diff --git a/include/fc/thread/future.hpp b/include/fc/thread/future.hpp index 3e544ee..7233274 100644 --- a/include/fc/thread/future.hpp +++ b/include/fc/thread/future.hpp @@ -13,6 +13,13 @@ # define FC_TASK_NAME_DEFAULT_ARG = "?" #endif +#define FC_CANCELATION_REASONS_ARE_MANDATORY 1 +#ifdef FC_CANCELATION_REASONS_ARE_MANDATORY +# define FC_CANCELATION_REASON_DEFAULT_ARG +#else +# define FC_CANCELATION_REASON_DEFAULT_ARG = nullptr +#endif + namespace fc { class abstract_thread; struct void_t{}; @@ -58,7 +65,7 @@ namespace fc { const char* get_desc()const; - virtual void cancel(); + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG); bool canceled()const { return _canceled; } bool ready()const; bool error()const; @@ -91,6 +98,7 @@ namespace fc { time_point _timeout; fc::exception_ptr _exceptp; bool _canceled; + const char* _cancellation_reason; const char* _desc; detail::completion_handler* _compl; }; @@ -210,14 +218,14 @@ namespace fc { /// @pre valid() bool error()const { return m_prom->error(); } - void cancel()const { if( m_prom ) m_prom->cancel(); } + void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) const { if( m_prom ) m_prom->cancel(reason); } bool canceled()const { if( m_prom ) return m_prom->canceled(); else return true;} - void cancel_and_wait() + void cancel_and_wait(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) { if( valid() ) { - cancel(); + cancel(reason); try { wait(); @@ -276,9 +284,9 @@ namespace fc { bool valid()const { return !!m_prom; } bool canceled()const { return m_prom ? m_prom->canceled() : true; } - void cancel_and_wait() + void cancel_and_wait(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) { - cancel(); + cancel(reason); try { wait(); @@ -294,7 +302,7 @@ namespace fc { /// @pre valid() bool error()const { return m_prom->error(); } - void cancel()const { if( m_prom ) m_prom->cancel(); } + void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) const { if( m_prom ) m_prom->cancel(reason); } template void on_complete( CompletionHandler&& c ) { diff --git a/include/fc/thread/task.hpp b/include/fc/thread/task.hpp index 7517cd1..dd52b77 100644 --- a/include/fc/thread/task.hpp +++ b/include/fc/thread/task.hpp @@ -30,7 +30,7 @@ namespace fc { class task_base : virtual public promise_base { public: void run(); - virtual void cancel() override; + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override; protected: ~task_base(); @@ -99,7 +99,7 @@ namespace fc { _promise_impl = static_cast*>(this); _run_functor = &detail::functor_run::run; } - virtual void cancel() override { task_base::cancel(); } + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override { task_base::cancel(reason); } aligned _functor; private: @@ -119,7 +119,7 @@ namespace fc { _promise_impl = static_cast*>(this); _run_functor = &detail::void_functor_run::run; } - virtual void cancel() override { task_base::cancel(); } + virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override { task_base::cancel(reason); } aligned _functor; private: diff --git a/src/log/file_appender.cpp b/src/log/file_appender.cpp index 92c2848..e44d060 100644 --- a/src/log/file_appender.cpp +++ b/src/log/file_appender.cpp @@ -82,7 +82,7 @@ namespace fc { { try { - _rotation_task.cancel_and_wait(); + _rotation_task.cancel_and_wait("file_appender is destructing"); } catch( ... ) { diff --git a/src/network/ntp.cpp b/src/network/ntp.cpp index 9febe47..31d8b87 100644 --- a/src/network/ntp.cpp +++ b/src/network/ntp.cpp @@ -139,7 +139,7 @@ namespace fc my->_ntp_thread.async([=](){ try { - my->_request_time_task_done.cancel_and_wait(); + my->_request_time_task_done.cancel_and_wait("ntp object is destructing"); } catch ( const fc::exception& e ) { @@ -152,7 +152,7 @@ namespace fc try { - my->_read_loop_done.cancel(); + my->_read_loop_done.cancel("ntp object is destructing"); my->_sock.close(); my->_read_loop_done.wait(); } diff --git a/src/network/udt_socket.cpp b/src/network/udt_socket.cpp index 0018238..ee4dbb5 100644 --- a/src/network/udt_socket.cpp +++ b/src/network/udt_socket.cpp @@ -36,7 +36,7 @@ namespace fc { ~udt_epoll_service() { - _epoll_loop.cancel(); + _epoll_loop.cancel("udt_epoll_service is destructing"); _epoll_loop.wait(); UDT::cleanup(); } diff --git a/src/rpc/json_connection.cpp b/src/rpc/json_connection.cpp index 06d3fa4..2d1014f 100644 --- a/src/rpc/json_connection.cpp +++ b/src/rpc/json_connection.cpp @@ -243,7 +243,7 @@ namespace fc { namespace rpc { { if( my->_done.valid() && !my->_done.ready() ) { - my->_done.cancel(); + my->_done.cancel("json_connection is destructing"); my->_out->close(); my->_done.wait(); } diff --git a/src/thread/context.hpp b/src/thread/context.hpp index 4ab0dd4..ecd360f 100644 --- a/src/thread/context.hpp +++ b/src/thread/context.hpp @@ -48,6 +48,9 @@ namespace fc { next(0), ctx_thread(t), canceled(false), +#ifndef NDEBUG + cancellation_reason(nullptr), +#endif complete(false), cur_task(0) { @@ -78,6 +81,9 @@ namespace fc { next(0), ctx_thread(t), canceled(false), +#ifndef NDEBUG + cancellation_reason(nullptr), +#endif complete(false), cur_task(0) {} @@ -192,6 +198,9 @@ namespace fc { fc::context* next; fc::thread* ctx_thread; bool canceled; +#ifndef NDEBUG + const char* cancellation_reason; +#endif bool complete; task_base* cur_task; }; diff --git a/src/thread/future.cpp b/src/thread/future.cpp index 9ccc47f..9ad6648 100644 --- a/src/thread/future.cpp +++ b/src/thread/future.cpp @@ -17,6 +17,9 @@ namespace fc { #endif _timeout(time_point::maximum()), _canceled(false), +#ifndef NDEBUG + _cancellation_reason(nullptr), +#endif _desc(desc), _compl(nullptr) { } @@ -25,9 +28,12 @@ namespace fc { return _desc; } - void promise_base::cancel(){ + void promise_base::cancel(const char* reason /* = nullptr */){ // wlog("${desc} canceled!", ("desc", _desc? _desc : "")); _canceled = true; +#ifndef NDEBUG + _cancellation_reason = reason; +#endif } bool promise_base::ready()const { return _ready; diff --git a/src/thread/task.cpp b/src/thread/task.cpp index f2759c9..46af23f 100644 --- a/src/thread/task.cpp +++ b/src/thread/task.cpp @@ -54,12 +54,15 @@ namespace fc { } } - void task_base::cancel() + void task_base::cancel(const char* reason /* = nullptr */) { - promise_base::cancel(); + promise_base::cancel(reason); if (_active_context) { _active_context->canceled = true; +#ifndef NDEBUG + _active_context->cancellation_reason = reason; +#endif } } diff --git a/tests/task_cancel.cpp b/tests/task_cancel.cpp index 0b9408e..ff1d51d 100644 --- a/tests/task_cancel.cpp +++ b/tests/task_cancel.cpp @@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE( leave_mutex_locked ) fc::usleep(fc::seconds(1)); }, "test_task"); fc::usleep(fc::seconds(3)); - test_task.cancel_and_wait(); + test_task.cancel_and_wait("cancel called directly by test"); } BOOST_TEST_PASSPOINT(); } @@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE( cancel_task_blocked_on_mutex) } fc::usleep(fc::seconds(3)); - test_task.cancel_and_wait(); + test_task.cancel_and_wait("cleaning up test"); } } @@ -115,7 +115,7 @@ BOOST_AUTO_TEST_CASE( cancel_an_active_task ) fc::usleep(fc::milliseconds(100)); BOOST_TEST_MESSAGE("Canceling task"); - task.cancel(); + task.cancel("canceling to test if cancel works"); try { task_result result = task.wait(); @@ -149,7 +149,7 @@ BOOST_AUTO_TEST_CASE( cleanup_cancelled_task ) BOOST_CHECK_MESSAGE(!weak_string_ptr.expired(), "Weak pointer should still be valid because async task should be holding the strong pointer"); fc::usleep(fc::milliseconds(100)); BOOST_TEST_MESSAGE("Canceling task"); - task.cancel(); + task.cancel("canceling to test if cancel works"); try { task.wait(); @@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE( cancel_scheduled_task ) simple_task(); simple_task(); fc::usleep(fc::seconds(4)); - simple_task_done.cancel(); + simple_task_done.cancel("canceling scheduled task to test if cancel works"); simple_task_done.wait(); } catch ( const fc::exception& e )