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:
Eric Frias 2014-08-28 15:42:01 -04:00
parent f8472af119
commit 3222dc7c0b
6 changed files with 46 additions and 11 deletions

View file

@ -13,7 +13,7 @@
# define FC_TASK_NAME_DEFAULT_ARG = "?"
#endif
#define FC_CANCELATION_REASONS_ARE_MANDATORY 1
//#define FC_CANCELATION_REASONS_ARE_MANDATORY 1
#ifdef FC_CANCELATION_REASONS_ARE_MANDATORY
# define FC_CANCELATION_REASON_DEFAULT_ARG
#else
@ -98,7 +98,11 @@ namespace fc {
time_point _timeout;
fc::exception_ptr _exceptp;
bool _canceled;
#ifndef NDEBUG
protected:
const char* _cancellation_reason;
private:
#endif
const char* _desc;
detail::completion_handler* _compl;
};

View file

@ -111,6 +111,21 @@ namespace fc {
#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 {
blocked_promise( promise_base* p=0, bool r=true )
:prom(p),required(r){}
@ -168,7 +183,7 @@ namespace fc {
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 ) {
i->prom->set_exception( e );
}

View file

@ -42,7 +42,13 @@ namespace fc {
if( !canceled() )
_run_functor( _functor, _promise_impl );
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 )
{

View file

@ -157,8 +157,8 @@ namespace fc {
fc::context* n = cur->next;
// this will move the context into the ready list.
//cur->prom->set_exception( boost::copy_exception( error::thread_quit() ) );
//cur->except_blocking_promises( thread_quit() );
cur->except_blocking_promises( std::make_shared<canceled_exception>() );
//cur->set_exception_on_blocking_promises( thread_quit() );
cur->set_exception_on_blocking_promises( std::make_shared<canceled_exception>(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting")) );
cur = n;
}

View file

@ -284,10 +284,14 @@ namespace fc {
*/
void check_fiber_exceptions() {
if( current && current->canceled ) {
#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
} else if( done ) {
ilog( "throwing canceled exception" );
FC_THROW_EXCEPTION( canceled_exception, "" );
FC_THROW_EXCEPTION( canceled_exception, "cancellation reason: thread quitting" );
// BOOST_THROW_EXCEPTION( thread_quit() );
}
}
@ -335,6 +339,7 @@ namespace fc {
next = pt_head;
pt_head = pt_head->next;
next->next = 0;
next->reinitialize();
} else { // create new context.
next = new fc::context( &thread_d::start_process_tasks, stack_alloc,
&fc::thread::current() );
@ -356,8 +361,12 @@ namespace fc {
}
if( current->canceled ) {
current->canceled = false;
//current->canceled = false;
#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;
@ -390,6 +399,7 @@ namespace fc {
current->cur_task = 0;
next->_set_active_context(0);
next->release();
current->reinitialize();
return true;
}
return false;