MAJOR BUG FIX - fc::usleep causing hang&leak

In certain cases when usleep is passed a small value, there is
a race condition that would cause the process to hang and then
when an attempt to quit the thread was made new contexts would
be allocated rapidly filling all available memory.
This commit is contained in:
Daniel Larimer 2013-08-19 14:44:13 -04:00
parent 24768d2d26
commit 7bf6374299
3 changed files with 42 additions and 20 deletions

View file

@ -136,4 +136,6 @@ set( BOOST_LIBRARIES ${Boost_THREAD_LIBRARY} ${Boost_SYSTEM_LIBRARY} ${Boost_FIL
#target_link_libraries( test_compress fc ${BOOST_LIBRARIES} ) #target_link_libraries( test_compress fc ${BOOST_LIBRARIES} )
#add_executable( test_aes tests/aes_test.cpp ) #add_executable( test_aes tests/aes_test.cpp )
#target_link_libraries( test_aes fc ${BOOST_LIBRARIES} ) #target_link_libraries( test_aes fc ${BOOST_LIBRARIES} )
#add_executable( test_sleep tests/sleep.cpp )
#target_link_libraries( test_sleep fc ${BOOST_LIBRARIES} )

View file

@ -136,7 +136,6 @@ namespace fc {
cur->canceled = true; cur->canceled = true;
cur = cur->next; cur = cur->next;
} }
my->done = true; my->done = true;
// now that we have poked all fibers... switch to the next one and // now that we have poked all fibers... switch to the next one and
@ -177,6 +176,14 @@ namespace fc {
my->check_fiber_exceptions(); my->check_fiber_exceptions();
} }
void thread::sleep_until( const time_point& tp ) { void thread::sleep_until( const time_point& tp ) {
//ilog( "sleep until ${tp} wait: ${delta}", ("tp",tp)("delta",(tp-fc::time_point::now()).count()) );
if( tp <= (time_point::now()+fc::microseconds(500)) )
{
this->yield(true);
}
my->yield_until( tp, false );
/*
my->check_fiber_exceptions(); my->check_fiber_exceptions();
BOOST_ASSERT( &current() == this ); BOOST_ASSERT( &current() == this );
@ -195,6 +202,7 @@ namespace fc {
my->current->resume_time = time_point::maximum(); my->current->resume_time = time_point::maximum();
my->check_fiber_exceptions(); my->check_fiber_exceptions();
*/
} }
int thread::wait_any_until( std::vector<promise_base::ptr>&& p, const time_point& timeout) { int thread::wait_any_until( std::vector<promise_base::ptr>&& p, const time_point& timeout) {
for( size_t i = 0; i < p.size(); ++i ) { for( size_t i = 0; i < p.size(); ++i ) {

View file

@ -89,7 +89,7 @@ namespace fc {
#if 0 #if 0
void debug( const fc::string& s ) { void debug( const fc::string& s ) {
return; return;
//boost::unique_lock<boost::mutex> lock(log_mutex()); //boost::unique_lock<boost::mutex> lock(log_mutex());
fc::cerr<<"--------------------- "<<s.c_str()<<" - "<<current; fc::cerr<<"--------------------- "<<s.c_str()<<" - "<<current;
@ -165,7 +165,7 @@ namespace fc {
void ready_push_front( const fc::context::ptr& c ) { void ready_push_front( const fc::context::ptr& c ) {
BOOST_ASSERT( c->next == nullptr ); BOOST_ASSERT( c->next == nullptr );
BOOST_ASSERT( c != current ); BOOST_ASSERT( c != current );
//if( c == current ) wlog( "pushing current to ready??" ); //if( c == current ) wlog( "pushing current to ready??" );
c->next = ready_head; c->next = ready_head;
ready_head = c; ready_head = c;
if( !ready_tail ) if( !ready_tail )
@ -174,7 +174,7 @@ namespace fc {
void ready_push_back( const fc::context::ptr& c ) { void ready_push_back( const fc::context::ptr& c ) {
BOOST_ASSERT( c->next == nullptr ); BOOST_ASSERT( c->next == nullptr );
BOOST_ASSERT( c != current ); BOOST_ASSERT( c != current );
//if( c == current ) wlog( "pushing current to ready??" ); //if( c == current ) wlog( "pushing current to ready??" );
c->next = 0; c->next = 0;
if( ready_tail ) { if( ready_tail ) {
ready_tail->next = c; ready_tail->next = c;
@ -246,6 +246,7 @@ namespace fc {
if( current && current->canceled ) { if( current && current->canceled ) {
FC_THROW_EXCEPTION( canceled_exception, "" ); FC_THROW_EXCEPTION( canceled_exception, "" );
} else if( done ) { } else if( done ) {
ilog( "throwing canceled exception" );
FC_THROW_EXCEPTION( canceled_exception, "" ); FC_THROW_EXCEPTION( canceled_exception, "" );
// BOOST_THROW_EXCEPTION( thread_quit() ); // BOOST_THROW_EXCEPTION( thread_quit() );
} }
@ -398,47 +399,58 @@ namespace fc {
*/ */
time_point check_for_timeouts() { time_point check_for_timeouts() {
if( !sleep_pqueue.size() && !task_sch_queue.size() ) { if( !sleep_pqueue.size() && !task_sch_queue.size() ) {
//ilog( "no timeouts ready" );
return time_point::maximum(); return time_point::maximum();
} }
time_point next = time_point::maximum(); time_point next = time_point::maximum();
if( task_sch_queue.size() && next > task_sch_queue.front()->_when )
next = task_sch_queue.front()->_when;
if( sleep_pqueue.size() && next > sleep_pqueue.front()->resume_time ) if( sleep_pqueue.size() && next > sleep_pqueue.front()->resume_time )
next = sleep_pqueue.front()->resume_time; next = sleep_pqueue.front()->resume_time;
if( task_sch_queue.size() && next > task_sch_queue.front()->_when )
next = task_sch_queue.front()->_when;
time_point now = time_point::now(); time_point now = time_point::now();
if( now < next ) { return next; } if( now < next ) { return next; }
// move all expired sleeping tasks to the ready queue // move all expired sleeping tasks to the ready queue
while( sleep_pqueue.size() && sleep_pqueue.front()->resume_time < now ) { while( sleep_pqueue.size() && sleep_pqueue.front()->resume_time < now )
{
fc::context::ptr c = sleep_pqueue.front(); fc::context::ptr c = sleep_pqueue.front();
std::pop_heap(sleep_pqueue.begin(), sleep_pqueue.end(), sleep_priority_less() ); std::pop_heap(sleep_pqueue.begin(), sleep_pqueue.end(), sleep_priority_less() );
//ilog( "sleep pop back..." );
sleep_pqueue.pop_back(); sleep_pqueue.pop_back();
if( c->blocking_prom.size() ) { if( c->blocking_prom.size() )
{
// ilog( "timeotu blocking prom" );
c->timeout_blocking_promises(); c->timeout_blocking_promises();
} }
else { else
if( c != current ) ready_push_front( c ); {
//ilog( "..." );
FC_ASSERT( c != current )
//ilog( "ready_push_front" );
ready_push_front( c );
} }
} }
return time_point::min(); return time_point::min();
} }
void unblock( fc::context* c ) { void unblock( fc::context* c ) {
if( fc::thread::current().my != this ) { if( fc::thread::current().my != this ) {
async( [=](){ unblock(c); } ); async( [=](){ unblock(c); } );
return; return;
} }
if( c != current ) ready_push_front(c); if( c != current ) ready_push_front(c);
} }
void yield_until( const time_point& tp, bool reschedule ) { void yield_until( const time_point& tp, bool reschedule ) {
check_fiber_exceptions(); check_fiber_exceptions();
if( tp <= time_point::now() ) if( tp <= (time_point::now()+fc::microseconds(500)) )
{
return; return;
}
if( !current ) { if( !current ) {
current = new fc::context(&fc::thread::current()); current = new fc::context(&fc::thread::current());
@ -450,7 +462,7 @@ namespace fc {
sleep_pqueue.push_back(current); sleep_pqueue.push_back(current);
std::push_heap( sleep_pqueue.begin(), std::push_heap( sleep_pqueue.begin(),
sleep_pqueue.end(), sleep_priority_less() ); sleep_pqueue.end(), sleep_priority_less() );
start_next_fiber(reschedule); start_next_fiber(reschedule);
// clear current context from sleep queue... // clear current context from sleep queue...