When reusing a context, re-initialize most of its fields. This fixes at least two errors:
- we were canceling tasks that hadn't been canceled, because the canceled flag was left set to true and the next task assigned to the context then became canceled as soon as it yielded - we were resumeing blocked tasks before they should have resumed, because their blocking_promises list wasn't cleared and they were unblocking because the erroneous promises were fulfilled As a debugging aid, we also record the cancellation reason whenever a task is canceled, and include that in the canceled_exception (this is only enabled in debug builds)
This commit is contained in:
parent
f8472af119
commit
3222dc7c0b
6 changed files with 46 additions and 11 deletions
|
|
@ -13,7 +13,7 @@
|
||||||
# define FC_TASK_NAME_DEFAULT_ARG = "?"
|
# define FC_TASK_NAME_DEFAULT_ARG = "?"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define FC_CANCELATION_REASONS_ARE_MANDATORY 1
|
//#define FC_CANCELATION_REASONS_ARE_MANDATORY 1
|
||||||
#ifdef FC_CANCELATION_REASONS_ARE_MANDATORY
|
#ifdef FC_CANCELATION_REASONS_ARE_MANDATORY
|
||||||
# define FC_CANCELATION_REASON_DEFAULT_ARG
|
# define FC_CANCELATION_REASON_DEFAULT_ARG
|
||||||
#else
|
#else
|
||||||
|
|
@ -98,7 +98,11 @@ namespace fc {
|
||||||
time_point _timeout;
|
time_point _timeout;
|
||||||
fc::exception_ptr _exceptp;
|
fc::exception_ptr _exceptp;
|
||||||
bool _canceled;
|
bool _canceled;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
protected:
|
||||||
const char* _cancellation_reason;
|
const char* _cancellation_reason;
|
||||||
|
private:
|
||||||
|
#endif
|
||||||
const char* _desc;
|
const char* _desc;
|
||||||
detail::completion_handler* _compl;
|
detail::completion_handler* _compl;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -111,6 +111,21 @@ namespace fc {
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void reinitialize()
|
||||||
|
{
|
||||||
|
canceled = false;
|
||||||
|
#ifndef NDEBUG
|
||||||
|
cancellation_reason = nullptr;
|
||||||
|
#endif
|
||||||
|
blocking_prom.clear();
|
||||||
|
caller_context = nullptr;
|
||||||
|
resume_time = fc::time_point();
|
||||||
|
next_blocked = nullptr;
|
||||||
|
next_blocked_mutex = nullptr;
|
||||||
|
next = nullptr;
|
||||||
|
complete = false;
|
||||||
|
}
|
||||||
|
|
||||||
struct blocked_promise {
|
struct blocked_promise {
|
||||||
blocked_promise( promise_base* p=0, bool r=true )
|
blocked_promise( promise_base* p=0, bool r=true )
|
||||||
:prom(p),required(r){}
|
:prom(p),required(r){}
|
||||||
|
|
@ -168,7 +183,7 @@ namespace fc {
|
||||||
i->prom->set_exception( std::make_shared<timeout_exception>() );
|
i->prom->set_exception( std::make_shared<timeout_exception>() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void except_blocking_promises( const exception_ptr& e ) {
|
void set_exception_on_blocking_promises( const exception_ptr& e ) {
|
||||||
for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) {
|
for( auto i = blocking_prom.begin(); i != blocking_prom.end(); ++i ) {
|
||||||
i->prom->set_exception( e );
|
i->prom->set_exception( e );
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,11 @@ namespace fc {
|
||||||
|
|
||||||
void promise_base::cancel(const char* reason /* = nullptr */){
|
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
|
#ifndef NDEBUG
|
||||||
_cancellation_reason = reason;
|
_cancellation_reason = reason;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
bool promise_base::ready()const {
|
bool promise_base::ready()const {
|
||||||
return _ready;
|
return _ready;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,13 @@ namespace fc {
|
||||||
if( !canceled() )
|
if( !canceled() )
|
||||||
_run_functor( _functor, _promise_impl );
|
_run_functor( _functor, _promise_impl );
|
||||||
else
|
else
|
||||||
FC_THROW_EXCEPTION( canceled_exception, "${description}", ("description", get_desc() ) );
|
#ifdef NDEBUG
|
||||||
|
FC_THROW_EXCEPTION( canceled_exception, "task ${description} canceled before starting", ("description", get_desc()));
|
||||||
|
#else
|
||||||
|
FC_THROW_EXCEPTION( canceled_exception, "task ${description} canceled before starting, reason ${reason}",
|
||||||
|
("description", get_desc())
|
||||||
|
("reason", _cancellation_reason ? _cancellation_reason : "[none given]"));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
catch ( const exception& e )
|
catch ( const exception& e )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -157,8 +157,8 @@ namespace fc {
|
||||||
fc::context* n = cur->next;
|
fc::context* n = cur->next;
|
||||||
// this will move the context into the ready list.
|
// this will move the context into the ready list.
|
||||||
//cur->prom->set_exception( boost::copy_exception( error::thread_quit() ) );
|
//cur->prom->set_exception( boost::copy_exception( error::thread_quit() ) );
|
||||||
//cur->except_blocking_promises( thread_quit() );
|
//cur->set_exception_on_blocking_promises( thread_quit() );
|
||||||
cur->except_blocking_promises( std::make_shared<canceled_exception>() );
|
cur->set_exception_on_blocking_promises( std::make_shared<canceled_exception>(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting")) );
|
||||||
|
|
||||||
cur = n;
|
cur = n;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -284,10 +284,14 @@ namespace fc {
|
||||||
*/
|
*/
|
||||||
void check_fiber_exceptions() {
|
void check_fiber_exceptions() {
|
||||||
if( current && current->canceled ) {
|
if( current && current->canceled ) {
|
||||||
|
#ifdef NDEBUG
|
||||||
FC_THROW_EXCEPTION( canceled_exception, "" );
|
FC_THROW_EXCEPTION( canceled_exception, "" );
|
||||||
|
#else
|
||||||
|
FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: ${reason}", ("reason", current->cancellation_reason ? current->cancellation_reason : "[none given]"));
|
||||||
|
#endif
|
||||||
} else if( done ) {
|
} else if( done ) {
|
||||||
ilog( "throwing canceled exception" );
|
ilog( "throwing canceled exception" );
|
||||||
FC_THROW_EXCEPTION( canceled_exception, "" );
|
FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: thread quitting" );
|
||||||
// BOOST_THROW_EXCEPTION( thread_quit() );
|
// BOOST_THROW_EXCEPTION( thread_quit() );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -335,6 +339,7 @@ namespace fc {
|
||||||
next = pt_head;
|
next = pt_head;
|
||||||
pt_head = pt_head->next;
|
pt_head = pt_head->next;
|
||||||
next->next = 0;
|
next->next = 0;
|
||||||
|
next->reinitialize();
|
||||||
} else { // create new context.
|
} else { // create new context.
|
||||||
next = new fc::context( &thread_d::start_process_tasks, stack_alloc,
|
next = new fc::context( &thread_d::start_process_tasks, stack_alloc,
|
||||||
&fc::thread::current() );
|
&fc::thread::current() );
|
||||||
|
|
@ -356,8 +361,12 @@ namespace fc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if( current->canceled ) {
|
if( current->canceled ) {
|
||||||
current->canceled = false;
|
//current->canceled = false;
|
||||||
FC_THROW_EXCEPTION( canceled_exception, "" );
|
#ifdef NDEBUG
|
||||||
|
FC_THROW_EXCEPTION( canceled_exception, "" );
|
||||||
|
#else
|
||||||
|
FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: ${reason}", ("reason", current->cancellation_reason ? current->cancellation_reason : "[none given]"));
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -390,6 +399,7 @@ namespace fc {
|
||||||
current->cur_task = 0;
|
current->cur_task = 0;
|
||||||
next->_set_active_context(0);
|
next->_set_active_context(0);
|
||||||
next->release();
|
next->release();
|
||||||
|
current->reinitialize();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue