提交 afbf6243 编写于 作者: Y youngwolf

1.1.2 release.

Fix bug: ascs::list cannot be moved properly via moving constructor.
Use ASCS_DELAY_CLOSE instead of ASCS_ENHANCED_STABILITY macro to control delay close duration,
 0 is an equivalent of defining ASCS_ENHANCED_STABILITY, other values keep the same meanings as before.
Move ascs::socket::closing related logic to ascs::object.
Make ascs::socket::id(uint_fast64_t) private to avoid changing IDs by users.
Call close at the end of shutdown function, just for safety.
Add move capture in lambda.
Optimize lambda expressions.
上级 2aabeebb
......@@ -2,7 +2,10 @@
#include <iostream>
//configuration
#define ASCS_SERVER_PORT 9527
#define ASCS_SERVER_PORT 9527
#define ASCS_DELAY_CLOSE 1 //this demo not used object pool and doesn't need life cycle management,
//so, define this to avoid hooks for async call (and slightly improve efficiency),
//any value which is bigger than zero is okay.
#define ASCS_FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer
#define ASCS_CUSTOM_LOG
#define ASCS_DEFAULT_UNPACKER non_copy_unpacker
......
......@@ -2,14 +2,15 @@
#include <iostream>
//configuration
#define ASCS_SERVER_PORT 9527
#define ASCS_SERVER_PORT 9527
//#define ASCS_REUSE_OBJECT //use objects pool
#define ASCS_DELAY_CLOSE 5 //define this to avoid hooks for async call (and slightly improve efficiency)
//#define ASCS_FORCE_TO_USE_MSG_RECV_BUFFER //force to use the msg recv buffer
//#define ASCS_CLEAR_OBJECT_INTERVAL 1
//#define ASCS_CLEAR_OBJECT_INTERVAL 1
//#define ASCS_WANT_MSG_SEND_NOTIFY
#define ASCS_FULL_STATISTIC //full statistic will slightly impact efficiency
#ifdef ASCS_WANT_MSG_SEND_NOTIFY
#define ASCS_INPUT_QUEUE non_lock_queue //we will never operate sending buffer concurrently, so need no locks.
#define ASCS_INPUT_QUEUE non_lock_queue //we will never operate sending buffer concurrently, so need no locks
#define ASCS_INPUT_CONTAINER list
#endif
//configuration
......
......@@ -2,7 +2,9 @@
#include <iostream>
//configuration
#define ASCS_SERVER_PORT 5050
#define ASCS_SERVER_PORT 5050
#define ASCS_DELAY_CLOSE 5 //define this to avoid hooks for async call (and slightly improve efficiency)
//#define ASCS_INPUT_QUEUE non_lock_queue
//we cannot use non_lock_queue, because we also send messages (talking messages) out of ascs::socket::on_msg_send().
#define ASCS_DEFAULT_UNPACKER replaceable_unpacker<>
//configuration
......
......@@ -4,7 +4,7 @@
//configuration
#define ASCS_SERVER_PORT 5050
#define ASCS_ASYNC_ACCEPT_NUM 5
#define ASCS_CLEAR_OBJECT_INTERVAL 60
#define ASCS_CLEAR_OBJECT_INTERVAL 60
#define ASCS_ENHANCED_STABILITY
#define ASCS_WANT_MSG_SEND_NOTIFY
#define ASCS_INPUT_QUEUE non_lock_queue
......
......@@ -2,7 +2,7 @@
//configuration
#define ASCS_SERVER_PORT 5050
#define ASCS_ASYNC_ACCEPT_NUM 5
#define ASCS_CLEAR_OBJECT_INTERVAL 60
#define ASCS_CLEAR_OBJECT_INTERVAL 60
#define ASCS_ENHANCED_STABILITY
#define ASCS_WANT_MSG_SEND_NOTIFY
#define ASCS_INPUT_QUEUE non_lock_queue
......
......@@ -2,12 +2,13 @@
#include <iostream>
//configuration
#define ASCS_SERVER_PORT 9527
#define ASCS_SERVER_PORT 9527
#define ASCS_REUSE_OBJECT //use objects pool
#define ASCS_DELAY_CLOSE 5 //define this to avoid hooks for async call (and slightly improve efficiency)
//#define ASCS_FORCE_TO_USE_MSG_RECV_BUFFER
//#define ASCS_WANT_MSG_SEND_NOTIFY
#define ASCS_MSG_BUFFER_SIZE 65536
#define ASCS_INPUT_QUEUE non_lock_queue //we will never operate sending buffer concurrently, so need no locks.
#define ASCS_INPUT_QUEUE non_lock_queue //we will never operate sending buffer concurrently, so need no locks
#define ASCS_INPUT_CONTAINER list
#define ASCS_DEFAULT_UNPACKER stream_unpacker //non-protocol
//configuration
......
......@@ -5,8 +5,9 @@
#define ASCS_SERVER_PORT 9527
#define ASCS_ASYNC_ACCEPT_NUM 5
#define ASCS_REUSE_OBJECT //use objects pool
#define ASCS_DELAY_CLOSE 5 //define this to avoid hooks for async call (and slightly improve efficiency)
//#define ASCS_FORCE_TO_USE_MSG_RECV_BUFFER
#define ASCS_MSG_BUFFER_SIZE 65536
#define ASCS_MSG_BUFFER_SIZE 65536
#define ASCS_INPUT_QUEUE non_lock_queue
#define ASCS_INPUT_CONTAINER list
//if pingpong_client only send message in on_msg() or on_msg_handle(), which means a responsive system, a real pingpong test,
......
......@@ -2,6 +2,9 @@
#include <iostream>
//configuration
#define ASCS_DELAY_CLOSE 1 //this demo not used object pool and doesn't need life cycle management,
//so, define this to avoid hooks for async call (and slightly improve efficiency),
//any value which is bigger than zero is okay.
//#define ASCS_DEFAULT_PACKER replaceable_packer<>
//#define ASCS_DEFAULT_UDP_UNPACKER replaceable_udp_unpacker<>
//configuration
......
......@@ -35,18 +35,28 @@
* Add a new packer--fixed_length_packer.
* Add a new class--message_queue.
*
* 2016.10.16 version 1.3.1
* 2016.10.16 version 1.1.1
* Support non-lock queue, it's totally not thread safe and lock-free, it can improve IO throughput with particular business.
* Demonstrate how and when to use non-lock queue as the input and output message buffer.
* Queues (and their internal containers) used as input and output message buffer are now configurable (by macros or template arguments).
* New macros--ASCS_INPUT_QUEUE, ASCS_INPUT_CONTAINER, ASCS_OUTPUT_QUEUE and ASCS_OUTPUT_CONTAINER.
* Drop macro ASCS_USE_CONCURRENT_QUEUE, rename macro ASCS_USE_CONCURRE to ASCS_HAS_CONCURRENT_QUEUE.
* In contrast to non_lock_queue, split message_queue into lock_queue and lock_free_queue.
* Move container related classes and functions from st_asio_wrapper_base.h to st_asio_wrapper_container.h.
* Move container related classes and functions from base.h to container.h.
* Improve efficiency in scenarios of low throughput like pingpong test.
* Replaceable packer/unpacker now support replaceable_buffer (an alias of auto_buffer) and shared_buffer to be their message type.
* Move class statistic and obj_with_begin_time out of ascs::socket to reduce template tiers.
*
* 2016.1.1 version 1.1.2
* Fix bug: ascs::list cannot be moved properly via moving constructor.
* Use ASCS_DELAY_CLOSE instead of ASCS_ENHANCED_STABILITY macro to control delay close duration,
* 0 is an equivalent of defining ASCS_ENHANCED_STABILITY, other values keep the same meanings as before.
* Move ascs::socket::closing related logic to ascs::object.
* Make ascs::socket::id(uint_fast64_t) private to avoid changing IDs by users.
* Call close at the end of shutdown function, just for safety.
* Add move capture in lambda.
* Optimize lambda expressions.
*
*/
#ifndef _ASCS_CONFIG_H_
......@@ -56,8 +66,8 @@
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#define ASCS_VER 10101 //[x]xyyzz -> [x]x.[y]y.[z]z
#define ASCS_VERSION "1.1.1"
#define ASCS_VER 10102 //[x]xyyzz -> [x]x.[y]y.[z]z
#define ASCS_VERSION "1.1.2"
//asio and compiler check
#ifdef _MSC_VER
......@@ -124,19 +134,13 @@ static_assert(ASCS_MAX_MSG_NUM > 0, "message capacity must be bigger than zero."
//after this duration, this socket can be freed from the heap or reused,
//you must define this macro as a value, not just define it, the value means the duration, unit is second.
//if macro ASCS_ENHANCED_STABILITY been defined, this macro will always be zero.
#ifdef ASCS_ENHANCED_STABILITY
#if defined(ASCS_DELAY_CLOSE) && ASCS_DELAY_CLOSE != 0
#warning ASCS_DELAY_CLOSE will always be zero if ASCS_ENHANCED_STABILITY macro been defined.
#endif
#undef ASCS_DELAY_CLOSE
#define ASCS_DELAY_CLOSE 0
#else
//a value equal to zero will cause ascs to use a mechanism to guarantee 100% safety when reusing or freeing this socket,
//ascs will hook all async calls to avoid this socket to be reused or freed before all async calls finish
//or been interrupted (of course, this mechanism will slightly impact efficiency).
#ifndef ASCS_DELAY_CLOSE
#define ASCS_DELAY_CLOSE 5 //seconds
#endif
static_assert(ASCS_DELAY_CLOSE > 0, "ASCS_DELAY_CLOSE must be bigger than zero.");
#define ASCS_DELAY_CLOSE 0 //seconds, guarantee 100% safety when reusing or freeing this socket
#endif
static_assert(ASCS_DELAY_CLOSE >= 0, "delay close duration must be bigger than or equal to zero.");
//full statistic include time consumption, or only numerable informations will be gathered
//#define ASCS_FULL_STATISTIC
......
......@@ -45,6 +45,7 @@ public:
typedef typename _Mybase::const_reverse_iterator const_reverse_iterator;
list() : s(0) {}
list(list&& other) : s(0) {swap(other);}
void swap(list& other) {impl.swap(other.impl); std::swap(s, other.s);}
bool empty() const {return 0 == s;}
......@@ -133,7 +134,7 @@ private:
};
//Container must at least has the following functions:
// Container(size) and Container() constructor
// Container() and Container(size_t) constructor
// move constructor
// swap
// size_approx
......@@ -156,7 +157,6 @@ public:
//not thread-safe
void clear() {super(std::move(*this));}
void swap(me& other) {super::swap(other);}
bool enqueue_(const T& item) {return this->enqueue(item);}
bool enqueue_(T&& item) {return this->enqueue(std::move(item));}
......@@ -164,7 +164,7 @@ public:
};
//Container must at least has the following functions:
// Container() constructor
// Container() and Container(size_t) constructor
// size
// empty
// clear
......@@ -184,10 +184,6 @@ public:
queue() {}
queue(size_t size) : super(size) {}
//not thread-safe
void clear() {super::clear();}
void swap(me& other) {super::swap(other);}
bool enqueue(const T& item) {typename Lockable::lock_guard lock(*this); return enqueue_(item);}
bool enqueue(T&& item) {typename Lockable::lock_guard lock(*this); return enqueue_(std::move(item));}
bool try_dequeue(T& item) {typename Lockable::lock_guard lock(*this); return try_dequeue_(item);}
......@@ -200,7 +196,7 @@ public:
template<typename T, typename Container> using non_lock_queue = queue<T, Container, dummy_lockable>; //totally not thread safe
template<typename T, typename Container> using lock_queue = queue<T, Container, lockable>;
//it's not thread safe for 'other', please note. for this queue, depends on 'Q'
//it's not thread safe for 'other', please note. for 'dest', depends on 'Q'
template<typename Q>
size_t move_items_in(Q& dest, Q& other, size_t max_size = ASCS_MAX_MSG_NUM)
{
......@@ -225,7 +221,7 @@ size_t move_items_in(Q& dest, Q& other, size_t max_size = ASCS_MAX_MSG_NUM)
return num;
}
//it's not thread safe for 'other', please note. for this queue, depends on 'Q'
//it's not thread safe for 'other', please note. for 'dest', depends on 'Q'
template<typename Q, typename Q2>
size_t move_items_in(Q& dest, Q2& other, size_t max_size = ASCS_MAX_MSG_NUM)
{
......@@ -273,7 +269,7 @@ bool splice_helper(_Can& dest_can, _Can& src_can, size_t max_size = ASCS_MAX_MSG
return true;
}
} //namespace
#endif /* _ASCS_CONTAINER_H_ */
\ No newline at end of file
......@@ -27,27 +27,23 @@ protected:
public:
bool stopped() const {return io_service_.stopped();}
#ifdef ASCS_ENHANCED_STABILITY
template<typename CallbackHandler>
void post(const CallbackHandler& handler) {auto unused(async_call_indicator); io_service_.post([=]() {handler();});}
template<typename CallbackHandler>
void post(CallbackHandler&& handler) {auto unused(async_call_indicator); io_service_.post([=]() {handler();});}
bool is_async_calling() const {return !async_call_indicator.unique();}
bool is_last_async_call() const {return async_call_indicator.use_count() <= 2;} //can only be called in callbacks
#if 0 == ASCS_DELAY_CLOSE
template<typename F> void post(F&& handler) {io_service_.post([unused(this->async_call_indicator), handler(std::move(handler))]() {handler();});}
template<typename F> void post(const F& handler) {io_service_.post([unused(this->async_call_indicator), handler]() {handler();});}
typedef std::function<void(const asio::error_code&)> handler_with_error;
template<typename F> handler_with_error make_handler_error(F&& handler) const {return [unused(this->async_call_indicator), handler(std::move(handler))](const auto& ec) {handler(ec);};}
template<typename F> handler_with_error make_handler_error(const F& handler) const {return [unused(this->async_call_indicator), handler](const auto& ec) {handler(ec);};}
template<typename CallbackHandler>
std::function<void(const asio::error_code&)> make_handler_error(CallbackHandler&& handler) const
{auto unused(async_call_indicator); return [=](const auto& ec) {handler(ec);};}
template<typename CallbackHandler>
std::function<void(const asio::error_code&)> make_handler_error(const CallbackHandler& handler) const
{auto unused(async_call_indicator); return [=](const auto& ec) {handler(ec);};}
typedef std::function<void(const asio::error_code&, size_t)> handler_with_error_size;
template<typename F> handler_with_error_size make_handler_error_size(F&& handler) const
{return [unused(this->async_call_indicator), handler(std::move(handler))](const auto& ec, auto bytes_transferred) {handler(ec, bytes_transferred);};}
template<typename F> handler_with_error_size make_handler_error_size(const F& handler) const
{return [unused(this->async_call_indicator), handler](const auto& ec, auto bytes_transferred) {handler(ec, bytes_transferred);};}
template<typename CallbackHandler>
std::function<void(const asio::error_code&, size_t)> make_handler_error_size(CallbackHandler&& handler) const
{auto unused(async_call_indicator); return [=](const auto& ec, auto bytes_transferred) {handler(ec, bytes_transferred);};}
template<typename CallbackHandler>
std::function<void(const asio::error_code&, size_t)> make_handler_error_size(CallbackHandler& handler) const
{auto unused(async_call_indicator); return [=](const auto& ec, auto bytes_transferred) {handler(ec, bytes_transferred);};}
bool is_async_calling() const {return !async_call_indicator.unique();}
bool is_last_async_call() const {return async_call_indicator.use_count() <= 2;} //can only be called in callbacks
inline void set_async_calling(bool) {}
protected:
void reset() {async_call_indicator = std::make_shared<char>('\0');}
......@@ -55,28 +51,26 @@ protected:
protected:
std::shared_ptr<char> async_call_indicator;
#else
template<typename CallbackHandler>
void post(const CallbackHandler& handler) {io_service_.post(handler);}
template<typename CallbackHandler>
void post(CallbackHandler&& handler) {io_service_.post(std::move(handler));}
bool is_async_calling() const {return false;}
bool is_last_async_call() const {return true;}
template<typename F>
inline F&& make_handler_error(F&& f) const {return std::move(f);}
template<typename F>
inline const F& make_handler_error(const F& f) const {return f;}
template<typename F>
inline F&& make_handler_error_size(F&& f) const {return std::move(f);}
template<typename F>
inline const F& make_handler_error_size(const F& f) const {return f;}
template<typename F> void post(F&& handler) {io_service_.post(std::move(handler));}
template<typename F> void post(const F& handler) {io_service_.post(handler);}
template<typename F> inline F&& make_handler_error(F&& f) const {return std::move(f);}
template<typename F> inline const F& make_handler_error(const F& f) const {return f;}
template<typename F> inline F&& make_handler_error_size(F&& f) const {return std::move(f);}
template<typename F> inline const F& make_handler_error_size(const F& f) const {return f;}
inline bool is_async_calling() const {return async_calling;}
inline bool is_last_async_call() const {return true;}
inline void set_async_calling(bool value) {async_calling = value;}
protected:
void reset() {}
#endif
void reset() {set_async_calling(false);}
protected:
bool async_calling;
#endif
asio::io_service& io_service_;
};
......
......@@ -151,7 +151,7 @@ protected:
object_type create_object() {return create_object(sp);}
public:
//to configure unordered_set(for example, set factor or reserved size), not locked the mutex, so must be called before service_pump starting up.
//to configure unordered_set(for example, set factor or reserved size), not thread safe, so must be called before service_pump startup.
container_type& container() {return object_can;}
size_t max_size() const {return max_size_;}
......@@ -227,7 +227,7 @@ public:
std::unique_lock<std::shared_mutex> lock(object_can_mutex);
for (auto iter = std::begin(object_can); iter != std::end(object_can);)
if (iter->second.unique() && iter->second->obsoleted())
if (iter->second->obsoleted())
{
objects.push_back(std::move(iter->second));
iter = object_can.erase(iter);
......@@ -259,7 +259,7 @@ public:
std::unique_lock<std::shared_mutex> lock(invalid_object_can_mutex);
for (auto iter = std::begin(invalid_object_can); num > 0 && iter != std::end(invalid_object_can);)
if ((*iter).unique() && (*iter)->obsoleted())
if ((*iter)->obsoleted())
{
--num;
++num_affected;
......
......@@ -55,9 +55,6 @@ protected:
sending = paused_sending = false;
dispatching = paused_dispatching = congestion_controlling = false;
#ifndef ASCS_ENHANCED_STABILITY
closing = false;
#endif
// started_ = false;
}
......@@ -71,9 +68,6 @@ protected:
}
public:
//please do not change id at runtime via the following function, except this socket is not managed by object_pool,
//it should only be used by object_pool when reusing or creating new socket.
void id(uint_fast64_t id) {assert(!started_); if (started_) unified_out::error_out("id is unchangeable!"); else _id = id;}
uint_fast64_t id() const {return _id;}
bool is_equal_to(uint_fast64_t id) const {return _id == id;}
......@@ -82,14 +76,7 @@ public:
typename Socket::lowest_layer_type& lowest_layer() {return next_layer().lowest_layer();}
const typename Socket::lowest_layer_type& lowest_layer() const {return next_layer().lowest_layer();}
virtual bool obsoleted()
{
#ifndef ASCS_ENHANCED_STABILITY
return started() || closing || this->is_async_calling() ? false : recv_msg_buffer.empty() && recv_msg_buffer.idle();
#else
return !started() && !this->is_async_calling();
#endif
}
virtual bool obsoleted() {return !dispatching && !started() && !is_async_calling();}
bool started() const {return started_;}
void start()
......@@ -121,7 +108,8 @@ public:
void suspend_send_msg(bool suspend) {if (!(paused_sending = suspend)) send_msg();}
bool suspend_send_msg() const {return paused_sending;}
void suspend_dispatch_msg(bool suspend) {if (!(paused_dispatching = suspend)) dispatch_msg();}
//for a socket that has been shut down, resuming message dispatching will not take effect for left messages.
void suspend_dispatch_msg(bool suspend) {if (!(paused_dispatching = suspend) && started()) dispatch_msg();}
bool suspend_dispatch_msg() const {return paused_dispatching;}
void congestion_control(bool enable) {congestion_controlling = enable; unified_out::warning_out("%s congestion control.", enable ? "open" : "close");}
......@@ -167,10 +155,10 @@ protected:
virtual void on_send_error(const asio::error_code& ec) {unified_out::error_out("send msg error (%d %s)", ec.value(), ec.message().data());}
//receiving error or peer endpoint quit(false ec means ok)
virtual void on_recv_error(const asio::error_code& ec) = 0;
//if ASCS_ENHANCED_STABILITY macro been defined, in this callback, socket guarantee that there's no any async call associated it,
//include user timers(created by set_timer()) and user async calls(started via post()),
//this means you can clean up any resource in this socket except this socket itself, because this socket maybe is being maintained by object_pool.
//if ASCS_ENHANCED_STABILITY macro not defined, socket simply call this callback ASCS_DELAY_CLOSE seconds later after link down, no any guarantees.
//if ASCS_DELAY_CLOSE is equal to zero, in this callback, socket guarantee that there's no any other async call associated it,
// include user timers(created by set_timer()) and user async calls(started via post()), this means you can clean up any resource
// in this socket except this socket itself, because this socket maybe is being maintained by object_pool.
//otherwise (bigger than zero), socket simply call this callback ASCS_DELAY_CLOSE seconds later after link down, no any guarantees.
virtual void on_close() {unified_out::info_out("on_close()");}
#ifndef ASCS_FORCE_TO_USE_MSG_RECV_BUFFER
......@@ -209,9 +197,7 @@ protected:
{
if (is_closable())
{
#ifndef ASCS_ENHANCED_STABILITY
closing = true;
#endif
set_async_calling(true);
set_timer(TIMER_DELAY_CLOSE, ASCS_DELAY_CLOSE * 1000 + 50, [this](auto id)->bool {return this->timer_handler(id);});
}
}
......@@ -309,6 +295,11 @@ protected:
}
private:
//please do not change id at runtime via the following function, except this socket is not managed by object_pool,
//it should only be used by object_pool when reusing or creating new socket.
template<typename Object> friend class object_pool;
void id(uint_fast64_t id) {assert(!started_); if (started_) unified_out::error_out("id is unchangeable!"); else _id = id;}
bool timer_handler(tid id)
{
switch (id)
......@@ -329,9 +320,8 @@ private:
lowest_layer().close(ec);
}
on_close();
#ifndef ASCS_ENHANCED_STABILITY
closing = false;
#endif
set_async_calling(false);
break;
default:
assert(false);
......@@ -377,7 +367,7 @@ protected:
in_container_type send_msg_buffer;
out_container_type recv_msg_buffer;
list<out_msg> temp_msg_buffer;
//ascs::tcp::socket will invoke handle_msg() when got some msgs. if these msgs can't be pushed into recv_msg_buffer because of:
//subclass will invoke handle_msg() when got some msgs. if these msgs can't be pushed into recv_msg_buffer because of:
// 1. msg dispatching suspended;
// 2. congestion control opened;
//ascs::socket will delay 50 milliseconds(non-blocking) to invoke handle_msg() again, and now, as you known, temp_msg_buffer is used to hold these msgs temporarily.
......@@ -386,9 +376,6 @@ protected:
std::shared_mutex send_mutex;
bool dispatching, paused_dispatching, congestion_controlling;
std::shared_mutex dispatch_mutex;
#ifndef ASCS_ENHANCED_STABILITY
bool closing;
#endif
bool started_; //has started or not
std::shared_mutex start_mutex;
......
......@@ -206,17 +206,20 @@ private:
if (!ec)
{
if (this->on_accept(client_ptr))
client_ptr->next_layer().async_handshake(asio::ssl::stream_base::server, [client_ptr, this](const auto& ec) {
this->on_handshake(ec, client_ptr);
if (!ec && this->add_client(client_ptr))
client_ptr->start();
});
client_ptr->next_layer().async_handshake(asio::ssl::stream_base::server, [client_ptr, this](const auto& ec) {this->handshake_handler(ec, client_ptr);});
start_next_accept();
}
else
this->stop_listen();
}
void handshake_handler(const asio::error_code& ec, typename server_base::object_ctype& client_ptr)
{
this->on_handshake(ec, client_ptr);
if (!ec && this->add_client(client_ptr))
client_ptr->start();
}
};
}} //namespace
......
......@@ -176,7 +176,6 @@ protected:
shutdown_state = shutdown_states::FORCE;
this->stop_all_timer();
this->close(); //must after stop_all_timer(), it's very important
this->started_ = false;
// reset_state();
......@@ -185,6 +184,8 @@ protected:
asio::error_code ec;
this->lowest_layer().shutdown(asio::ip::tcp::socket::shutdown_both, ec);
}
this->close(); //call this at the end of 'shutdown', it's very important
}
private:
......
......@@ -166,7 +166,6 @@ protected:
std::unique_lock<std::shared_mutex> lock(shutdown_mutex);
this->stop_all_timer();
this->close(); //must after stop_all_timer(), it's very important
this->started_ = false;
// reset_state();
......@@ -176,6 +175,8 @@ protected:
this->lowest_layer().shutdown(asio::ip::udp::socket::shutdown_both, ec);
this->lowest_layer().close(ec);
}
this->close(); //call this at the end of 'shutdown', it's very important
}
private:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册