2013-06-05 19:19:00 +00:00
|
|
|
#include <fc/thread/thread.hpp>
|
2012-09-08 02:50:37 +00:00
|
|
|
#include <fc/vector.hpp>
|
2013-06-05 19:19:00 +00:00
|
|
|
#include <fc/io/sstream.hpp>
|
|
|
|
|
#include <fc/log/logger.hpp>
|
2012-09-08 02:50:37 +00:00
|
|
|
#include "thread_d.hpp"
|
|
|
|
|
|
2014-04-01 20:56:11 +00:00
|
|
|
#if defined(_MSC_VER) && !defined(NDEBUG)
|
2016-05-22 05:33:19 +00:00
|
|
|
# include <windows.h>
|
2014-04-01 20:56:11 +00:00
|
|
|
const DWORD MS_VC_EXCEPTION=0x406D1388;
|
|
|
|
|
|
|
|
|
|
#pragma pack(push,8)
|
|
|
|
|
typedef struct tagTHREADNAME_INFO
|
|
|
|
|
{
|
|
|
|
|
DWORD dwType; // Must be 0x1000.
|
|
|
|
|
LPCSTR szName; // Pointer to name (in user addr space).
|
|
|
|
|
DWORD dwThreadID; // Thread ID (-1=caller thread).
|
|
|
|
|
DWORD dwFlags; // Reserved for future use, must be zero.
|
|
|
|
|
} THREADNAME_INFO;
|
|
|
|
|
#pragma pack(pop)
|
|
|
|
|
|
|
|
|
|
static void set_thread_name(const char* threadName)
|
|
|
|
|
{
|
|
|
|
|
THREADNAME_INFO info;
|
|
|
|
|
info.dwType = 0x1000;
|
|
|
|
|
info.szName = threadName;
|
|
|
|
|
info.dwThreadID = -1;
|
|
|
|
|
info.dwFlags = 0;
|
|
|
|
|
|
|
|
|
|
__try
|
|
|
|
|
{
|
|
|
|
|
RaiseException(MS_VC_EXCEPTION, 0, sizeof(info)/sizeof(ULONG_PTR), (ULONG_PTR*)&info);
|
|
|
|
|
}
|
|
|
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-01 15:32:21 +00:00
|
|
|
#elif defined(__linux__)
|
2014-04-01 20:56:11 +00:00
|
|
|
# include <pthread.h>
|
|
|
|
|
static void set_thread_name(const char* threadName)
|
|
|
|
|
{
|
|
|
|
|
pthread_setname_np(pthread_self(), threadName);
|
|
|
|
|
}
|
|
|
|
|
#elif defined(__APPLE__) && !defined(NDEBUG)
|
|
|
|
|
# include <pthread.h>
|
|
|
|
|
static void set_thread_name(const char* threadName)
|
|
|
|
|
{
|
|
|
|
|
pthread_setname_np(threadName);
|
|
|
|
|
}
|
|
|
|
|
#else
|
|
|
|
|
static void set_thread_name(const char* threadName)
|
|
|
|
|
{
|
|
|
|
|
// do nothing in release mode
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
2012-09-08 02:50:37 +00:00
|
|
|
namespace fc {
|
2012-09-09 15:34:26 +00:00
|
|
|
const char* thread_name() {
|
|
|
|
|
return thread::current().name().c_str();
|
2012-09-09 23:44:49 +00:00
|
|
|
}
|
|
|
|
|
void* thread_ptr() {
|
|
|
|
|
return &thread::current();
|
2012-09-09 15:34:26 +00:00
|
|
|
}
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2012-09-09 15:12:15 +00:00
|
|
|
thread*& current_thread() {
|
2018-10-03 08:47:02 +00:00
|
|
|
#ifdef _MSC_VER
|
2012-09-09 15:12:15 +00:00
|
|
|
static __declspec(thread) thread* t = NULL;
|
2018-10-03 08:47:02 +00:00
|
|
|
#else
|
2012-09-09 15:12:15 +00:00
|
|
|
static __thread thread* t = NULL;
|
2018-10-03 08:47:02 +00:00
|
|
|
#endif
|
2012-09-09 15:12:15 +00:00
|
|
|
return t;
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-03 08:47:02 +00:00
|
|
|
thread::thread( const std::string& name, thread_idle_notifier* notifier ) {
|
2014-07-27 21:37:21 +00:00
|
|
|
promise<void>::ptr p(new promise<void>("thread start"));
|
2018-10-03 08:47:02 +00:00
|
|
|
boost::thread* t = new boost::thread( [this,p,name,notifier]() {
|
2012-09-08 02:50:37 +00:00
|
|
|
try {
|
2014-10-21 14:25:28 +00:00
|
|
|
set_thread_name(name.c_str()); // set thread's name for the debugger to display
|
2018-10-03 08:47:02 +00:00
|
|
|
this->my = new thread_d( *this, notifier );
|
2012-09-09 15:12:15 +00:00
|
|
|
current_thread() = this;
|
2012-09-08 02:50:37 +00:00
|
|
|
p->set_value();
|
|
|
|
|
exec();
|
2013-06-05 19:19:00 +00:00
|
|
|
} catch ( fc::exception& e ) {
|
|
|
|
|
wlog( "unhandled exception" );
|
|
|
|
|
p->set_exception( e.dynamic_copy_exception() );
|
2012-09-08 02:50:37 +00:00
|
|
|
} catch ( ... ) {
|
2013-06-05 19:19:00 +00:00
|
|
|
wlog( "unhandled exception" );
|
|
|
|
|
p->set_exception( std::make_shared<unhandled_exception>( FC_LOG_MESSAGE( warn, "unhandled exception: ${diagnostic}", ("diagnostic",boost::current_exception_diagnostic_information()) ) ) );
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
|
|
|
|
} );
|
|
|
|
|
p->wait();
|
|
|
|
|
my->boost_thread = t;
|
2014-10-21 14:25:28 +00:00
|
|
|
my->name = name;
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
|
|
|
|
thread::thread( thread_d* ) {
|
|
|
|
|
my = new thread_d(*this);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread::~thread() {
|
2014-10-21 14:25:28 +00:00
|
|
|
if( my )
|
2018-02-02 12:29:46 +00:00
|
|
|
quit();
|
|
|
|
|
|
|
|
|
|
delete my;
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
thread& thread::current() {
|
2016-10-21 18:06:22 +00:00
|
|
|
if( !current_thread() )
|
2014-10-21 14:25:28 +00:00
|
|
|
current_thread() = new thread((thread_d*)0);
|
2012-09-09 15:12:15 +00:00
|
|
|
return *current_thread();
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
2014-10-21 14:25:28 +00:00
|
|
|
|
2018-02-02 12:29:46 +00:00
|
|
|
void thread::cleanup() {
|
|
|
|
|
delete current_thread();
|
|
|
|
|
current_thread() = nullptr;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
const string& thread::name()const
|
|
|
|
|
{
|
|
|
|
|
return my->name;
|
2014-10-21 14:25:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void thread::set_name( const fc::string& n )
|
2016-10-21 18:06:22 +00:00
|
|
|
{
|
2014-10-21 14:25:28 +00:00
|
|
|
if (!is_current())
|
|
|
|
|
{
|
2018-10-03 08:47:02 +00:00
|
|
|
async([this,n](){ set_name(n); }, "set_name").wait();
|
2014-10-21 14:25:28 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
my->name = n;
|
2014-10-21 14:25:28 +00:00
|
|
|
set_thread_name(my->name.c_str()); // set thread's name for the debugger to display
|
|
|
|
|
}
|
2014-08-24 22:33:05 +00:00
|
|
|
|
|
|
|
|
const char* thread::current_task_desc() const
|
|
|
|
|
{
|
|
|
|
|
if (my->current && my->current->cur_task)
|
|
|
|
|
return my->current->cur_task->get_desc();
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
|
2013-06-05 19:19:00 +00:00
|
|
|
void thread::debug( const fc::string& d ) { /*my->debug(d);*/ }
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
void thread::quit()
|
2014-10-21 14:25:28 +00:00
|
|
|
{
|
|
|
|
|
//if quitting from a different thread, start quit task on thread.
|
|
|
|
|
//If we have and know our attached boost thread, wait for it to finish, then return.
|
2018-02-02 12:29:46 +00:00
|
|
|
if( !is_current() )
|
2014-10-21 14:25:28 +00:00
|
|
|
{
|
2018-02-02 12:29:46 +00:00
|
|
|
auto t = my->boost_thread;
|
2018-10-03 08:47:02 +00:00
|
|
|
async( [this](){quit();}, "thread::quit" );
|
2018-02-02 12:29:46 +00:00
|
|
|
if( t )
|
|
|
|
|
t->join();
|
2014-10-21 14:25:28 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2018-02-02 12:29:46 +00:00
|
|
|
|
2014-10-21 14:25:28 +00:00
|
|
|
my->done = true;
|
|
|
|
|
// We are quiting from our own thread...
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2014-10-21 14:25:28 +00:00
|
|
|
// break all promises, thread quit!
|
2016-10-21 18:06:22 +00:00
|
|
|
while( my->blocked )
|
2014-10-21 14:25:28 +00:00
|
|
|
{
|
|
|
|
|
fc::context* cur = my->blocked;
|
2016-10-21 18:06:22 +00:00
|
|
|
while( cur )
|
2014-10-21 14:25:28 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
fc::context* n = cur->next;
|
2014-10-21 14:25:28 +00:00
|
|
|
// this will move the context into the ready list.
|
|
|
|
|
cur->set_exception_on_blocking_promises( std::make_shared<canceled_exception>(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting")) );
|
2016-10-21 18:06:22 +00:00
|
|
|
|
2012-09-08 02:50:37 +00:00
|
|
|
cur = n;
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
if( my->blocked )
|
|
|
|
|
debug( "on quit" );
|
2014-10-21 14:25:28 +00:00
|
|
|
}
|
|
|
|
|
BOOST_ASSERT( my->blocked == 0 );
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2014-10-21 14:25:28 +00:00
|
|
|
for (task_base* unstarted_task : my->task_pqueue)
|
|
|
|
|
unstarted_task->set_exception(std::make_shared<canceled_exception>(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting")));
|
|
|
|
|
my->task_pqueue.clear();
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2014-10-21 14:25:28 +00:00
|
|
|
for (task_base* scheduled_task : my->task_sch_queue)
|
|
|
|
|
scheduled_task->set_exception(std::make_shared<canceled_exception>(FC_LOG_MESSAGE(error, "cancellation reason: thread quitting")));
|
|
|
|
|
my->task_sch_queue.clear();
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
|
2014-10-21 14:25:28 +00:00
|
|
|
|
|
|
|
|
// move all sleep tasks to ready
|
|
|
|
|
for( uint32_t i = 0; i < my->sleep_pqueue.size(); ++i )
|
|
|
|
|
my->add_context_to_ready_list( my->sleep_pqueue[i] );
|
|
|
|
|
my->sleep_pqueue.clear();
|
|
|
|
|
|
|
|
|
|
// move all idle tasks to ready
|
|
|
|
|
fc::context* cur = my->pt_head;
|
2016-10-21 18:06:22 +00:00
|
|
|
while( cur )
|
2014-10-21 14:25:28 +00:00
|
|
|
{
|
|
|
|
|
fc::context* n = cur->next;
|
|
|
|
|
cur->next = 0;
|
|
|
|
|
my->add_context_to_ready_list( cur );
|
|
|
|
|
cur = n;
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
// mark all ready tasks (should be everyone)... as canceled
|
2014-10-21 14:25:28 +00:00
|
|
|
for (fc::context* ready_context : my->ready_heap)
|
|
|
|
|
ready_context->canceled = true;
|
|
|
|
|
|
|
|
|
|
// now that we have poked all fibers... switch to the next one and
|
|
|
|
|
// let them all quit.
|
|
|
|
|
while (!my->ready_heap.empty())
|
|
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
my->start_next_fiber(true);
|
2014-10-21 14:25:28 +00:00
|
|
|
my->check_for_timeouts();
|
|
|
|
|
}
|
|
|
|
|
my->clear_free_list();
|
|
|
|
|
my->cleanup_thread_specific_data();
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
|
|
|
|
|
void thread::exec()
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
if( !my->current )
|
2014-10-16 20:26:19 +00:00
|
|
|
my->current = new fc::context(&fc::thread::current());
|
2016-10-21 18:06:22 +00:00
|
|
|
|
|
|
|
|
try
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
my->process_tasks();
|
|
|
|
|
}
|
2014-08-21 18:36:29 +00:00
|
|
|
catch( canceled_exception& e )
|
2013-06-05 19:19:00 +00:00
|
|
|
{
|
2016-02-15 16:59:28 +00:00
|
|
|
dlog( "thread canceled: ${e}", ("e", e.to_detail_string()) );
|
2013-06-05 19:19:00 +00:00
|
|
|
}
|
2012-09-08 02:50:37 +00:00
|
|
|
delete my->current;
|
|
|
|
|
my->current = 0;
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
|
|
|
|
|
bool thread::is_running()const
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
return !my->done;
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
|
|
|
|
|
priority thread::current_priority()const
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
BOOST_ASSERT(my);
|
2016-10-21 18:06:22 +00:00
|
|
|
if( my->current )
|
2014-10-16 20:26:19 +00:00
|
|
|
return my->current->prio;
|
2012-09-08 02:50:37 +00:00
|
|
|
return priority();
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
void thread::yield(bool reschedule)
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
my->check_fiber_exceptions();
|
|
|
|
|
my->start_next_fiber(reschedule);
|
|
|
|
|
my->check_fiber_exceptions();
|
|
|
|
|
}
|
2014-06-29 01:46:10 +00:00
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
void thread::sleep_until( const time_point& tp )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
if( tp <= (time_point::now()+fc::microseconds(10000)) )
|
2014-10-16 20:26:19 +00:00
|
|
|
yield(true);
|
|
|
|
|
my->yield_until( tp, false );
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
2014-06-29 01:46:10 +00:00
|
|
|
|
2013-06-27 18:19:08 +00:00
|
|
|
int thread::wait_any_until( std::vector<promise_base::ptr>&& p, const time_point& timeout) {
|
2014-10-16 20:26:19 +00:00
|
|
|
for( size_t i = 0; i < p.size(); ++i )
|
2016-10-21 18:06:22 +00:00
|
|
|
if( p[i]->ready() )
|
2014-10-16 20:26:19 +00:00
|
|
|
return i;
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
if( timeout < time_point::now() )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
|
|
|
|
fc::stringstream ss;
|
|
|
|
|
for( auto i = p.begin(); i != p.end(); ++i )
|
|
|
|
|
ss << (*i)->get_desc() << ", ";
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2014-10-16 20:26:19 +00:00
|
|
|
FC_THROW_EXCEPTION( timeout_exception, "${task}", ("task",ss.str()) );
|
2012-12-31 16:06:10 +00:00
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
|
2014-10-16 20:26:19 +00:00
|
|
|
if( !my->current )
|
2016-10-21 18:06:22 +00:00
|
|
|
my->current = new fc::context(&fc::thread::current());
|
|
|
|
|
|
2014-10-16 20:26:19 +00:00
|
|
|
for( uint32_t i = 0; i < p.size(); ++i )
|
|
|
|
|
my->current->add_blocking_promise(p[i].get(),false);
|
2012-09-08 02:50:37 +00:00
|
|
|
|
|
|
|
|
// if not max timeout, added to sleep pqueue
|
2016-10-21 18:06:22 +00:00
|
|
|
if( timeout != time_point::maximum() )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
my->current->resume_time = timeout;
|
|
|
|
|
my->sleep_pqueue.push_back(my->current);
|
|
|
|
|
std::push_heap( my->sleep_pqueue.begin(),
|
2016-10-21 18:06:22 +00:00
|
|
|
my->sleep_pqueue.end(),
|
2012-09-08 02:50:37 +00:00
|
|
|
sleep_priority_less() );
|
|
|
|
|
}
|
2014-10-16 20:26:19 +00:00
|
|
|
|
2012-09-08 02:50:37 +00:00
|
|
|
my->add_to_blocked( my->current );
|
|
|
|
|
my->start_next_fiber();
|
|
|
|
|
|
2014-10-16 20:26:19 +00:00
|
|
|
for( auto i = p.begin(); i != p.end(); ++i )
|
|
|
|
|
my->current->remove_blocking_promise(i->get());
|
2016-10-21 18:06:22 +00:00
|
|
|
|
2012-09-08 02:50:37 +00:00
|
|
|
my->check_fiber_exceptions();
|
|
|
|
|
|
2014-10-16 20:26:19 +00:00
|
|
|
for( uint32_t i = 0; i < p.size(); ++i )
|
2016-10-21 18:06:22 +00:00
|
|
|
if( p[i]->ready() )
|
2014-10-16 20:26:19 +00:00
|
|
|
return i;
|
|
|
|
|
|
2012-09-08 02:50:37 +00:00
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-27 21:37:21 +00:00
|
|
|
void thread::async_task( task_base* t, const priority& p ) {
|
2014-10-14 18:21:42 +00:00
|
|
|
async_task( t, p, time_point::min() );
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
|
|
|
|
|
2012-09-11 03:13:31 +00:00
|
|
|
void thread::poke() {
|
|
|
|
|
boost::unique_lock<boost::mutex> lock(my->task_ready_mutex);
|
|
|
|
|
my->task_ready.notify_one();
|
|
|
|
|
}
|
|
|
|
|
|
2014-07-27 21:37:21 +00:00
|
|
|
void thread::async_task( task_base* t, const priority& p, const time_point& tp ) {
|
2012-09-25 21:45:28 +00:00
|
|
|
assert(my);
|
2012-09-23 01:26:13 +00:00
|
|
|
t->_when = tp;
|
2012-09-08 02:50:37 +00:00
|
|
|
task_base* stale_head = my->task_in_queue.load(boost::memory_order_relaxed);
|
|
|
|
|
do { t->_next = stale_head;
|
|
|
|
|
}while( !my->task_in_queue.compare_exchange_weak( stale_head, t, boost::memory_order_release ) );
|
|
|
|
|
|
|
|
|
|
// Because only one thread can post the 'first task', only that thread will attempt
|
|
|
|
|
// to aquire the lock and therefore there should be no contention on this lock except
|
2016-10-21 18:06:22 +00:00
|
|
|
// when *this thread is about to block on a wait condition.
|
|
|
|
|
if( this != ¤t() && !stale_head ) {
|
2012-09-08 02:50:37 +00:00
|
|
|
boost::unique_lock<boost::mutex> lock(my->task_ready_mutex);
|
|
|
|
|
my->task_ready.notify_one();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void yield() {
|
|
|
|
|
thread::current().yield();
|
|
|
|
|
}
|
|
|
|
|
void usleep( const microseconds& u ) {
|
|
|
|
|
thread::current().sleep_until( time_point::now() + u);
|
|
|
|
|
}
|
|
|
|
|
void sleep_until( const time_point& tp ) {
|
|
|
|
|
thread::current().sleep_until(tp);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
void exec()
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
return thread::current().exec();
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
int wait_any( std::vector<promise_base::ptr>&& v, const microseconds& timeout_us )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
return thread::current().wait_any_until( fc::move(v), time_point::now() + timeout_us );
|
|
|
|
|
}
|
2014-10-16 20:26:19 +00:00
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
int wait_any_until( std::vector<promise_base::ptr>&& v, const time_point& tp )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
return thread::current().wait_any_until( fc::move(v), tp );
|
|
|
|
|
}
|
2014-10-16 20:26:19 +00:00
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
void thread::wait_until( promise_base::ptr&& p, const time_point& timeout )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
if( p->ready() )
|
2014-10-16 20:26:19 +00:00
|
|
|
return;
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
if( timeout < time_point::now() )
|
2014-10-16 20:26:19 +00:00
|
|
|
FC_THROW_EXCEPTION( timeout_exception, "${task}", ("task", p->get_desc()) );
|
2016-10-21 18:06:22 +00:00
|
|
|
|
|
|
|
|
if( !my->current )
|
|
|
|
|
my->current = new fc::context(&fc::thread::current());
|
|
|
|
|
|
2014-10-16 20:26:19 +00:00
|
|
|
my->current->add_blocking_promise(p.get(), true);
|
2012-09-08 02:50:37 +00:00
|
|
|
|
|
|
|
|
// if not max timeout, added to sleep pqueue
|
2016-10-21 18:06:22 +00:00
|
|
|
if( timeout != time_point::maximum() )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
my->current->resume_time = timeout;
|
|
|
|
|
my->sleep_pqueue.push_back(my->current);
|
|
|
|
|
std::push_heap( my->sleep_pqueue.begin(),
|
2016-10-21 18:06:22 +00:00
|
|
|
my->sleep_pqueue.end(),
|
2014-10-16 20:26:19 +00:00
|
|
|
sleep_priority_less() );
|
2012-09-08 02:50:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
my->add_to_blocked( my->current );
|
|
|
|
|
|
|
|
|
|
my->start_next_fiber();
|
|
|
|
|
|
|
|
|
|
my->current->remove_blocking_promise(p.get());
|
|
|
|
|
|
|
|
|
|
my->check_fiber_exceptions();
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-21 18:06:22 +00:00
|
|
|
void thread::notify( const promise_base::ptr& p )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
BOOST_ASSERT(p->ready());
|
2016-10-21 18:06:22 +00:00
|
|
|
if( !is_current() )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-23 06:01:27 +00:00
|
|
|
this->async( [=](){ notify(p); }, "notify", priority::max() );
|
2012-09-08 02:50:37 +00:00
|
|
|
return;
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
// TODO: store a list of blocked contexts with the promise
|
2012-09-08 02:50:37 +00:00
|
|
|
// to accelerate the lookup.... unless it introduces contention...
|
2016-10-21 18:06:22 +00:00
|
|
|
|
2012-09-08 02:50:37 +00:00
|
|
|
// iterate over all blocked contexts
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
fc::context* cur_blocked = my->blocked;
|
|
|
|
|
fc::context* prev_blocked = 0;
|
2016-10-21 18:06:22 +00:00
|
|
|
while( cur_blocked )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
// if the blocked context is waiting on this promise
|
|
|
|
|
if( cur_blocked->try_unblock( p.get() ) )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
// remove it from the blocked list.
|
|
|
|
|
|
|
|
|
|
// remove this context from the sleep queue...
|
2016-10-21 18:06:22 +00:00
|
|
|
for( uint32_t i = 0; i < my->sleep_pqueue.size(); ++i )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2016-10-21 18:06:22 +00:00
|
|
|
if( my->sleep_pqueue[i] == cur_blocked )
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 02:50:37 +00:00
|
|
|
my->sleep_pqueue[i]->blocking_prom.clear();
|
|
|
|
|
my->sleep_pqueue[i] = my->sleep_pqueue.back();
|
|
|
|
|
my->sleep_pqueue.pop_back();
|
|
|
|
|
std::make_heap( my->sleep_pqueue.begin(),my->sleep_pqueue.end(), sleep_priority_less() );
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
auto cur = cur_blocked;
|
2016-10-21 18:06:22 +00:00
|
|
|
if( prev_blocked )
|
|
|
|
|
{
|
|
|
|
|
prev_blocked->next_blocked = cur_blocked->next_blocked;
|
2012-09-08 02:50:37 +00:00
|
|
|
cur_blocked = prev_blocked->next_blocked;
|
2016-10-21 18:06:22 +00:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
my->blocked = cur_blocked->next_blocked;
|
2012-09-08 02:50:37 +00:00
|
|
|
cur_blocked = my->blocked;
|
|
|
|
|
}
|
|
|
|
|
cur->next_blocked = 0;
|
2014-10-14 18:21:42 +00:00
|
|
|
my->add_context_to_ready_list( cur );
|
2016-10-21 18:06:22 +00:00
|
|
|
}
|
|
|
|
|
else
|
2014-10-16 20:26:19 +00:00
|
|
|
{ // goto the next blocked task
|
2012-09-08 02:50:37 +00:00
|
|
|
prev_blocked = cur_blocked;
|
|
|
|
|
cur_blocked = cur_blocked->next_blocked;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-10-21 18:06:22 +00:00
|
|
|
|
|
|
|
|
bool thread::is_current()const
|
2014-10-16 20:26:19 +00:00
|
|
|
{
|
2012-09-08 21:37:25 +00:00
|
|
|
return this == ¤t();
|
|
|
|
|
}
|
2012-09-08 02:50:37 +00:00
|
|
|
|
2014-09-08 14:31:07 +00:00
|
|
|
void thread::notify_task_has_been_canceled()
|
|
|
|
|
{
|
2018-10-03 08:47:02 +00:00
|
|
|
async( [this](){ my->notify_task_has_been_canceled(); }, "notify_task_has_been_canceled", priority::max() );
|
2014-09-08 14:31:07 +00:00
|
|
|
}
|
|
|
|
|
|
2014-10-21 14:25:28 +00:00
|
|
|
void thread::unblock(fc::context* c)
|
|
|
|
|
{
|
|
|
|
|
my->unblock(c);
|
|
|
|
|
}
|
|
|
|
|
|
2018-10-03 08:47:02 +00:00
|
|
|
namespace detail {
|
|
|
|
|
idle_guard::idle_guard( thread_d* t ) : notifier(t->notifier)
|
|
|
|
|
{
|
|
|
|
|
if( notifier )
|
|
|
|
|
{
|
|
|
|
|
task_base* work = notifier->idle();
|
|
|
|
|
if( work )
|
|
|
|
|
{
|
|
|
|
|
task_base* stale_head = t->task_in_queue.load(boost::memory_order_relaxed);
|
|
|
|
|
do {
|
|
|
|
|
work->_next = stale_head;
|
|
|
|
|
} while( !t->task_in_queue.compare_exchange_weak( stale_head, work, boost::memory_order_release ) );
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
idle_guard::~idle_guard()
|
|
|
|
|
{
|
|
|
|
|
if( notifier ) notifier->busy();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-21 14:25:28 +00:00
|
|
|
|
2014-03-08 23:48:19 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
|
/* support for providing a structured exception handler for async tasks */
|
|
|
|
|
namespace detail
|
|
|
|
|
{
|
|
|
|
|
unhandled_exception_filter_type unhandled_structured_exception_filter = nullptr;
|
|
|
|
|
}
|
|
|
|
|
void set_unhandled_structured_exception_filter(unhandled_exception_filter_type new_filter)
|
|
|
|
|
{
|
|
|
|
|
detail::unhandled_structured_exception_filter = new_filter;
|
|
|
|
|
}
|
|
|
|
|
unhandled_exception_filter_type get_unhandled_structured_exception_filter()
|
|
|
|
|
{
|
|
|
|
|
return detail::unhandled_structured_exception_filter;
|
|
|
|
|
}
|
|
|
|
|
#endif // _MSC_VER
|
|
|
|
|
} // end namespace fc
|