Allow the user to supply a reason string when canceling a task (useful for debugging)

This commit is contained in:
Eric Frias 2014-08-27 14:07:44 -04:00
parent 8841f5e271
commit ac385d1f6b
10 changed files with 49 additions and 23 deletions

View file

@ -13,6 +13,13 @@
# define FC_TASK_NAME_DEFAULT_ARG = "?" # define FC_TASK_NAME_DEFAULT_ARG = "?"
#endif #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 { namespace fc {
class abstract_thread; class abstract_thread;
struct void_t{}; struct void_t{};
@ -58,7 +65,7 @@ namespace fc {
const char* get_desc()const; 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 canceled()const { return _canceled; }
bool ready()const; bool ready()const;
bool error()const; bool error()const;
@ -91,6 +98,7 @@ namespace fc {
time_point _timeout; time_point _timeout;
fc::exception_ptr _exceptp; fc::exception_ptr _exceptp;
bool _canceled; bool _canceled;
const char* _cancellation_reason;
const char* _desc; const char* _desc;
detail::completion_handler* _compl; detail::completion_handler* _compl;
}; };
@ -210,14 +218,14 @@ namespace fc {
/// @pre valid() /// @pre valid()
bool error()const { return m_prom->error(); } 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;} 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() ) if( valid() )
{ {
cancel(); cancel(reason);
try try
{ {
wait(); wait();
@ -276,9 +284,9 @@ namespace fc {
bool valid()const { return !!m_prom; } bool valid()const { return !!m_prom; }
bool canceled()const { return m_prom ? m_prom->canceled() : true; } 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 try
{ {
wait(); wait();
@ -294,7 +302,7 @@ namespace fc {
/// @pre valid() /// @pre valid()
bool error()const { return m_prom->error(); } 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<typename CompletionHandler> template<typename CompletionHandler>
void on_complete( CompletionHandler&& c ) { void on_complete( CompletionHandler&& c ) {

View file

@ -30,7 +30,7 @@ namespace fc {
class task_base : virtual public promise_base { class task_base : virtual public promise_base {
public: public:
void run(); void run();
virtual void cancel() override; virtual void cancel(const char* reason FC_CANCELATION_REASON_DEFAULT_ARG) override;
protected: protected:
~task_base(); ~task_base();
@ -99,7 +99,7 @@ namespace fc {
_promise_impl = static_cast<promise<R>*>(this); _promise_impl = static_cast<promise<R>*>(this);
_run_functor = &detail::functor_run<FunctorType>::run; _run_functor = &detail::functor_run<FunctorType>::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<FunctorSize> _functor; aligned<FunctorSize> _functor;
private: private:
@ -119,7 +119,7 @@ namespace fc {
_promise_impl = static_cast<promise<void>*>(this); _promise_impl = static_cast<promise<void>*>(this);
_run_functor = &detail::void_functor_run<FunctorType>::run; _run_functor = &detail::void_functor_run<FunctorType>::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<FunctorSize> _functor; aligned<FunctorSize> _functor;
private: private:

View file

@ -82,7 +82,7 @@ namespace fc {
{ {
try try
{ {
_rotation_task.cancel_and_wait(); _rotation_task.cancel_and_wait("file_appender is destructing");
} }
catch( ... ) catch( ... )
{ {

View file

@ -139,7 +139,7 @@ namespace fc
my->_ntp_thread.async([=](){ my->_ntp_thread.async([=](){
try 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 ) catch ( const fc::exception& e )
{ {
@ -152,7 +152,7 @@ namespace fc
try try
{ {
my->_read_loop_done.cancel(); my->_read_loop_done.cancel("ntp object is destructing");
my->_sock.close(); my->_sock.close();
my->_read_loop_done.wait(); my->_read_loop_done.wait();
} }

View file

@ -36,7 +36,7 @@ namespace fc {
~udt_epoll_service() ~udt_epoll_service()
{ {
_epoll_loop.cancel(); _epoll_loop.cancel("udt_epoll_service is destructing");
_epoll_loop.wait(); _epoll_loop.wait();
UDT::cleanup(); UDT::cleanup();
} }

View file

@ -243,7 +243,7 @@ namespace fc { namespace rpc {
{ {
if( my->_done.valid() && !my->_done.ready() ) if( my->_done.valid() && !my->_done.ready() )
{ {
my->_done.cancel(); my->_done.cancel("json_connection is destructing");
my->_out->close(); my->_out->close();
my->_done.wait(); my->_done.wait();
} }

View file

@ -48,6 +48,9 @@ namespace fc {
next(0), next(0),
ctx_thread(t), ctx_thread(t),
canceled(false), canceled(false),
#ifndef NDEBUG
cancellation_reason(nullptr),
#endif
complete(false), complete(false),
cur_task(0) cur_task(0)
{ {
@ -78,6 +81,9 @@ namespace fc {
next(0), next(0),
ctx_thread(t), ctx_thread(t),
canceled(false), canceled(false),
#ifndef NDEBUG
cancellation_reason(nullptr),
#endif
complete(false), complete(false),
cur_task(0) cur_task(0)
{} {}
@ -192,6 +198,9 @@ namespace fc {
fc::context* next; fc::context* next;
fc::thread* ctx_thread; fc::thread* ctx_thread;
bool canceled; bool canceled;
#ifndef NDEBUG
const char* cancellation_reason;
#endif
bool complete; bool complete;
task_base* cur_task; task_base* cur_task;
}; };

View file

@ -17,6 +17,9 @@ namespace fc {
#endif #endif
_timeout(time_point::maximum()), _timeout(time_point::maximum()),
_canceled(false), _canceled(false),
#ifndef NDEBUG
_cancellation_reason(nullptr),
#endif
_desc(desc), _desc(desc),
_compl(nullptr) _compl(nullptr)
{ } { }
@ -25,9 +28,12 @@ namespace fc {
return _desc; return _desc;
} }
void promise_base::cancel(){ void promise_base::cancel(const char* reason /* = nullptr */){
// wlog("${desc} canceled!", ("desc", _desc? _desc : "")); // wlog("${desc} canceled!", ("desc", _desc? _desc : ""));
_canceled = true; _canceled = true;
#ifndef NDEBUG
_cancellation_reason = reason;
#endif
} }
bool promise_base::ready()const { bool promise_base::ready()const {
return _ready; return _ready;

View file

@ -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) if (_active_context)
{ {
_active_context->canceled = true; _active_context->canceled = true;
#ifndef NDEBUG
_active_context->cancellation_reason = reason;
#endif
} }
} }

View file

@ -18,7 +18,7 @@ BOOST_AUTO_TEST_CASE( leave_mutex_locked )
fc::usleep(fc::seconds(1)); fc::usleep(fc::seconds(1));
}, "test_task"); }, "test_task");
fc::usleep(fc::seconds(3)); fc::usleep(fc::seconds(3));
test_task.cancel_and_wait(); test_task.cancel_and_wait("cancel called directly by test");
} }
BOOST_TEST_PASSPOINT(); BOOST_TEST_PASSPOINT();
} }
@ -57,7 +57,7 @@ BOOST_AUTO_TEST_CASE( cancel_task_blocked_on_mutex)
} }
fc::usleep(fc::seconds(3)); 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)); fc::usleep(fc::milliseconds(100));
BOOST_TEST_MESSAGE("Canceling task"); BOOST_TEST_MESSAGE("Canceling task");
task.cancel(); task.cancel("canceling to test if cancel works");
try try
{ {
task_result result = task.wait(); 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"); 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)); fc::usleep(fc::milliseconds(100));
BOOST_TEST_MESSAGE("Canceling task"); BOOST_TEST_MESSAGE("Canceling task");
task.cancel(); task.cancel("canceling to test if cancel works");
try try
{ {
task.wait(); task.wait();
@ -181,7 +181,7 @@ BOOST_AUTO_TEST_CASE( cancel_scheduled_task )
simple_task(); simple_task();
simple_task(); simple_task();
fc::usleep(fc::seconds(4)); 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(); simple_task_done.wait();
} }
catch ( const fc::exception& e ) catch ( const fc::exception& e )