From de580474a34242c4c16acf415101dfe0913572cf Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Fri, 20 Feb 2015 19:11:08 +0100 Subject: [PATCH 01/28] make inline_executor, loop_executor and thread_executor copyable. --- example/executor.cpp | 12 + .../thread/executors/inline_executor.hpp | 335 +++++++++++------- .../boost/thread/executors/loop_executor.hpp | 247 +++++++++---- .../thread/executors/thread_executor.hpp | 186 +++++++--- 4 files changed, 540 insertions(+), 240 deletions(-) diff --git a/example/executor.cpp b/example/executor.cpp index 7f6b1ce3..f8e9eb14 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -98,6 +98,10 @@ int test_executor_adaptor() submit_some(ea); } // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::loop_executor e1; + boost::loop_executor e2 = e1; + } { boost::executor_adaptor < boost::loop_executor > ea2; submit_some( ea2); @@ -112,11 +116,19 @@ int test_executor_adaptor() } #endif // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::inline_executor e1; + boost::inline_executor e2 = e1; + } { boost::executor_adaptor < boost::inline_executor > ea1; submit_some(ea1); } // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::thread_executor e1; + boost::thread_executor e2 = e1; + } { boost::executor_adaptor < boost::thread_executor > ea1; submit_some(ea1); diff --git a/include/boost/thread/executors/inline_executor.hpp b/include/boost/thread/executors/inline_executor.hpp index 5dd52318..c975bd83 100644 --- a/include/boost/thread/executors/inline_executor.hpp +++ b/include/boost/thread/executors/inline_executor.hpp @@ -25,142 +25,227 @@ namespace executors public: /// type-erasure to store the works to do typedef executors::work work; - bool closed_; - mutable mutex mtx_; - /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. - */ - bool try_executing_one() - { - return false; - } + private: + struct shared_state { + typedef executors::work work; + + bool closed_; + mutable mutex mtx_; + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return false; + } + + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a inline executor that runs closures immediately. + * + * \b Throws: Nothing. + */ + shared_state() + : closed_(false) + { + } + /** + * \b Effects: Destroys the inline executor. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c inline_executor destructor. + */ + ~shared_state() + { + // signal to all the worker thread that there will be no more submissions. + close(); + } + + /** + * \b Effects: close the \c inline_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + lock_guard lk(mtx_); + closed_ = true; + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed(lock_guard& ) const + { + return closed_; + } + bool closed() const + { + lock_guard lk(mtx_); + return closed(lk); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c inline_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + } + try + { + closure(); + } + catch (...) + { + std::terminate(); + return; + } + } + #endif + void submit(void (*closure)()) + { + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + } + try + { + closure(); + } + catch (...) + { + std::terminate(); + return; + } + } + + template + void submit(BOOST_THREAD_FWD_REF(Closure) closure) + { + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + } + try + { + closure(); + } + catch (...) + { + std::terminate(); + return; + } + } + + /** + * \b Requires: This must be called from an scheduled task. + * + * \b Effects: reschedule functions until pred() + */ + template + bool reschedule_until(Pred const& ) + { + return false; + } + }; public: - /// inline_executor is not copyable. - BOOST_THREAD_NO_COPYABLE(inline_executor) + /** + * \b Effects: creates a inline executor that runs closures immediately. + * + * \b Throws: Nothing. + */ + inline_executor() + : pimpl(make_shared()) + { + } - /** - * \b Effects: creates a inline executor that runs closures immediately. - * - * \b Throws: Nothing. - */ - inline_executor() - : closed_(false) - { - } - /** - * \b Effects: Destroys the inline executor. - * - * \b Synchronization: The completion of all the closures happen before the completion of the \c inline_executor destructor. - */ - ~inline_executor() - { - // signal to all the worker thread that there will be no more submissions. - close(); - } + /** + * \b Effects: close the \c inline_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + pimpl->close(); + } - /** - * \b Effects: close the \c inline_executor for submissions. - * The loop will work until there is no more closures to run. - */ - void close() - { - lock_guard lk(mtx_); - closed_ = true; - } + /** + * \b Returns: whether the executor is closed for submissions. + */ + bool closed() const + { + return pimpl->closed(); + } - /** - * \b Returns: whether the pool is closed for submissions. - */ - bool closed(lock_guard& ) - { - return closed_; - } - bool closed() - { - lock_guard lk(mtx_); - return closed(lk); - } - - /** - * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. - * - * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. - * If invoked closure throws an exception the \c inline_executor will call \c std::terminate, as is the case with threads. - * - * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. - * - * \b Throws: \c sync_queue_is_closed if the thread pool is closed. - * Whatever exception that can be throw while storing the closure. - */ + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c inline_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - template - void submit(Closure & closure) - { - { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - } - try - { - closure(); - } - catch (...) - { - std::terminate(); - return; - } - } + template + void submit(Closure & closure) + { + pimpl->submit(closure); + } #endif - void submit(void (*closure)()) - { - { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - } - try - { - closure(); - } - catch (...) - { - std::terminate(); - return; - } - } + void submit(void (*closure)()) + { + pimpl->submit(closure); + } - template - void submit(BOOST_THREAD_FWD_REF(Closure) closure) - { - { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - } - try - { - closure(); - } - catch (...) - { - std::terminate(); - return; - } - } + template + void submit(BOOST_THREAD_FWD_REF(Closure) closure) + { + pimpl->submit(boost::forward(closure)); + } - /** - * \b Requires: This must be called from an scheduled task. - * - * \b Effects: reschedule functions until pred() - */ - template - bool reschedule_until(Pred const& ) - { - return false; - } + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return pimpl->try_executing_one(); + } + /** + * \b Requires: This must be called from an scheduled task. + * + * \b Effects: reschedule functions until pred() + */ + template + bool reschedule_until(Pred const& p) + { + return pimpl->reschedule_until(p); + } + private: + shared_ptr pimpl; }; } using executors::inline_executor; diff --git a/include/boost/thread/executors/loop_executor.hpp b/include/boost/thread/executors/loop_executor.hpp index e9eadadf..a8ca3758 100644 --- a/include/boost/thread/executors/loop_executor.hpp +++ b/include/boost/thread/executors/loop_executor.hpp @@ -30,59 +30,170 @@ namespace executors /// type-erasure to store the works to do typedef executors::work work; private: - /// the thread safe work queue - concurrent::sync_queue work_queue; - public: - /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. - */ - bool try_executing_one() - { - work task; - try + struct shared_state { + typedef executors::work work; + /// the thread safe work queue + concurrent::sync_queue work_queue; + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() { - if (work_queue.try_pull(task) == queue_op_status::success) + work task; + try { + if (work_queue.try_pull(task) == queue_op_status::success) + { + task(); + return true; + } + return false; + } + catch (...) + { + std::terminate(); + return false; + } + } + /** + * Effects: schedule one task or yields + * Throws: whatever the current task constructor throws or the task() throws. + */ + void schedule_one_or_yield() + { + if ( ! try_executing_one()) + { + this_thread::yield(); + } + } + + /// loop_executor is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + shared_state() + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c loop_executor destructor. + */ + ~shared_state() + { + // signal to all the worker thread that there will be no more submissions. + close(); + } + + /** + * The main loop of the worker thread + */ + void loop() + { + while (!closed()) + { + schedule_one_or_yield(); + } + while (try_executing_one()) + { + } + } + + /** + * \b Effects: close the \c loop_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + work_queue.close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return work_queue.closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c loop_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + work_queue.push(work(closure)); + } + #endif + void submit(void (*closure)()) + { + work_queue.push(work(closure)); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + work_queue.push(work(boost::forward(closure))); + } + + /** + * \b Requires: This must be called from an scheduled task. + * + * \b Effects: reschedule functions until pred() + */ + template + bool reschedule_until(Pred const& pred) + { + do { + if ( ! try_executing_one()) + { + return false; + } + } while (! pred()); + return true; + } + + /** + * run queued closures + */ + void run_queued_closures() + { + sync_queue::underlying_queue_type q = work_queue.underlying_queue(); + while (! q.empty()) + { + work& task = q.front(); task(); - return true; + q.pop_front(); } - return false; } - catch (...) - { - std::terminate(); - return false; - } - } - private: - /** - * Effects: schedule one task or yields - * Throws: whatever the current task constructor throws or the task() throws. - */ - void schedule_one_or_yield() - { - if ( ! try_executing_one()) - { - this_thread::yield(); - } - } - - - + }; public: - /// loop_executor is not copyable. - BOOST_THREAD_NO_COPYABLE(loop_executor) - /** * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. * * \b Throws: Whatever exception is thrown while initializing the needed resources. */ loop_executor() + : pimpl(make_shared()) { } /** @@ -92,22 +203,33 @@ namespace executors */ ~loop_executor() { - // signal to all the worker thread that there will be no more submissions. - close(); } + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return pimpl->try_executing_one(); + } +// /** +// * Effects: schedule one task or yields +// * Throws: whatever the current task constructor throws or the task() throws. +// */ +// void schedule_one_or_yield() +// { +// return pimpl->schedule_one_or_yield(); +// } + + /** * The main loop of the worker thread */ void loop() { - while (!closed()) - { - schedule_one_or_yield(); - } - while (try_executing_one()) - { - } + pimpl->loop(); } /** @@ -116,7 +238,7 @@ namespace executors */ void close() { - work_queue.close(); + pimpl->close(); } /** @@ -124,7 +246,7 @@ namespace executors */ bool closed() { - return work_queue.closed(); + return pimpl->closed(); } /** @@ -143,18 +265,18 @@ namespace executors template void submit(Closure & closure) { - work_queue.push(work(closure)); + pimpl->submit(closure); } #endif void submit(void (*closure)()) { - work_queue.push(work(closure)); + pimpl->submit(closure); } template void submit(BOOST_THREAD_RV_REF(Closure) closure) { - work_queue.push(work(boost::forward(closure))); + pimpl->submit(boost::forward(closure)); } /** @@ -165,13 +287,7 @@ namespace executors template bool reschedule_until(Pred const& pred) { - do { - if ( ! try_executing_one()) - { - return false; - } - } while (! pred()); - return true; + return pimpl->reschedule_until(pred); } /** @@ -179,15 +295,10 @@ namespace executors */ void run_queued_closures() { - sync_queue::underlying_queue_type q = work_queue.underlying_queue(); - while (! q.empty()) - { - work& task = q.front(); - task(); - q.pop_front(); - } + pimpl->run_queued_closures(); } - + private: + shared_ptr pimpl; }; } using executors::loop_executor; diff --git a/include/boost/thread/executors/thread_executor.hpp b/include/boost/thread/executors/thread_executor.hpp index a8cd5c21..eb57adfa 100644 --- a/include/boost/thread/executors/thread_executor.hpp +++ b/include/boost/thread/executors/thread_executor.hpp @@ -29,33 +29,134 @@ namespace executors public: /// type-erasure to store the works to do typedef executors::work work; - bool closed_; - typedef scoped_thread<> thread_t; - typedef csbl::vector threads_type; - threads_type threads_; - mutable mutex mtx_; + private: - /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. - */ - bool try_executing_one() - { - return false; - } + struct shared_state { + typedef executors::work work; + bool closed_; + typedef scoped_thread<> thread_t; + typedef csbl::vector threads_type; + threads_type threads_; + mutable mutex mtx_; + /// thread_executor is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a inline executor that runs closures immediately. + * + * \b Throws: Nothing. + */ + shared_state() + : closed_(false) + { + } + /** + * \b Effects: Waits for closures (if any) to complete, then joins and destroys the threads. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c thread_executor destructor. + */ + ~shared_state() + { + // signal to all the worker thread that there will be no more submissions. + close(); + // all the scoped threads will join before destroying + } + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return false; + } + + /** + * \b Effects: close the \c thread_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + lock_guard lk(mtx_); + closed_ = true; + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed(lock_guard& ) + { + return closed_; + } + bool closed() + { + lock_guard lk(mtx_); + return closed(lk); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c thread_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + threads_.reserve(threads_.size() + 1); + thread th(closure); + threads_.push_back(thread_t(boost::move(th))); + } + #endif + void submit(void (*closure)()) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + threads_.reserve(threads_.size() + 1); + thread th(closure); + threads_.push_back(thread_t(boost::move(th))); + } + + template + void submit(BOOST_THREAD_FWD_REF(Closure) closure) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + threads_.reserve(threads_.size() + 1); + thread th(boost::forward(closure)); + threads_.push_back(thread_t(boost::move(th))); + } + + /** + * \b Requires: This must be called from an scheduled task. + * + * \b Effects: reschedule functions until pred() + */ + template + bool reschedule_until(Pred const&) + { + return false; + } + }; public: - /// thread_executor is not copyable. - BOOST_THREAD_NO_COPYABLE(thread_executor) - /** * \b Effects: creates a inline executor that runs closures immediately. * * \b Throws: Nothing. */ thread_executor() - : closed_(false) + : pimpl(make_shared()) { } /** @@ -65,9 +166,16 @@ namespace executors */ ~thread_executor() { - // signal to all the worker thread that there will be no more submissions. - close(); - // all the scoped threads will join before destroying + } + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return pimpl->try_executing_one(); } /** @@ -76,21 +184,15 @@ namespace executors */ void close() { - lock_guard lk(mtx_); - closed_ = true; + pimpl->close(); } /** * \b Returns: whether the pool is closed for submissions. */ - bool closed(lock_guard& ) - { - return closed_; - } bool closed() { - lock_guard lk(mtx_); - return closed(lk); + return pimpl->closed(); } /** @@ -109,30 +211,18 @@ namespace executors template void submit(Closure & closure) { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - threads_.reserve(threads_.size() + 1); - thread th(closure); - threads_.push_back(thread_t(boost::move(th))); + pimpl->submit(closure); } #endif void submit(void (*closure)()) { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - threads_.reserve(threads_.size() + 1); - thread th(closure); - threads_.push_back(thread_t(boost::move(th))); + pimpl->submit(closure); } template void submit(BOOST_THREAD_FWD_REF(Closure) closure) { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - threads_.reserve(threads_.size() + 1); - thread th(boost::forward(closure)); - threads_.push_back(thread_t(boost::move(th))); + pimpl->submit(boost::forward(closure)); } /** @@ -141,12 +231,14 @@ namespace executors * \b Effects: reschedule functions until pred() */ template - bool reschedule_until(Pred const&) + bool reschedule_until(Pred const& p) { - return false; + return pimpl->reschedule_until(p); } - + private: + shared_ptr pimpl; }; + } using executors::thread_executor; } From ff9457e79c0932870948a4b56622135666e837bc Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Fri, 20 Feb 2015 20:47:30 +0100 Subject: [PATCH 02/28] make basic_thread_pool copyable. --- example/executor.cpp | 6 +- example/generic_executor_ref.cpp | 2 +- .../thread/executors/basic_thread_pool.hpp | 455 +++++++++++------- 3 files changed, 296 insertions(+), 167 deletions(-) diff --git a/example/executor.cpp b/example/executor.cpp index f8e9eb14..5e895b6e 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -65,7 +65,7 @@ void submit_some(boost::executor& tp) } -void at_th_entry(boost::basic_thread_pool& ) +void at_th_entry( ) { } @@ -76,6 +76,10 @@ int test_executor_adaptor() { try { + { + boost::basic_thread_pool e1; + boost::basic_thread_pool e2 = e1; + } { boost::executor_adaptor < boost::basic_thread_pool > ea(4); submit_some( ea); diff --git a/example/generic_executor_ref.cpp b/example/generic_executor_ref.cpp index 5797b735..4804e5aa 100644 --- a/example/generic_executor_ref.cpp +++ b/example/generic_executor_ref.cpp @@ -64,7 +64,7 @@ void submit_some(boost::generic_executor_ref tp) } -void at_th_entry(boost::basic_thread_pool& ) +void at_th_entry() { } diff --git a/include/boost/thread/executors/basic_thread_pool.hpp b/include/boost/thread/executors/basic_thread_pool.hpp index 64ba1e90..4c6fba4c 100644 --- a/include/boost/thread/executors/basic_thread_pool.hpp +++ b/include/boost/thread/executors/basic_thread_pool.hpp @@ -30,125 +30,291 @@ namespace executors /// type-erasure to store the works to do typedef executors::work work; private: - /// the kind of stored threads are scoped threads to ensure that the threads are joined. - /// A move aware vector type - typedef scoped_thread<> thread_t; - typedef csbl::vector thread_vector; - /// the thread safe work queue - concurrent::sync_queue work_queue; - /// A move aware vector - thread_vector threads; + struct shared_state { + typedef executors::work work; + /// the kind of stored threads are scoped threads to ensure that the threads are joined. + /// A move aware vector type + typedef scoped_thread<> thread_t; + typedef csbl::vector thread_vector; - public: - /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. - */ - bool try_executing_one() - { - try - { - work task; - if (work_queue.try_pull(task) == queue_op_status::success) - { - task(); - return true; - } - return false; - } - catch (...) - { - std::terminate(); - return false; - } - } - /** - * Effects: schedule one task or yields - * Throws: whatever the current task constructor throws or the task() throws. - */ - void schedule_one_or_yield() - { - if ( ! try_executing_one()) - { - this_thread::yield(); - } - } - private: + /// the thread safe work queue + concurrent::sync_queue work_queue; + /// A move aware vector + thread_vector threads; - /** - * The main loop of the worker threads - */ - void worker_thread() - { - try + public: + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() { - for(;;) + try { work task; - queue_op_status st = work_queue.wait_pull(task); - if (st == queue_op_status::closed) return; - task(); + if (work_queue.try_pull(task) == queue_op_status::success) + { + task(); + return true; + } + return false; + } + catch (...) + { + std::terminate(); + return false; } } - catch (...) + /** + * Effects: schedule one task or yields + * Throws: whatever the current task constructor throws or the task() throws. + */ + void schedule_one_or_yield() { - std::terminate(); - return; + if ( ! try_executing_one()) + { + this_thread::yield(); + } } - } -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - template - void worker_thread1(AtThreadEntry& at_thread_entry) - { - at_thread_entry(*this); - worker_thread(); - } -#endif - void worker_thread2(void(*at_thread_entry)(basic_thread_pool&)) - { - at_thread_entry(*this); - worker_thread(); - } - template - void worker_thread3(BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) - { - at_thread_entry(*this); - worker_thread(); - } - static void do_nothing_at_thread_entry(basic_thread_pool&) {} + private: + + /** + * The main loop of the worker threads + */ + void worker_thread() + { + try + { + for(;;) + { + work task; + queue_op_status st = work_queue.wait_pull(task); + if (st == queue_op_status::closed) return; + task(); + } + } + catch (...) + { + std::terminate(); + return; + } + } + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void worker_thread1(AtThreadEntry& at_thread_entry) + { + at_thread_entry(); + worker_thread(); + } + #endif + void worker_thread2(void(*at_thread_entry)()) + { + at_thread_entry(); + worker_thread(); + } + template + void worker_thread3(BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) + { + at_thread_entry(); + worker_thread(); + } + static void do_nothing_at_thread_entry() {} + + public: + /// basic_thread_pool is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a thread pool that runs closures on \c thread_count threads. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + shared_state(unsigned const thread_count = thread::hardware_concurrency()+1) + { + try + { + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { + #if 1 + thread th (&shared_state::worker_thread, this); + threads.push_back(thread_t(boost::move(th))); + #else + threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile + #endif + } + } + catch (...) + { + close(); + throw; + } + } + /** + * \b Effects: creates a thread pool that runs closures on \c thread_count threads + * and executes the at_thread_entry function at the entry of each created thread. . + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + shared_state( unsigned const thread_count, AtThreadEntry& at_thread_entry) + { + try + { + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { + thread th (&shared_state::worker_thread1, this, at_thread_entry); + threads.push_back(thread_t(boost::move(th))); + //threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile + } + } + catch (...) + { + close(); + throw; + } + } + #endif + shared_state( unsigned const thread_count, void(*at_thread_entry)()) + { + try + { + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { + thread th (&shared_state::worker_thread2, this, at_thread_entry); + threads.push_back(thread_t(boost::move(th))); + //threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile + } + } + catch (...) + { + close(); + throw; + } + } + template + shared_state( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) + { + try + { + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { + thread th (&shared_state::worker_thread3, this, boost::forward(at_thread_entry)); + threads.push_back(thread_t(boost::move(th))); + //threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile + } + } + catch (...) + { + close(); + throw; + } + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c basic_thread_pool destructor. + */ + ~shared_state() + { + // signal to all the worker threads that there will be no more submissions. + close(); + // joins all the threads as the threads were scoped_threads + } + + /** + * \b Effects: join all the threads. + */ + void join() + { + for (unsigned i = 0; i < threads.size(); ++i) + { + threads[i].join(); + } + } + + /** + * \b Effects: close the \c basic_thread_pool for submissions. + * The worker threads will work until there is no more closures to run. + */ + void close() + { + work_queue.close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return work_queue.closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c basic_thread_pool will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + work_queue.push(work(closure)); + } + #endif + void submit(void (*closure)()) + { + work_queue.push(work(closure)); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + work_queue.push(work(boost::forward(closure))); + } + + /** + * \b Requires: This must be called from an scheduled task. + * + * \b Effects: reschedule functions until pred() + */ + template + bool reschedule_until(Pred const& pred) + { + do { + if ( ! try_executing_one()) + { + return false; + } + } while (! pred()); + return true; + } + }; public: - /// basic_thread_pool is not copyable. - BOOST_THREAD_NO_COPYABLE(basic_thread_pool) - /** * \b Effects: creates a thread pool that runs closures on \c thread_count threads. * * \b Throws: Whatever exception is thrown while initializing the needed resources. */ basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) + : pimpl(make_shared(thread_count)) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { -#if 1 - thread th (&basic_thread_pool::worker_thread, this); - threads.push_back(thread_t(boost::move(th))); -#else - threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile -#endif - } - } - catch (...) - { - close(); - throw; - } } + /** * \b Effects: creates a thread pool that runs closures on \c thread_count threads * and executes the at_thread_entry function at the entry of each created thread. . @@ -158,61 +324,21 @@ namespace executors #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template basic_thread_pool( unsigned const thread_count, AtThreadEntry& at_thread_entry) + : pimpl(make_shared(thread_count, at_thread_entry)) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - thread th (&basic_thread_pool::worker_thread1, this, at_thread_entry); - threads.push_back(thread_t(boost::move(th))); - //threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile - } - } - catch (...) - { - close(); - throw; - } } #endif - basic_thread_pool( unsigned const thread_count, void(*at_thread_entry)(basic_thread_pool&)) + + basic_thread_pool( unsigned const thread_count, void(*at_thread_entry)()) + : pimpl(make_shared(thread_count, at_thread_entry)) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - thread th (&basic_thread_pool::worker_thread2, this, at_thread_entry); - threads.push_back(thread_t(boost::move(th))); - //threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile - } - } - catch (...) - { - close(); - throw; - } } template basic_thread_pool( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) + : pimpl(make_shared(thread_count, at_thread_entry)) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - thread th (&basic_thread_pool::worker_thread3, this, boost::forward(at_thread_entry)); - threads.push_back(thread_t(boost::move(th))); - //threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile - } - } - catch (...) - { - close(); - throw; - } } + /** * \b Effects: Destroys the thread pool. * @@ -220,9 +346,16 @@ namespace executors */ ~basic_thread_pool() { - // signal to all the worker threads that there will be no more submissions. - close(); - // joins all the threads as the threads were scoped_threads + } + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return pimpl->try_executing_one(); } /** @@ -230,10 +363,7 @@ namespace executors */ void join() { - for (unsigned i = 0; i < threads.size(); ++i) - { - threads[i].join(); - } + pimpl->join(); } /** @@ -242,7 +372,7 @@ namespace executors */ void close() { - work_queue.close(); + pimpl->close(); } /** @@ -250,7 +380,7 @@ namespace executors */ bool closed() { - return work_queue.closed(); + return pimpl->closed(); } /** @@ -269,18 +399,18 @@ namespace executors template void submit(Closure & closure) { - work_queue.push(work(closure)); + pimpl->submit(closure); } #endif void submit(void (*closure)()) { - work_queue.push(work(closure)); + pimpl->submit(closure); } template void submit(BOOST_THREAD_RV_REF(Closure) closure) { - work_queue.push(work(boost::forward(closure))); + pimpl->submit(boost::forward(closure)); } /** @@ -291,15 +421,10 @@ namespace executors template bool reschedule_until(Pred const& pred) { - do { - if ( ! try_executing_one()) - { - return false; - } - } while (! pred()); - return true; + return pimpl->reschedule_until(pred); } - + private: + shared_ptr pimpl; }; } using executors::basic_thread_pool; From 9492bcd4859ab245c3e26bee9de9b2e08a9bf636 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Fri, 20 Feb 2015 22:26:12 +0100 Subject: [PATCH 03/28] Make serial_executor copyable. Replace generic_executor_ref by generic_executor. --- example/executor.cpp | 5 + example/generic_executor.cpp | 154 +++++++++ example/generic_executor_ref.cpp | 1 + .../thread/executors/generic_executor.hpp | 142 +++++++++ .../thread/executors/serial_executor.hpp | 293 ++++++++++++------ test/Jamfile.v2 | 1 + 6 files changed, 501 insertions(+), 95 deletions(-) create mode 100644 example/generic_executor.cpp create mode 100644 include/boost/thread/executors/generic_executor.hpp diff --git a/example/executor.cpp b/example/executor.cpp index 5e895b6e..d1a7c033 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -113,6 +113,11 @@ int test_executor_adaptor() } #if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool tp; + boost::serial_executor e1(tp); + boost::serial_executor e2 = e1; + } { boost::executor_adaptor < boost::basic_thread_pool > ea1(4); boost::executor_adaptor < boost::serial_executor > ea2(ea1); diff --git a/example/generic_executor.cpp b/example/generic_executor.cpp new file mode 100644 index 00000000..a363a3b2 --- /dev/null +++ b/example/generic_executor.cpp @@ -0,0 +1,154 @@ +// Copyright (C) 2014 Vicente Botet +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#if ! defined BOOST_NO_CXX11_DECLTYPE +#define BOOST_RESULT_OF_USE_DECLTYPE +#endif + +#define BOOST_THREAD_VERSION 4 +#define BOOST_THREAD_PROVIDES_EXECUTORS +//#define BOOST_THREAD_USES_LOG +#define BOOST_THREAD_USES_LOG_THREAD_ID +#define BOOST_THREAD_QUEUE_DEPRECATE_OLD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void p1() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + //boost::this_thread::sleep_for(boost::chrono::milliseconds(200)); +} + +void p2() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + //boost::this_thread::sleep_for(boost::chrono::seconds(10)); +} + +int f1() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::seconds(1)); + return 1; +} +int f2(int i) +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::seconds(2)); + return i + 1; +} + +void submit_some(boost::generic_executor tp) +{ + for (int i = 0; i < 3; ++i) { + tp.submit(&p2); + } + for (int i = 0; i < 3; ++i) { + tp.submit(&p1); + } + +} + +void at_th_entry() +{ + +} + + + +int test_generic_executor() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + { + try + { + { + boost::basic_thread_pool ea(4); + submit_some( ea); + { + boost::future t1 = boost::async(ea, &f1); + boost::future t2 = boost::async(ea, &f1); + // std::cout << BOOST_CONTEXTOF << " t1= " << t1.get() << std::endl; + // std::cout << BOOST_CONTEXTOF << " t2= " << t2.get() << std::endl; + } + submit_some(ea); + { + boost::basic_thread_pool ea3(1); + boost::future t1 = boost::async(ea3, &f1); + boost::future t2 = boost::async(ea3, &f1); + //boost::future t2 = boost::async(ea3, f2, 1); // todo this doesn't compiles yet on C++11 + //boost::future t2 = boost::async(ea3, boost::bind(f2, 1)); // todo this doesn't compiles yet on C++98 + // std::cout << BOOST_CONTEXTOF << " t1= " << t1.get() << std::endl; + // std::cout << BOOST_CONTEXTOF << " t2= " << t2.get() << std::endl; + } + submit_some(ea); + } + // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::loop_executor ea2; + submit_some( ea2); + ea2.run_queued_closures(); + } +#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool ea1(4); + boost::serial_executor ea2(ea1); + submit_some(ea2); + } +#endif + // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::inline_executor ea1; + submit_some(ea1); + } + // std::cout << BOOST_CONTEXTOF << std::endl; + { + //boost::thread_executor ea1; + //submit_some(ea1); + } + // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool ea(4, at_th_entry); + boost::future t1 = boost::async(ea, &f1); + // std::cout << BOOST_CONTEXTOF << " t1= " << t1.get() << std::endl; + } + } + catch (std::exception& ex) + { + std::cout << "ERROR= " << ex.what() << "" << std::endl; + return 1; + } + catch (...) + { + std::cout << " ERROR= exception thrown" << std::endl; + return 2; + } + } + // std::cout << BOOST_CONTEXTOF << std::endl; + return 0; +} + + +int main() +{ + return test_generic_executor(); + + +} diff --git a/example/generic_executor_ref.cpp b/example/generic_executor_ref.cpp index 4804e5aa..31fda264 100644 --- a/example/generic_executor_ref.cpp +++ b/example/generic_executor_ref.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include diff --git a/include/boost/thread/executors/generic_executor.hpp b/include/boost/thread/executors/generic_executor.hpp new file mode 100644 index 00000000..a55791f7 --- /dev/null +++ b/include/boost/thread/executors/generic_executor.hpp @@ -0,0 +1,142 @@ +// Copyright (C) 2014 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// + +#ifndef BOOST_THREAD_EXECUTORS_GENERIC_EXECUTOR_HPP +#define BOOST_THREAD_EXECUTORS_GENERIC_EXECUTOR_HPP + +#include + +#include +#include +#include + +#include + +#include + +namespace boost +{ + namespace executors + { + + class generic_executor + { + shared_ptr ex; + public: + /// type-erasure to store the works to do + typedef executors::work work; + + template + generic_executor(Executor& ex) + //: ex(make_shared >(ex)) // todo check why this doesn't works with C++03 + : ex( new executor_adaptor(ex) ) + { + } + + //generic_executor(generic_executor const& other) noexcept {} + //generic_executor& operator=(generic_executor const& other) noexcept {} + + + /** + * \par Effects + * Close the \c executor for submissions. + * The worker threads will work until there is no more closures to run. + */ + void close() { ex->close(); } + + /** + * \par Returns + * Whether the pool is closed for submissions. + */ + bool closed() { return ex->closed(); } + + void submit(BOOST_THREAD_RV_REF(work) closure) + { + ex->submit(boost::forward(closure)); + } + + /** + * \par Requires + * \c Closure is a model of Callable(void()) and a model of CopyConstructible/MoveConstructible. + * + * \par Effects + * The specified closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the thread pool will call std::terminate, as is the case with threads. + * + * \par Synchronization + * Completion of closure on a particular thread happens before destruction of thread's thread local variables. + * + * \par Throws + * \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + work w ((closure)); + submit(boost::move(w)); + } +#endif + void submit(void (*closure)()) + { + work w ((closure)); + submit(boost::move(w)); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + work w = boost::move(closure); + submit(boost::move(w)); + } + +// size_t num_pending_closures() const +// { +// return ex->num_pending_closures(); +// } + + /** + * \par Effects + * Try to execute one task. + * + * \par Returns + * Whether a task has been executed. + * + * \par Throws + * Whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() { return ex->try_executing_one(); } + + /** + * \par Requires + * This must be called from an scheduled task. + * + * \par Effects + * reschedule functions until pred() + */ + template + bool reschedule_until(Pred const& pred) + { + do { + //schedule_one_or_yield(); + if ( ! try_executing_one()) + { + return false; + } + } while (! pred()); + return true; + } + + }; + } + using executors::generic_executor; +} + +#include + +#endif diff --git a/include/boost/thread/executors/serial_executor.hpp b/include/boost/thread/executors/serial_executor.hpp index 6f426666..23709b53 100644 --- a/include/boost/thread/executors/serial_executor.hpp +++ b/include/boost/thread/executors/serial_executor.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -30,91 +30,182 @@ namespace executors /// type-erasure to store the works to do typedef executors::work work; private: - typedef scoped_thread<> thread_t; - /// the thread safe work queue - concurrent::sync_queue work_queue; - generic_executor_ref ex; - thread_t thr; + struct shared_state { + typedef executors::work work; + typedef scoped_thread<> thread_t; - struct try_executing_one_task { - work& task; - boost::promise &p; - try_executing_one_task(work& task, boost::promise &p) - : task(task), p(p) {} - void operator()() { - try { - task(); - p.set_value(); - } catch (...) - { - p.set_exception(current_exception()); + /// the thread safe work queue + concurrent::sync_queue work_queue; + generic_executor ex; + thread_t thr; + + struct try_executing_one_task { + work& task; + boost::promise &p; + try_executing_one_task(work& task, boost::promise &p) + : task(task), p(p) {} + void operator()() { + try { + task(); + p.set_value(); + } catch (...) + { + p.set_exception(current_exception()); + } } + }; + public: + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex; } + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + work task; + try + { + if (work_queue.try_pull(task) == queue_op_status::success) + { + boost::promise p; + try_executing_one_task tmp(task,p); + ex.submit(tmp); + p.get_future().wait(); + return true; + } + return false; + } + catch (...) + { + std::terminate(); + return false; + } + } + private: + /** + * Effects: schedule one task or yields + * Throws: whatever the current task constructor throws or the task() throws. + */ + void schedule_one_or_yield() + { + if ( ! try_executing_one()) + { + this_thread::yield(); + } + } + + /** + * The main loop of the worker thread + */ + void worker_thread() + { + while (!closed()) + { + schedule_one_or_yield(); + } + while (try_executing_one()) + { + } + } + + public: + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + template + shared_state(Executor& ex) + : ex(ex), thr(&shared_state::worker_thread, this) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c shared_state destructor. + */ + ~shared_state() + { + // signal to the worker thread that there will be no more submissions. + close(); + } + + /** + * \b Effects: close the \c serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + work_queue.close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return work_queue.closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c serial_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + work_queue.push(work(closure)); + } + #endif + void submit(void (*closure)()) + { + work_queue.push(work(closure)); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + work_queue.push(work(boost::forward(closure))); + } + + /** + * \b Requires: This must be called from an scheduled task. + * + * \b Effects: reschedule functions until pred() + */ + template + bool reschedule_until(Pred const& pred) + { + do { + if ( ! try_executing_one()) + { + return false; + } + } while (! pred()); + return true; } }; - public: - /** - * \par Returns - * The underlying executor wrapped on a generic executor reference. - */ - generic_executor_ref& underlying_executor() BOOST_NOEXCEPT { return ex; } - - /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. - */ - bool try_executing_one() - { - work task; - try - { - if (work_queue.try_pull(task) == queue_op_status::success) - { - boost::promise p; - try_executing_one_task tmp(task,p); - ex.submit(tmp); - p.get_future().wait(); - return true; - } - return false; - } - catch (...) - { - std::terminate(); - return false; - } - } - private: - /** - * Effects: schedule one task or yields - * Throws: whatever the current task constructor throws or the task() throws. - */ - void schedule_one_or_yield() - { - if ( ! try_executing_one()) - { - this_thread::yield(); - } - } - - /** - * The main loop of the worker thread - */ - void worker_thread() - { - while (!closed()) - { - schedule_one_or_yield(); - } - while (try_executing_one()) - { - } - } public: - /// serial_executor is not copyable. - BOOST_THREAD_NO_COPYABLE(serial_executor) /** * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. @@ -123,7 +214,7 @@ namespace executors */ template serial_executor(Executor& ex) - : ex(ex), thr(&serial_executor::worker_thread, this) + : pimpl(make_shared(ex)) { } /** @@ -133,8 +224,25 @@ namespace executors */ ~serial_executor() { - // signal to the worker thread that there will be no more submissions. - close(); + } + + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT + { + return pimpl->underlying_executor(); + } + + /** + * Effects: try to execute one task. + * Returns: whether a task has been executed. + * Throws: whatever the current task constructor throws or the task() throws. + */ + bool try_executing_one() + { + return pimpl->try_executing_one(); } /** @@ -143,7 +251,7 @@ namespace executors */ void close() { - work_queue.close(); + pimpl->close(); } /** @@ -151,7 +259,7 @@ namespace executors */ bool closed() { - return work_queue.closed(); + return pimpl->closed(); } /** @@ -170,18 +278,18 @@ namespace executors template void submit(Closure & closure) { - work_queue.push(work(closure)); + pimpl->submit(closure); } #endif void submit(void (*closure)()) { - work_queue.push(work(closure)); + pimpl->submit(closure); } template void submit(BOOST_THREAD_RV_REF(Closure) closure) { - work_queue.push(work(boost::forward(closure))); + pimpl->submit(boost::forward(closure)); } /** @@ -192,15 +300,10 @@ namespace executors template bool reschedule_until(Pred const& pred) { - do { - if ( ! try_executing_one()) - { - return false; - } - } while (! pred()); - return true; + return pimpl->reschedule_until(pred); } - + private: + shared_ptr pimpl; }; } using executors::serial_executor; diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 84401f19..e7ab5a57 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -808,6 +808,7 @@ rule thread-compile ( sources : reqs * : name ) [ thread-run2 ../example/user_scheduler.cpp : ex_user_scheduler ] [ thread-run2 ../example/executor.cpp : ex_executor ] [ thread-run2 ../example/generic_executor_ref.cpp : ex_generic_executor_ref ] + [ thread-run2 ../example/generic_executor.cpp : ex_generic_executor ] [ thread-run2 ../example/serial_executor.cpp : ex_serial_executor ] [ thread-run2 ../example/serial_executor_cont.cpp : ex_serial_executor_cont ] [ thread-run2 ../example/future_when_all.cpp : ex_future_when_all ] From cd31e9c34fe0e651792d4c7a9914c4de11a42357 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 01:00:12 +0100 Subject: [PATCH 04/28] Make executor_adaptor copyable. --- include/boost/thread/executors/executor_adaptor.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/boost/thread/executors/executor_adaptor.hpp b/include/boost/thread/executors/executor_adaptor.hpp index ebe4e347..fe730c41 100644 --- a/include/boost/thread/executors/executor_adaptor.hpp +++ b/include/boost/thread/executors/executor_adaptor.hpp @@ -30,9 +30,6 @@ namespace executors /// type-erasure to store the works to do typedef executor::work work; - /// executor is not copyable. - BOOST_THREAD_NO_COPYABLE(executor_adaptor) - /** * executor_adaptor constructor */ From a4827a31f31169b9414c386e6dd594790d494678 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 11:16:19 +0100 Subject: [PATCH 05/28] Change copyright date. --- include/boost/thread/executors/basic_thread_pool.hpp | 2 +- include/boost/thread/executors/executor.hpp | 2 +- include/boost/thread/executors/executor_adaptor.hpp | 2 +- include/boost/thread/executors/generic_executor.hpp | 2 +- include/boost/thread/executors/inline_executor.hpp | 2 +- include/boost/thread/executors/loop_executor.hpp | 2 +- include/boost/thread/executors/thread_executor.hpp | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/boost/thread/executors/basic_thread_pool.hpp b/include/boost/thread/executors/basic_thread_pool.hpp index 4c6fba4c..e59386e6 100644 --- a/include/boost/thread/executors/basic_thread_pool.hpp +++ b/include/boost/thread/executors/basic_thread_pool.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2013-2014 Vicente J. Botet Escriba +// Copyright (C) 2013-2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/boost/thread/executors/executor.hpp b/include/boost/thread/executors/executor.hpp index 1075bce7..67adce85 100644 --- a/include/boost/thread/executors/executor.hpp +++ b/include/boost/thread/executors/executor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2013,2014 Vicente J. Botet Escriba +// Copyright (C) 2013,2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/boost/thread/executors/executor_adaptor.hpp b/include/boost/thread/executors/executor_adaptor.hpp index fe730c41..55ecc193 100644 --- a/include/boost/thread/executors/executor_adaptor.hpp +++ b/include/boost/thread/executors/executor_adaptor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2013,2014 Vicente J. Botet Escriba +// Copyright (C) 2013,2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/boost/thread/executors/generic_executor.hpp b/include/boost/thread/executors/generic_executor.hpp index a55791f7..95e3a3de 100644 --- a/include/boost/thread/executors/generic_executor.hpp +++ b/include/boost/thread/executors/generic_executor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Vicente J. Botet Escriba +// Copyright (C) 2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/boost/thread/executors/inline_executor.hpp b/include/boost/thread/executors/inline_executor.hpp index c975bd83..9930965c 100644 --- a/include/boost/thread/executors/inline_executor.hpp +++ b/include/boost/thread/executors/inline_executor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Vicente J. Botet Escriba +// Copyright (C) 2014-2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/boost/thread/executors/loop_executor.hpp b/include/boost/thread/executors/loop_executor.hpp index a8ca3758..3cdc96bc 100644 --- a/include/boost/thread/executors/loop_executor.hpp +++ b/include/boost/thread/executors/loop_executor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2013,2014 Vicente J. Botet Escriba +// Copyright (C) 2013-2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) diff --git a/include/boost/thread/executors/thread_executor.hpp b/include/boost/thread/executors/thread_executor.hpp index eb57adfa..d5a6db0d 100644 --- a/include/boost/thread/executors/thread_executor.hpp +++ b/include/boost/thread/executors/thread_executor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Vicente J. Botet Escriba +// Copyright (C) 2014-2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) From 81f67eeb54f4492093ffc1f060d7a9f07115281e Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 11:18:08 +0100 Subject: [PATCH 06/28] Change copyright date. --- include/boost/thread/executors/serial_executor.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/thread/executors/serial_executor.hpp b/include/boost/thread/executors/serial_executor.hpp index 23709b53..ac72cf99 100644 --- a/include/boost/thread/executors/serial_executor.hpp +++ b/include/boost/thread/executors/serial_executor.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2013 Vicente J. Botet Escriba +// Copyright (C) 2013,2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) From ff7e3940844c20cdb0ea5d4c526db4352bb52a4d Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 11:20:42 +0100 Subject: [PATCH 07/28] remove last sleep as now the tasks block the executors shared state lifetime as it is copied. --- example/serial_executor.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/example/serial_executor.cpp b/example/serial_executor.cpp index ad25e822..967c1ea7 100644 --- a/example/serial_executor.cpp +++ b/example/serial_executor.cpp @@ -86,7 +86,6 @@ int test_executor_adaptor() boost::basic_thread_pool ea1(4); boost::serial_executor ea2(ea1); submit_some(ea2); - boost::this_thread::sleep_for(boost::chrono::seconds(10)); } #endif // std::cout << BOOST_CONTEXTOF << std::endl; From 41bde57707a41c4a5ad5b4ba1297707185a54130 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 11:21:20 +0100 Subject: [PATCH 08/28] Make scheduler copyable. --- .../detail/scheduled_executor_base.hpp | 5 +- include/boost/thread/executors/scheduler.hpp | 76 +++++++++++++++---- test/test_scheduler.cpp | 1 - 3 files changed, 64 insertions(+), 18 deletions(-) diff --git a/include/boost/thread/executors/detail/scheduled_executor_base.hpp b/include/boost/thread/executors/detail/scheduled_executor_base.hpp index cad0d22f..bc5b0d2a 100644 --- a/include/boost/thread/executors/detail/scheduled_executor_base.hpp +++ b/include/boost/thread/executors/detail/scheduled_executor_base.hpp @@ -1,5 +1,5 @@ // Copyright (C) 2014 Ian Forbed -// Copyright (C) 2014 Vicente J. Botet Escriba +// Copyright (C) 2014-2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -26,7 +26,8 @@ namespace detail class scheduled_executor_base : public priority_executor_base, Clock > > { public: - typedef boost::function work; + typedef priority_executor_base, Clock > > super; + typedef typename super::work work; //typedef executors::work work; typedef Clock clock; typedef typename clock::duration duration; diff --git a/include/boost/thread/executors/scheduler.hpp b/include/boost/thread/executors/scheduler.hpp index 5796a7d3..4b5fb88b 100644 --- a/include/boost/thread/executors/scheduler.hpp +++ b/include/boost/thread/executors/scheduler.hpp @@ -1,4 +1,4 @@ -// Copyright (C) 2014 Vicente J. Botet Escriba +// Copyright (C) 2014-2015 Vicente J. Botet Escriba // // Distributed under the Boost Software License, Version 1.0. (See accompanying // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -150,8 +150,8 @@ namespace boost } private: - Scheduler& sch; - Executor& ex; + Scheduler sch; + Executor ex; }; //end class /// Wraps a reference to a @c Scheduler providing an @c Executor that @@ -208,7 +208,7 @@ namespace boost } private: - Scheduler& sch; + Scheduler sch; time_point tp; bool is_closed; }; //end class @@ -217,22 +217,71 @@ namespace boost /// It provides factory helper functions such as at/after that convert a @c Scheduler into an @c Executor /// that submit the work at/after a specific time/duration respectively. template - class scheduler : public detail::scheduled_executor_base + class scheduler { - public: - typedef typename detail::scheduled_executor_base::work work; + private: + struct shared_state : public detail::scheduled_executor_base { + typedef detail::scheduled_executor_base super; + typedef typename super::work work; + thread thr; + + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + shared_state() + : super(), + thr(&super::loop, this) {} + + ~shared_state() + { + this->close(); + thr.join(); + } + + }; + + public: + typedef typename shared_state::work work; typedef Clock clock; + typedef typename clock::duration duration; + typedef typename clock::time_point time_point; scheduler() - : super(), - thr(&super::loop, this) {} + : pimpl(make_shared()) + {} ~scheduler() { - this->close(); - thr.join(); } + + /** + * \b Effects: close the \c serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + pimpl->close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return pimpl->closed(); + } + + void submit_at(work w, const time_point& tp) + { + return pimpl->submit_at(w, tp); + } + + void submit_after(work w, const duration& d) + { + return pimpl->submit_after(w, d); + } + template scheduler_executor_wrapper on(Ex& ex) { @@ -250,13 +299,10 @@ namespace boost { return at_executor(*this, tp); } - private: - typedef detail::scheduled_executor_base super; - thread thr; + shared_ptr pimpl; }; - } using executors::resubmitter; using executors::resubmit; diff --git a/test/test_scheduler.cpp b/test/test_scheduler.cpp index 5847ff01..47a23a11 100644 --- a/test/test_scheduler.cpp +++ b/test/test_scheduler.cpp @@ -75,7 +75,6 @@ int main() test_after(5, sch); test_at(5, sch); test_on(5, sch, tp); - boost::this_thread::sleep_for(boost::chrono::seconds(10)); return boost::report_errors(); } From 71bce54c71354408fd6c8b227d8528e2222091bd Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 12:25:29 +0100 Subject: [PATCH 09/28] fix serial_exeutor: reschedule_until and try_executing_one must return false, as a serial executor can not re-enter. --- .../thread/executors/serial_executor.hpp | 86 +++++-------------- 1 file changed, 22 insertions(+), 64 deletions(-) diff --git a/include/boost/thread/executors/serial_executor.hpp b/include/boost/thread/executors/serial_executor.hpp index ac72cf99..c3651d6e 100644 --- a/include/boost/thread/executors/serial_executor.hpp +++ b/include/boost/thread/executors/serial_executor.hpp @@ -62,56 +62,31 @@ namespace executors */ generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex; } - /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. - */ - bool try_executing_one() - { - work task; - try - { - if (work_queue.try_pull(task) == queue_op_status::success) - { - boost::promise p; - try_executing_one_task tmp(task,p); - ex.submit(tmp); - p.get_future().wait(); - return true; - } - return false; - } - catch (...) - { - std::terminate(); - return false; - } - } private: - /** - * Effects: schedule one task or yields - * Throws: whatever the current task constructor throws or the task() throws. - */ - void schedule_one_or_yield() - { - if ( ! try_executing_one()) - { - this_thread::yield(); - } - } /** * The main loop of the worker thread */ void worker_thread() { - while (!closed()) + try { - schedule_one_or_yield(); + for(;;) + { + work task; + queue_op_status st = work_queue.wait_pull(task); + if (st == queue_op_status::closed) return; + + boost::promise p; + try_executing_one_task tmp(task,p); + ex.submit(tmp); + p.get_future().wait(); + } } - while (try_executing_one()) + catch (...) { + std::terminate(); + return; } } @@ -187,22 +162,6 @@ namespace executors work_queue.push(work(boost::forward(closure))); } - /** - * \b Requires: This must be called from an scheduled task. - * - * \b Effects: reschedule functions until pred() - */ - template - bool reschedule_until(Pred const& pred) - { - do { - if ( ! try_executing_one()) - { - return false; - } - } while (! pred()); - return true; - } }; public: @@ -236,13 +195,12 @@ namespace executors } /** - * Effects: try to execute one task. - * Returns: whether a task has been executed. - * Throws: whatever the current task constructor throws or the task() throws. + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. */ bool try_executing_one() { - return pimpl->try_executing_one(); + return false; } /** @@ -293,14 +251,14 @@ namespace executors } /** - * \b Requires: This must be called from an scheduled task. - * - * \b Effects: reschedule functions until pred() + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. */ template bool reschedule_until(Pred const& pred) { - return pimpl->reschedule_until(pred); + //return pimpl->reschedule_until(pred); + return false; } private: shared_ptr pimpl; From 532d215de95259220e20edeb136328e52e610666 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 12:26:40 +0100 Subject: [PATCH 10/28] Make serial_executor_cont copyable, and fix it: reschedule_until and try_executing_one must return false, as a serial executor can not re-enter. --- example/serial_executor_cont.cpp | 1 - .../thread/executors/serial_executor_cont.hpp | 124 +++++++++++++++--- 2 files changed, 106 insertions(+), 19 deletions(-) diff --git a/example/serial_executor_cont.cpp b/example/serial_executor_cont.cpp index 1883b071..89842798 100644 --- a/example/serial_executor_cont.cpp +++ b/example/serial_executor_cont.cpp @@ -86,7 +86,6 @@ int test_executor_adaptor() boost::basic_thread_pool ea1(4); boost::serial_executor_cont ea2(ea1); submit_some(ea2); - boost::this_thread::sleep_for(boost::chrono::seconds(10)); } #endif // std::cout << BOOST_CONTEXTOF << std::endl; diff --git a/include/boost/thread/executors/serial_executor_cont.hpp b/include/boost/thread/executors/serial_executor_cont.hpp index 1c4cc14a..3557a35b 100644 --- a/include/boost/thread/executors/serial_executor_cont.hpp +++ b/include/boost/thread/executors/serial_executor_cont.hpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include @@ -31,7 +31,10 @@ namespace executors typedef executors::work work; private: - generic_executor_ref ex_; + struct shared_state { + typedef executors::work work; + + generic_executor ex_; future fut_; // protected by mtx_ bool closed_; // protected by mtx_ mutex mtx_; @@ -63,10 +66,10 @@ namespace executors * \par Returns * The underlying executor wrapped on a generic executor reference. */ - generic_executor_ref& underlying_executor() BOOST_NOEXCEPT { return ex_; } + generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex_; } - /// serial_executor_cont is not copyable. - BOOST_THREAD_NO_COPYABLE(serial_executor_cont) + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) /** * \b Effects: creates a serial executor that runs closures in fifo order using one the associated executor. @@ -80,7 +83,7 @@ namespace executors * - the continuation can not submit to this serial executor. */ template - serial_executor_cont(Executor& ex) + shared_state(Executor& ex) : ex_(ex), fut_(make_ready_future()), closed_(false) { } @@ -89,7 +92,7 @@ namespace executors * * \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor_cont destructor. */ - ~serial_executor_cont() + ~shared_state() { // signal to the worker thread that there will be no more submissions. close(); @@ -114,17 +117,6 @@ namespace executors return closed(lk); } - /** - * Effects: none. - * Returns: always false. - * Throws: No. - * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. - */ - bool try_executing_one() - { - return false; - } - /** * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. * @@ -159,7 +151,103 @@ namespace executors if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); fut_ = fut_.then(ex_, continuation(work(boost::forward(closure)))); } + }; + public: + /** + * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + template + serial_executor_cont(Executor& ex) + : pimpl(make_shared(ex)) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor destructor. + */ + ~serial_executor_cont() + { + } + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT + { + return pimpl->underlying_executor(); + } + + /** + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. + */ + bool try_executing_one() + { + return false; + } + + /** + * \b Effects: close the \c serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + pimpl->close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return pimpl->closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c serial_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + pimpl->submit(closure); + } +#endif + void submit(void (*closure)()) + { + pimpl->submit(closure); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + pimpl->submit(boost::forward(closure)); + } + + /** + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. + */ + template + bool reschedule_until(Pred const& pred) + { + return false; + } + private: + shared_ptr pimpl; }; } using executors::serial_executor_cont; From eecf8f6c36705da5766b0ea528e3959cdfc91a7b Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 14:29:51 +0100 Subject: [PATCH 11/28] Allow polymorphic executors to be copiable. --- include/boost/thread/executors/executor.hpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/include/boost/thread/executors/executor.hpp b/include/boost/thread/executors/executor.hpp index 67adce85..031af1f7 100644 --- a/include/boost/thread/executors/executor.hpp +++ b/include/boost/thread/executors/executor.hpp @@ -27,8 +27,6 @@ namespace boost /// type-erasure to store the works to do typedef executors::work work; - /// executor is not copyable. - BOOST_THREAD_NO_COPYABLE(executor) executor() {} /** @@ -128,7 +126,6 @@ namespace boost bool reschedule_until(Pred const& pred) { do { - //schedule_one_or_yield(); if ( ! try_executing_one()) { return false; From e44b5309ae67540d1247500bc9e30d076454cf55 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 16:17:11 +0100 Subject: [PATCH 12/28] rename serial_executors to generic_serial_executors and let serial_executor be the template form. --- example/executor.cpp | 8 +- example/generic_serial_executor.cpp | 112 ++++++++ example/generic_serial_executor_cont.cpp | 112 ++++++++ example/serial_executor.cpp | 6 +- example/serial_executor_cont.cpp | 5 +- .../executors/generic_serial_executor.hpp | 271 ++++++++++++++++++ .../generic_serial_executor_cont.hpp | 257 +++++++++++++++++ .../thread/executors/serial_executor.hpp | 10 +- .../thread/executors/serial_executor_cont.hpp | 219 +++++++------- test/Jamfile.v2 | 2 + 10 files changed, 876 insertions(+), 126 deletions(-) create mode 100644 example/generic_serial_executor.cpp create mode 100644 example/generic_serial_executor_cont.cpp create mode 100644 include/boost/thread/executors/generic_serial_executor.hpp create mode 100644 include/boost/thread/executors/generic_serial_executor_cont.hpp diff --git a/example/executor.cpp b/example/executor.cpp index d1a7c033..119904e5 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include #include @@ -115,12 +115,12 @@ int test_executor_adaptor() // std::cout << BOOST_CONTEXTOF << std::endl; { boost::basic_thread_pool tp; - boost::serial_executor e1(tp); - boost::serial_executor e2 = e1; + boost::generic_serial_executor e1(tp); + boost::generic_serial_executor e2 = e1; } { boost::executor_adaptor < boost::basic_thread_pool > ea1(4); - boost::executor_adaptor < boost::serial_executor > ea2(ea1); + boost::executor_adaptor < boost::generic_serial_executor > ea2(ea1); submit_some(ea2); } #endif diff --git a/example/generic_serial_executor.cpp b/example/generic_serial_executor.cpp new file mode 100644 index 00000000..149d5cac --- /dev/null +++ b/example/generic_serial_executor.cpp @@ -0,0 +1,112 @@ +// Copyright (C) 2015 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#if ! defined BOOST_NO_CXX11_DECLTYPE +#define BOOST_RESULT_OF_USE_DECLTYPE +#endif + +#define BOOST_THREAD_VERSION 4 +#define BOOST_THREAD_PROVIDES_EXECUTORS +//#define BOOST_THREAD_USES_LOG +#define BOOST_THREAD_USES_LOG_THREAD_ID +#define BOOST_THREAD_QUEUE_DEPRECATE_OLD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void p1() +{ + std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::milliseconds(30)); + std::cout << BOOST_CONTEXTOF << std::endl; +} + +void p2() +{ + std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); + std::cout << BOOST_CONTEXTOF << std::endl; +} + +int f1() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::seconds(1)); + return 1; +} +int f2(int i) +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::seconds(2)); + return i + 1; +} + +void submit_some(boost::generic_serial_executor& tp) +{ + std::cout << BOOST_CONTEXTOF << std::endl; + for (int i = 0; i < 3; ++i) { + std::cout << BOOST_CONTEXTOF << std::endl; + tp.submit(&p2); + } + for (int i = 0; i < 3; ++i) { + std::cout << BOOST_CONTEXTOF << std::endl; + tp.submit(&p1); + } + std::cout << BOOST_CONTEXTOF << std::endl; + +} + + +void at_th_entry(boost::basic_thread_pool& ) +{ + +} + +int test_executor_adaptor() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + { + try + { + +#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool ea1(4); + boost::generic_serial_executor ea2(ea1); + submit_some(ea2); + } +#endif + // std::cout << BOOST_CONTEXTOF << std::endl; + } + catch (std::exception& ex) + { + std::cout << "ERROR= " << ex.what() << "" << std::endl; + return 1; + } + catch (...) + { + std::cout << " ERROR= exception thrown" << std::endl; + return 2; + } + } + // std::cout << BOOST_CONTEXTOF << std::endl; + return 0; +} + + +int main() +{ + return test_executor_adaptor(); +} diff --git a/example/generic_serial_executor_cont.cpp b/example/generic_serial_executor_cont.cpp new file mode 100644 index 00000000..eb3bbc81 --- /dev/null +++ b/example/generic_serial_executor_cont.cpp @@ -0,0 +1,112 @@ +// Copyright (C) 2015 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#include +#if ! defined BOOST_NO_CXX11_DECLTYPE +#define BOOST_RESULT_OF_USE_DECLTYPE +#endif + +#define BOOST_THREAD_VERSION 4 +#define BOOST_THREAD_PROVIDES_EXECUTORS +//#define BOOST_THREAD_USES_LOG +#define BOOST_THREAD_USES_LOG_THREAD_ID +#define BOOST_THREAD_QUEUE_DEPRECATE_OLD + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void p1() +{ + std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::milliseconds(30)); + std::cout << BOOST_CONTEXTOF << std::endl; +} + +void p2() +{ + std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); + std::cout << BOOST_CONTEXTOF << std::endl; +} + +int f1() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::seconds(1)); + return 1; +} +int f2(int i) +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + boost::this_thread::sleep_for(boost::chrono::seconds(2)); + return i + 1; +} + +void submit_some(boost::generic_serial_executor_cont& tp) +{ + std::cout << BOOST_CONTEXTOF << std::endl; + for (int i = 0; i < 3; ++i) { + std::cout << BOOST_CONTEXTOF << std::endl; + tp.submit(&p2); + } + for (int i = 0; i < 3; ++i) { + std::cout << BOOST_CONTEXTOF << std::endl; + tp.submit(&p1); + } + std::cout << BOOST_CONTEXTOF << std::endl; + +} + + +void at_th_entry(boost::basic_thread_pool& ) +{ + +} + +int test_executor_adaptor() +{ + // std::cout << BOOST_CONTEXTOF << std::endl; + { + try + { + +#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + // std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool ea1(4); + boost::generic_serial_executor_cont ea2(ea1); + submit_some(ea2); + } +#endif + // std::cout << BOOST_CONTEXTOF << std::endl; + } + catch (std::exception& ex) + { + std::cout << "ERROR= " << ex.what() << "" << std::endl; + return 1; + } + catch (...) + { + std::cout << " ERROR= exception thrown" << std::endl; + return 2; + } + } + // std::cout << BOOST_CONTEXTOF << std::endl; + return 0; +} + + +int main() +{ + return test_executor_adaptor(); +} diff --git a/example/serial_executor.cpp b/example/serial_executor.cpp index 967c1ea7..f64ac4b9 100644 --- a/example/serial_executor.cpp +++ b/example/serial_executor.cpp @@ -51,8 +51,8 @@ int f2(int i) boost::this_thread::sleep_for(boost::chrono::seconds(2)); return i + 1; } - -void submit_some(boost::serial_executor& tp) +template +void submit_some(boost::serial_executor& tp) { std::cout << BOOST_CONTEXTOF << std::endl; for (int i = 0; i < 3; ++i) { @@ -84,7 +84,7 @@ int test_executor_adaptor() // std::cout << BOOST_CONTEXTOF << std::endl; { boost::basic_thread_pool ea1(4); - boost::serial_executor ea2(ea1); + boost::serial_executor ea2(ea1); submit_some(ea2); } #endif diff --git a/example/serial_executor_cont.cpp b/example/serial_executor_cont.cpp index 89842798..4c452e11 100644 --- a/example/serial_executor_cont.cpp +++ b/example/serial_executor_cont.cpp @@ -52,7 +52,8 @@ int f2(int i) return i + 1; } -void submit_some(boost::serial_executor_cont& tp) +template < class Executor> +void submit_some(boost::serial_executor_cont& tp) { std::cout << BOOST_CONTEXTOF << std::endl; for (int i = 0; i < 3; ++i) { @@ -84,7 +85,7 @@ int test_executor_adaptor() // std::cout << BOOST_CONTEXTOF << std::endl; { boost::basic_thread_pool ea1(4); - boost::serial_executor_cont ea2(ea1); + boost::serial_executor_cont ea2(ea1); submit_some(ea2); } #endif diff --git a/include/boost/thread/executors/generic_serial_executor.hpp b/include/boost/thread/executors/generic_serial_executor.hpp new file mode 100644 index 00000000..2d7d3cb7 --- /dev/null +++ b/include/boost/thread/executors/generic_serial_executor.hpp @@ -0,0 +1,271 @@ +// Copyright (C) 2013,2015 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// 2013/11 Vicente J. Botet Escriba +// first implementation of a simple serial scheduler. + +#ifndef BOOST_THREAD_GENERIC_SERIAL_EXECUTOR_HPP +#define BOOST_THREAD_GENERIC_SERIAL_EXECUTOR_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost +{ +namespace executors +{ + class generic_serial_executor + { + public: + /// type-erasure to store the works to do + typedef executors::work work; + private: + + struct shared_state { + typedef executors::work work; + typedef scoped_thread<> thread_t; + + /// the thread safe work queue + concurrent::sync_queue work_queue; + generic_executor ex; + thread_t thr; + + struct try_executing_one_task { + work& task; + boost::promise &p; + try_executing_one_task(work& task, boost::promise &p) + : task(task), p(p) {} + void operator()() { + try { + task(); + p.set_value(); + } catch (...) + { + p.set_exception(current_exception()); + } + } + }; + public: + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex; } + + private: + + /** + * The main loop of the worker thread + */ + void worker_thread() + { + try + { + for(;;) + { + work task; + queue_op_status st = work_queue.wait_pull(task); + if (st == queue_op_status::closed) return; + + boost::promise p; + try_executing_one_task tmp(task,p); + ex.submit(tmp); + p.get_future().wait(); + } + } + catch (...) + { + std::terminate(); + return; + } + } + + public: + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + template + shared_state(Executor& ex) + : ex(ex), thr(&shared_state::worker_thread, this) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c shared_state destructor. + */ + ~shared_state() + { + // signal to the worker thread that there will be no more submissions. + close(); + } + + /** + * \b Effects: close the \c generic_serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + work_queue.close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return work_queue.closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c generic_serial_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + work_queue.push(work(closure)); + } + #endif + void submit(void (*closure)()) + { + work_queue.push(work(closure)); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + work_queue.push(work(boost::forward(closure))); + } + + }; + + public: + + /** + * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + template + generic_serial_executor(Executor& ex) + : pimpl(make_shared(ex)) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c generic_serial_executor destructor. + */ + ~generic_serial_executor() + { + } + + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT + { + return pimpl->underlying_executor(); + } + + /** + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. + */ + bool try_executing_one() + { + return false; + } + + /** + * \b Effects: close the \c generic_serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + pimpl->close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return pimpl->closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c generic_serial_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + pimpl->submit(closure); + } +#endif + void submit(void (*closure)()) + { + pimpl->submit(closure); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + pimpl->submit(boost::forward(closure)); + } + + /** + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. + */ + template + bool reschedule_until(Pred const& pred) + { + return false; + } + private: + shared_ptr pimpl; + }; +} +using executors::generic_serial_executor; +} + +#include + +#endif diff --git a/include/boost/thread/executors/generic_serial_executor_cont.hpp b/include/boost/thread/executors/generic_serial_executor_cont.hpp new file mode 100644 index 00000000..d3ebd32b --- /dev/null +++ b/include/boost/thread/executors/generic_serial_executor_cont.hpp @@ -0,0 +1,257 @@ +// Copyright (C) 2015 Vicente J. Botet Escriba +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// 2013/11 Vicente J. Botet Escriba +// first implementation of a simple serial scheduler. + +#ifndef BOOST_THREAD_GENERIC_SERIAL_EXECUTOR_CONT_HPP +#define BOOST_THREAD_GENERIC_SERIAL_EXECUTOR_CONT_HPP + +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace boost +{ +namespace executors +{ + class generic_serial_executor_cont + { + public: + /// type-erasure to store the works to do + typedef executors::work work; + private: + + struct shared_state { + typedef executors::work work; + + generic_executor ex_; + future fut_; // protected by mtx_ + bool closed_; // protected by mtx_ + mutex mtx_; + + struct continuation { + work task; + template + struct result { + typedef void type; + }; + continuation(BOOST_THREAD_RV_REF(work) tsk) + : task(boost::move(tsk)) {} + void operator()(future f) + { + try { + task(); + } catch (...) { + std::terminate(); + } + } + }; + + bool closed(lock_guard&) const + { + return closed_; + } + public: + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex_; } + + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) + + /** + * \b Effects: creates a serial executor that runs closures in fifo order using one the associated executor. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + * + * \b Notes: + * * The lifetime of the associated executor must outlive the serial executor. + * * The current implementation doesn't support submission from synchronous continuation, that is, + * - the executor must execute the continuation asynchronously or + * - the continuation can not submit to this serial executor. + */ + template + shared_state(Executor& ex) + : ex_(ex), fut_(make_ready_future()), closed_(false) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c generic_serial_executor_cont destructor. + */ + ~shared_state() + { + // signal to the worker thread that there will be no more submissions. + close(); + } + + /** + * \b Effects: close the \c generic_serial_executor_cont for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + lock_guard lk(mtx_); + closed_ = true;; + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + lock_guard lk(mtx_); + return closed(lk); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution after the last submitted closure finish. + * If the invoked closure throws an exception the \c generic_serial_executor_cont will call \c std::terminate, as is the case with threads. + * + * \b Throws: \c sync_queue_is_closed if the executor is closed. + * Whatever exception that can be throw while storing the closure. + * + */ + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + fut_ = fut_.then(ex_, continuation(work(closure))); + } +#endif + void submit(void (*closure)()) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + fut_ = fut_.then(ex_, continuation(work(closure))); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + fut_ = fut_.then(ex_, continuation(work(boost::forward(closure)))); + } + }; + public: + /** + * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + template + generic_serial_executor_cont(Executor& ex) + : pimpl(make_shared(ex)) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor destructor. + */ + ~generic_serial_executor_cont() + { + } + + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + generic_executor& underlying_executor() BOOST_NOEXCEPT + { + return pimpl->underlying_executor(); + } + + /** + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. + */ + bool try_executing_one() + { + return false; + } + + /** + * \b Effects: close the \c serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + pimpl->close(); + } + + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return pimpl->closed(); + } + + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. + * If invoked closure throws an exception the \c serial_executor will call \c std::terminate, as is the case with threads. + * + * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. + * + * \b Throws: \c sync_queue_is_closed if the thread pool is closed. + * Whatever exception that can be throw while storing the closure. + */ + +#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + pimpl->submit(closure); + } +#endif + void submit(void (*closure)()) + { + pimpl->submit(closure); + } + + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + pimpl->submit(boost::forward(closure)); + } + + /** + * \b Returns: always false as a serial executor can not re-enter. + * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks. + */ + template + bool reschedule_until(Pred const& pred) + { + return false; + } + private: + shared_ptr pimpl; + }; +} +using executors::generic_serial_executor_cont; +} + +#include + +#endif diff --git a/include/boost/thread/executors/serial_executor.hpp b/include/boost/thread/executors/serial_executor.hpp index c3651d6e..0021cae3 100644 --- a/include/boost/thread/executors/serial_executor.hpp +++ b/include/boost/thread/executors/serial_executor.hpp @@ -24,6 +24,7 @@ namespace boost { namespace executors { + template class serial_executor { public: @@ -37,7 +38,7 @@ namespace executors /// the thread safe work queue concurrent::sync_queue work_queue; - generic_executor ex; + Executor ex; thread_t thr; struct try_executing_one_task { @@ -60,7 +61,7 @@ namespace executors * \par Returns * The underlying executor wrapped on a generic executor reference. */ - generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex; } + Executor& underlying_executor() BOOST_NOEXCEPT { return ex; } private: @@ -99,7 +100,6 @@ namespace executors * * \b Throws: Whatever exception is thrown while initializing the needed resources. */ - template shared_state(Executor& ex) : ex(ex), thr(&shared_state::worker_thread, this) { @@ -171,7 +171,6 @@ namespace executors * * \b Throws: Whatever exception is thrown while initializing the needed resources. */ - template serial_executor(Executor& ex) : pimpl(make_shared(ex)) { @@ -189,7 +188,7 @@ namespace executors * \par Returns * The underlying executor wrapped on a generic executor reference. */ - generic_executor& underlying_executor() BOOST_NOEXCEPT + Executor& underlying_executor() BOOST_NOEXCEPT { return pimpl->underlying_executor(); } @@ -257,7 +256,6 @@ namespace executors template bool reschedule_until(Pred const& pred) { - //return pimpl->reschedule_until(pred); return false; } private: diff --git a/include/boost/thread/executors/serial_executor_cont.hpp b/include/boost/thread/executors/serial_executor_cont.hpp index 3557a35b..f5a561ec 100644 --- a/include/boost/thread/executors/serial_executor_cont.hpp +++ b/include/boost/thread/executors/serial_executor_cont.hpp @@ -12,9 +12,7 @@ #include #include #include -#include #include -#include #include #include @@ -24,6 +22,7 @@ namespace boost { namespace executors { + template class serial_executor_cont { public: @@ -34,123 +33,122 @@ namespace executors struct shared_state { typedef executors::work work; - generic_executor ex_; - future fut_; // protected by mtx_ - bool closed_; // protected by mtx_ - mutex mtx_; + Executor ex_; + future fut_; // protected by mtx_ + bool closed_; // protected by mtx_ + mutex mtx_; - struct continuation { - work task; - template - struct result { - typedef void type; - }; - continuation(BOOST_THREAD_RV_REF(work) tsk) - : task(boost::move(tsk)) {} - void operator()(future f) - { - try { - task(); - } catch (...) { - std::terminate(); + struct continuation { + work task; + template + struct result { + typedef void type; + }; + continuation(BOOST_THREAD_RV_REF(work) tsk) + : task(boost::move(tsk)) {} + void operator()(future f) + { + try { + task(); + } catch (...) { + std::terminate(); + } } + }; + + bool closed(lock_guard&) const + { + return closed_; } - }; + public: + /** + * \par Returns + * The underlying executor wrapped on a generic executor reference. + */ + Executor& underlying_executor() BOOST_NOEXCEPT { return ex_; } - bool closed(lock_guard&) const - { - return closed_; - } - public: - /** - * \par Returns - * The underlying executor wrapped on a generic executor reference. - */ - generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex_; } + /// shared_state is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) - /// shared_state is not copyable. - BOOST_THREAD_NO_COPYABLE(shared_state) + /** + * \b Effects: creates a serial executor that runs closures in fifo order using one the associated executor. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + * + * \b Notes: + * * The lifetime of the associated executor must outlive the serial executor. + * * The current implementation doesn't support submission from synchronous continuation, that is, + * - the executor must execute the continuation asynchronously or + * - the continuation can not submit to this serial executor. + */ + shared_state(Executor& ex) + : ex_(ex), fut_(make_ready_future()), closed_(false) + { + } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor_cont destructor. + */ + ~shared_state() + { + // signal to the worker thread that there will be no more submissions. + close(); + } - /** - * \b Effects: creates a serial executor that runs closures in fifo order using one the associated executor. - * - * \b Throws: Whatever exception is thrown while initializing the needed resources. - * - * \b Notes: - * * The lifetime of the associated executor must outlive the serial executor. - * * The current implementation doesn't support submission from synchronous continuation, that is, - * - the executor must execute the continuation asynchronously or - * - the continuation can not submit to this serial executor. - */ - template - shared_state(Executor& ex) - : ex_(ex), fut_(make_ready_future()), closed_(false) - { - } - /** - * \b Effects: Destroys the thread pool. - * - * \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor_cont destructor. - */ - ~shared_state() - { - // signal to the worker thread that there will be no more submissions. - close(); - } + /** + * \b Effects: close the \c serial_executor_cont for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + lock_guard lk(mtx_); + closed_ = true;; + } - /** - * \b Effects: close the \c serial_executor_cont for submissions. - * The loop will work until there is no more closures to run. - */ - void close() - { - lock_guard lk(mtx_); - closed_ = true;; - } + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + lock_guard lk(mtx_); + return closed(lk); + } - /** - * \b Returns: whether the pool is closed for submissions. - */ - bool closed() - { - lock_guard lk(mtx_); - return closed(lk); - } + /** + * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. + * + * \b Effects: The specified \c closure will be scheduled for execution after the last submitted closure finish. + * If the invoked closure throws an exception the \c serial_executor_cont will call \c std::terminate, as is the case with threads. + * + * \b Throws: \c sync_queue_is_closed if the executor is closed. + * Whatever exception that can be throw while storing the closure. + * + */ - /** - * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. - * - * \b Effects: The specified \c closure will be scheduled for execution after the last submitted closure finish. - * If the invoked closure throws an exception the \c serial_executor_cont will call \c std::terminate, as is the case with threads. - * - * \b Throws: \c sync_queue_is_closed if the executor is closed. - * Whatever exception that can be throw while storing the closure. - * - */ + #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) + template + void submit(Closure & closure) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + fut_ = fut_.then(ex_, continuation(work(closure))); + } + #endif + void submit(void (*closure)()) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + fut_ = fut_.then(ex_, continuation(work(closure))); + } -#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - template - void submit(Closure & closure) - { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - fut_ = fut_.then(ex_, continuation(work(closure))); - } -#endif - void submit(void (*closure)()) - { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - fut_ = fut_.then(ex_, continuation(work(closure))); - } - - template - void submit(BOOST_THREAD_RV_REF(Closure) closure) - { - lock_guard lk(mtx_); - if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); - fut_ = fut_.then(ex_, continuation(work(boost::forward(closure)))); - } + template + void submit(BOOST_THREAD_RV_REF(Closure) closure) + { + lock_guard lk(mtx_); + if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() ); + fut_ = fut_.then(ex_, continuation(work(boost::forward(closure)))); + } }; public: /** @@ -158,7 +156,6 @@ namespace executors * * \b Throws: Whatever exception is thrown while initializing the needed resources. */ - template serial_executor_cont(Executor& ex) : pimpl(make_shared(ex)) { @@ -176,7 +173,7 @@ namespace executors * \par Returns * The underlying executor wrapped on a generic executor reference. */ - generic_executor& underlying_executor() BOOST_NOEXCEPT + Executor& underlying_executor() BOOST_NOEXCEPT { return pimpl->underlying_executor(); } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e7ab5a57..065f3a8c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -809,7 +809,9 @@ rule thread-compile ( sources : reqs * : name ) [ thread-run2 ../example/executor.cpp : ex_executor ] [ thread-run2 ../example/generic_executor_ref.cpp : ex_generic_executor_ref ] [ thread-run2 ../example/generic_executor.cpp : ex_generic_executor ] + [ thread-run2 ../example/generic_serial_executor.cpp : ex_generic_serial_executor ] [ thread-run2 ../example/serial_executor.cpp : ex_serial_executor ] + [ thread-run2 ../example/generic_serial_executor_cont.cpp : ex_generic_serial_executor_cont ] [ thread-run2 ../example/serial_executor_cont.cpp : ex_serial_executor_cont ] [ thread-run2 ../example/future_when_all.cpp : ex_future_when_all ] [ thread-run2 ../example/parallel_accumulate.cpp : ex_parallel_accumulate ] From 9481562b5cd6723f6accb88a0af40cf4caece8be Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 21 Feb 2015 16:51:55 +0100 Subject: [PATCH 13/28] update executors doc removing the move-only constraint. --- doc/async_executors.qbk | 244 +++++++++++++++++++++++++++++++++------- 1 file changed, 206 insertions(+), 38 deletions(-) diff --git a/doc/async_executors.qbk b/doc/async_executors.qbk index ea6ef7bc..e5586793 100644 --- a/doc/async_executors.qbk +++ b/doc/async_executors.qbk @@ -1,5 +1,5 @@ [/ - / Copyright (c) 2014 Vicente J. Botet Escriba + / Copyright (c) 2014-2015 Vicente J. Botet Escriba / / Distributed under the Boost Software License, Version 1.0. (See accompanying / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -516,9 +516,6 @@ Executor abstract base class. public: typedef boost::work work; - executor(executor const&) = delete; - executor& operator=(executor const&) = delete; - executor(); virtual ~executor() {}; @@ -583,9 +580,6 @@ Polymorphic adaptor of a model of Executor to an executor. public: typedef executor::work work; - executor_adaptor(executor_adaptor const&) = delete; - executor_adaptor& operator=(executor_adaptor const&) = delete; - template executor_adaptor(Args&& ... args); @@ -651,20 +645,24 @@ Polymorphic adaptor of a model of Executor to an executor. [/////////////////////////////////] [section:generic_executor_ref Class `generic_executor_ref`] -Executor abstract base class. +Type erased executor class. #include namespace boost { class generic_executor_ref { public: - generic_executor_ref(generic_executor_ref const&); - generic_executor_ref& operator=(generic_executor_ref const&); template generic_executor_ref(Executor& ex); - generic_executor_ref() {}; + generic_executor_ref() = delete; + generic_executor_ref(generic_executor_ref const&) = default; + generic_executor_ref(generic_executor_ref &&) = default; + generic_executor_ref& operator=(generic_executor_ref const&) = default; + generic_executor_ref& operator=(generic_executor_ref &&) = default; + executor& underlying_executor() noexcept; + void close() = 0; bool closed() = 0; @@ -679,6 +677,44 @@ Executor abstract base class. [endsect] + +[/////////////////////////////////] +[section:generic_executor Class `generic_executor`] + +Type erased executor class. + + #include + namespace boost { + class generic_executor + { + public: + + template + generic_executor(Executor& ex); + generic_executor() = delete; + + generic_executor(generic_executor const&) = default; + generic_executor(generic_executor &&) = default; + generic_executor& operator=(generic_executor const&) = default; + generic_executor& operator=(generic_executor &&) = default; + + executor& underlying_executor() noexcept; + + void close() = 0; + bool closed() = 0; + + template + void submit(Closure&& closure); + + virtual bool try_executing_one() = 0; + template + bool reschedule_until(Pred const& pred); + }; + } + +[endsect] + + [//////////////////////////////////////////////////////////] [section: scheduler Template Class `scheduler `] @@ -693,10 +729,7 @@ Scheduler providing time related functions. Note that `scheduler` is not an Exec public: using work = boost::function ; using clock = Clock; - - scheduler(scheduler const&) = delete; - scheduler& operator=(scheduler const&) = delete; - + scheduler(); ~scheduler(); @@ -743,7 +776,7 @@ Scheduler providing time related functions. Note that `scheduler` is not an Exec [[Effects:] [Destroys the scheduler.]] -[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]] +[[Synchronization:] [The completion of all the closures happen before the completion of the destructor.]] ] @@ -1349,10 +1382,6 @@ A serial executor ensuring that there are no two work units that executes concur class serial_executor { public: - serial_executor(serial_executor const&) = delete; - serial_executor& operator=(serial_executor const&) = delete; - - template serial_executor(Executor& ex); Executor& underlying_executor() noexcept; @@ -1403,7 +1432,7 @@ A serial executor ensuring that there are no two work units that executes concur [/////////////////////////////////////] [section:underlying_executor Function member `underlying_executor()`] - generic_executor_ref& underlying_executor() noexcept; + Executor& underlying_executor() noexcept; [variablelist @@ -1428,13 +1457,10 @@ A serial executor ensuring that there are no two work units that executes concur class generic_serial_executor { public: - generic_serial_executor(generic_serial_executor const&) = delete; - generic_serial_executor& operator=(generic_serial_executor const&) = delete; - template generic_serial_executor(Executor& ex); - generic_executor_ref& underlying_executor() noexcept; + generic_executor& underlying_executor() noexcept; void close(); bool closed(); @@ -1443,6 +1469,7 @@ A serial executor ensuring that there are no two work units that executes concur void submit(Closure&& closure); bool try_executing_one(); + template bool reschedule_until(Pred const& pred); @@ -1466,7 +1493,7 @@ A serial executor ensuring that there are no two work units that executes concur [endsect] [/////////////////////////////////////] -[section:destructor Destructor `~serial_executor()`] +[section:destructor Destructor `~generic_serial_executor()`] ~generic_serial_executor(); @@ -1478,6 +1505,80 @@ A serial executor ensuring that there are no two work units that executes concur ] +[endsect] +[/////////////////////////////////////] +[section:underlying_executor Function member `underlying_executor()`] + + generic_executor& underlying_executor() noexcept; + +[variablelist + +[[Return:] [The underlying executor instance. ]] + +] + + +[endsect] + +[endsect] + +[//////////////////////////////////////////////////////////] +[section:serial_executor_cont Template Class `serial_executor_cont`] + +A serial executor ensuring that there are no two work units that executes concurrently. + + #include + namespace boost { + template + class serial_executor_cont + { + public: + serial_executor_cont(Executor& ex); + + Executor& underlying_executor() noexcept; + + void close(); + bool closed(); + + template + void submit(Closure&& closure); + + bool try_executing_one(); + template + bool reschedule_until(Pred const& pred); + + }; + } + +[/////////////////////////////////////] +[section:constructor Constructor `serial_executor_cont(Executor&)`] + + template + serial_executor_cont(Executor& ex); + +[variablelist + +[[Effects:] [Constructs a serial_executor_cont. ]] + +[[Throws:] [Nothing. ]] + +] + + +[endsect] +[/////////////////////////////////////] +[section:destructor Destructor `~serial_executor_cont()`] + + ~serial_executor_cont(); + +[variablelist + +[[Effects:] [Destroys the serial_executor.]] + +[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]] + +] + [endsect] [/////////////////////////////////////] [section:underlying_executor Function member `underlying_executor()`] @@ -1488,6 +1589,8 @@ A serial executor ensuring that there are no two work units that executes concur [[Return:] [The underlying executor instance. ]] +[[Throws:] [Nothing.]] + ] @@ -1495,6 +1598,82 @@ A serial executor ensuring that there are no two work units that executes concur [endsect] +[//////////////////////////////////////////////////////////] +[section:generic_serial_cont_executor Class `generic_serial_cont_executor`] + +A serial executor ensuring that there are no two work units that executes concurrently. + + #include + namespace boost { + class generic_serial_cont_executor + { + public: + template + generic_serial_cont_executor(Executor& ex); + + generic_executor& underlying_executor() noexcept; + + void close(); + bool closed(); + + template + void submit(Closure&& closure); + + bool try_executing_one(); + + template + bool reschedule_until(Pred const& pred); + + }; + } + +[/////////////////////////////////////] +[section:constructor Constructor `generic_serial_cont_executor(Executor&)`] + + template + generic_serial_cont_executor(Executor& ex); + +[variablelist + +[[Effects:] [Constructs a generic_serial_cont_executor. ]] + +[[Throws:] [Nothing. ]] + +] + + +[endsect] +[/////////////////////////////////////] +[section:destructor Destructor `~generic_serial_cont_executor()`] + + ~generic_serial_cont_executor(); + +[variablelist + +[[Effects:] [Destroys the generic_serial_cont_executor.]] + +[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]] + +] + +[endsect] +[/////////////////////////////////////] +[section:underlying_executor Function member `underlying_executor()`] + + generic_executor& underlying_executor() noexcept; + +[variablelist + +[[Return:] [The underlying executor instance. ]] + +] + + +[endsect] + +[endsect] + + [//////////////////////////////////////////////////////////] [section:inline_executor Class `inline_executor`] @@ -1506,10 +1685,8 @@ A serial executor ensuring that there are no two work units that executes concur class inline_executor { public: - inline_executor(inline_executor const&) = delete; - inline_executor& operator=(inline_executor const&) = delete; - inline_executor(); + ~inline_executor(); void close(); bool closed(); @@ -1570,9 +1747,6 @@ A thread pool with up to a fixed number of threads. class basic_thread_pool { public: - - basic_thread_pool(basic_thread_pool const&) = delete; - basic_thread_pool& operator=(basic_thread_pool const&) = delete; basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()); template @@ -1633,9 +1807,6 @@ A thread_executor with a threads for each task. { public: - thread_executor(thread_executor const&) = delete; - thread_executor& operator=(thread_executor const&) = delete; - thread_executor(); template basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry); @@ -1690,9 +1861,6 @@ A user scheduled executor. class loop_executor { public: - - loop_executor(loop_executor const&) = delete; - loop_executor& operator=(loop_executor const&) = delete; loop_executor(); ~loop_executor(); From fdd1db970d6824329530ace46b94243d061b1675 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 09:06:57 +0100 Subject: [PATCH 14/28] cleanup work and store by value scheduler. --- include/boost/thread/executors/scheduler.hpp | 2 +- include/boost/thread/executors/work.hpp | 23 +------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/include/boost/thread/executors/scheduler.hpp b/include/boost/thread/executors/scheduler.hpp index 3d87ae91..19e88df7 100644 --- a/include/boost/thread/executors/scheduler.hpp +++ b/include/boost/thread/executors/scheduler.hpp @@ -102,7 +102,7 @@ namespace boost } private: - Scheduler& sch; + Scheduler sch; Executor& ex; typename clock::time_point tp; bool is_closed; diff --git a/include/boost/thread/executors/work.hpp b/include/boost/thread/executors/work.hpp index f7bc4fc0..e4a26d48 100644 --- a/include/boost/thread/executors/work.hpp +++ b/include/boost/thread/executors/work.hpp @@ -8,15 +8,6 @@ #define BOOST_THREAD_EXECUTORS_WORK_HPP #include - -#if ! defined BOOST_THREAD_EXECUTORS_WORK_ACCEPTS_MOVABLE \ - && ! defined BOOST_THREAD_EXECUTORS_WORK_DONT_ACCEPT_MOVABLE -#define BOOST_THREAD_EXECUTORS_WORK_ACCEPTS_MOVABLE -//#define BOOST_THREAD_EXECUTORS_WORK_DONT_ACCEPT_MOVABLE -#endif - -#if defined BOOST_THREAD_EXECUTORS_WORK_ACCEPTS_MOVABLE - #include #include @@ -30,22 +21,10 @@ namespace boost //typedef detail::nullary_function work_pq; typedef csbl::function work_pq; #else - typedef boost::function work_pq; + typedef csbl::function work_pq; #endif } } // namespace boost -#else -#include - -namespace boost -{ - namespace executors - { - typedef csbl::function work; - } -} // namespace boost - -#endif #endif // BOOST_THREAD_EXECUTORS_WORK_HPP From c192777aefdc05dc381b7556afb6a6d1b877ad8b Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 09:53:09 +0100 Subject: [PATCH 15/28] fix missing include in caller_context.hpp and let the possibility to dump function at compile time. --- include/boost/thread/caller_context.hpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/boost/thread/caller_context.hpp b/include/boost/thread/caller_context.hpp index 1e341686..d035baf6 100644 --- a/include/boost/thread/caller_context.hpp +++ b/include/boost/thread/caller_context.hpp @@ -12,6 +12,7 @@ #include #endif #include +#include #include #include @@ -43,9 +44,11 @@ namespace boost #endif { io::ios_flags_saver ifs(os); - os << ctx.filename << "[" + os << std::setw(50) << ctx.filename << "[" << std::setw(4) << std::right << std::dec<< ctx.lineno << "] "; +#if defined BOOST_THREAD_USES_LOG_CURRENT_FUNCTION os << ctx.func << " " ; +#endif } return os; } From 05d6eca09d571b7279ad30100521cd23280a2eac Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 10:38:15 +0100 Subject: [PATCH 16/28] Run some failing tests that work when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined. --- .../mutual_exclusion/sync_deque/single_thread_pass.cpp | 9 ++++----- .../mutual_exclusion/sync_queue/single_thread_pass.cpp | 8 ++++---- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/test/sync/mutual_exclusion/sync_deque/single_thread_pass.cpp b/test/sync/mutual_exclusion/sync_deque/single_thread_pass.cpp index aeb50b97..cec3e217 100644 --- a/test/sync/mutual_exclusion/sync_deque/single_thread_pass.cpp +++ b/test/sync/mutual_exclusion/sync_deque/single_thread_pass.cpp @@ -76,7 +76,7 @@ int main() BOOST_TEST(! q.closed()); } -#if 0 +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES { // empty queue push rvalue/non_copyable succeeds boost::sync_deque q; @@ -137,11 +137,11 @@ int main() BOOST_TEST(! q.closed()); } -#if 0 +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES { // empty queue try_push rvalue/non-copyable succeeds boost::sync_deque q; - BOOST_TEST(boost::queue_op_status::success ==q.try_push_back(non_copyable())); + BOOST_TEST(boost::queue_op_status::success ==q.try_push_back(non_copyable(1))); BOOST_TEST(! q.empty()); BOOST_TEST(! q.full()); BOOST_TEST_EQ(q.size(), 1u); @@ -179,8 +179,7 @@ int main() BOOST_TEST(! q.closed()); } - -#if 0 +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES { // empty queue nonblocking_push_back rvalue/non-copyable succeeds boost::sync_deque q; diff --git a/test/sync/mutual_exclusion/sync_queue/single_thread_pass.cpp b/test/sync/mutual_exclusion/sync_queue/single_thread_pass.cpp index 82a1ab0d..39d76be3 100644 --- a/test/sync/mutual_exclusion/sync_queue/single_thread_pass.cpp +++ b/test/sync/mutual_exclusion/sync_queue/single_thread_pass.cpp @@ -47,7 +47,7 @@ int main() BOOST_TEST(! q.closed()); } -#if 0 +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES { // empty queue push rvalue/non_copyable succeeds boost::sync_queue q; @@ -108,11 +108,11 @@ int main() BOOST_TEST(! q.closed()); } -#if 0 +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES { // empty queue try_push rvalue/non-copyable succeeds boost::sync_queue q; - BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable())); + BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable(1))); BOOST_TEST(! q.empty()); BOOST_TEST(! q.full()); BOOST_TEST_EQ(q.size(), 1u); @@ -150,7 +150,7 @@ int main() BOOST_TEST(! q.closed()); } -#if 0 +#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES { // empty queue nonblocking_push rvalue/non-copyable succeeds boost::sync_queue q; From 65c4693c873533d1dc852dedcd4559920ff20da7 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 10:41:20 +0100 Subject: [PATCH 17/28] Add missing push(movable&&) and Run some failing tests that work when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined. --- .../concurrent_queues/sync_timed_queue.hpp | 21 +++++++++ .../sync_pq/pq_single_thread_pass.cpp | 44 +++++++++---------- 2 files changed, 41 insertions(+), 24 deletions(-) diff --git a/include/boost/thread/concurrent_queues/sync_timed_queue.hpp b/include/boost/thread/concurrent_queues/sync_timed_queue.hpp index 78712e58..8f24f43e 100644 --- a/include/boost/thread/concurrent_queues/sync_timed_queue.hpp +++ b/include/boost/thread/concurrent_queues/sync_timed_queue.hpp @@ -106,6 +106,11 @@ namespace detail template void push(const T& elem, chrono::duration const& dura); + template + void push(BOOST_THREAD_RV_REF(T) elem, chrono::time_point const& tp); + template + void push(BOOST_THREAD_RV_REF(T) elem, chrono::duration const& dura); + template queue_op_status try_push(const T& elem, chrono::time_point const& tp); template @@ -158,6 +163,22 @@ namespace detail push(elem, clock::now() + dura); } + template + template + void sync_timed_queue::push(BOOST_THREAD_RV_REF(T) elem, chrono::time_point const& tp) + { + super::push(stype(boost::move(elem),tp)); + } + + template + template + void sync_timed_queue::push(BOOST_THREAD_RV_REF(T) elem, chrono::duration const& dura) + { + push(boost::move(elem), clock::now() + dura); + } + + + template template queue_op_status sync_timed_queue::try_push(const T& elem, chrono::time_point const& tp) diff --git a/test/sync/mutual_exclusion/sync_pq/pq_single_thread_pass.cpp b/test/sync/mutual_exclusion/sync_pq/pq_single_thread_pass.cpp index 7d6af4a1..70cb21bd 100644 --- a/test/sync/mutual_exclusion/sync_pq/pq_single_thread_pass.cpp +++ b/test/sync/mutual_exclusion/sync_pq/pq_single_thread_pass.cpp @@ -149,17 +149,15 @@ int main() //test_pull_until_when_not_empty(); #if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES -#if 0 { // empty queue try_push rvalue/non-copyable succeeds boost::concurrent::sync_priority_queue q; - BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable())); + BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable(1))); BOOST_TEST(! q.empty()); BOOST_TEST(! q.full()); BOOST_TEST_EQ(q.size(), 1u); BOOST_TEST(! q.closed()); } -#endif { //fixme // empty queue try_push rvalue/non-copyable succeeds @@ -183,17 +181,16 @@ int main() BOOST_TEST_EQ(q.size(), 1u); BOOST_TEST(! q.closed()); } -// { -// // empty queue try_push rvalue succeeds -// boost::concurrent::sync_priority_queue q; -// BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(1)); -// BOOST_TEST(! q.empty()); -// BOOST_TEST(! q.full()); -// BOOST_TEST_EQ(q.size(), 1u); -// BOOST_TEST(! q.closed()); -// } -#if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES #if 0 + { + // empty queue try_push rvalue succeeds + boost::concurrent::sync_priority_queue q; + BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(1)); + BOOST_TEST(! q.empty()); + BOOST_TEST(! q.full()); + BOOST_TEST_EQ(q.size(), 1u); + BOOST_TEST(! q.closed()); + } { // empty queue nonblocking_push rvalue/non-copyable succeeds boost::concurrent::sync_priority_queue q; @@ -203,17 +200,16 @@ int main() BOOST_TEST_EQ(q.size(), 1u); BOOST_TEST(! q.closed()); } -#endif -// { -// // empty queue nonblocking_push rvalue/non-copyable succeeds -// boost::concurrent::sync_priority_queue q; -// non_copyable nc(1); -// BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(boost::move(nc))); -// BOOST_TEST(! q.empty()); -// BOOST_TEST(! q.full()); -// BOOST_TEST_EQ(q.size(), 1u); -// BOOST_TEST(! q.closed()); -// } + { + // empty queue nonblocking_push rvalue/non-copyable succeeds + boost::concurrent::sync_priority_queue q; + non_copyable nc(1); + BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(boost::move(nc))); + BOOST_TEST(! q.empty()); + BOOST_TEST(! q.full()); + BOOST_TEST_EQ(q.size(), 1u); + BOOST_TEST(! q.closed()); + } #endif { From 264ed4c308f5e589beea2e335c7f51b3bf85c782 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 10:44:44 +0100 Subject: [PATCH 18/28] move the work parameter. --- .../thread/executors/detail/scheduled_executor_base.hpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/boost/thread/executors/detail/scheduled_executor_base.hpp b/include/boost/thread/executors/detail/scheduled_executor_base.hpp index 813121d5..ec0038f7 100644 --- a/include/boost/thread/executors/detail/scheduled_executor_base.hpp +++ b/include/boost/thread/executors/detail/scheduled_executor_base.hpp @@ -16,6 +16,8 @@ #include #include +#include + namespace boost { namespace executors @@ -45,12 +47,12 @@ namespace detail void submit_at(work w, const time_point& tp) { - this->_workq.push(w, tp); + this->_workq.push(boost::move(w), tp); } void submit_after(work w, const duration& dura) { - this->_workq.push(w, dura+clock::now()); + this->_workq.push(boost::move(w), dura+clock::now()); } }; //end class @@ -58,4 +60,7 @@ namespace detail } //end detail namespace } //end executors namespace } //end boost namespace + +#include + #endif From df14c8ac18acd63623b15b09a2abe78079b7ebd2 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 16:29:00 +0100 Subject: [PATCH 19/28] fix the move(w) on scheduler and store copies of the Executors. --- include/boost/thread/executors/scheduler.hpp | 10 +++++----- include/boost/thread/executors/work.hpp | 4 ++-- test/test_scheduler.cpp | 2 -- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/include/boost/thread/executors/scheduler.hpp b/include/boost/thread/executors/scheduler.hpp index 19e88df7..12a51146 100644 --- a/include/boost/thread/executors/scheduler.hpp +++ b/include/boost/thread/executors/scheduler.hpp @@ -38,7 +38,7 @@ namespace boost } private: - Executor& ex; + Executor ex; Function funct; }; @@ -103,7 +103,7 @@ namespace boost private: Scheduler sch; - Executor& ex; + Executor ex; typename clock::time_point tp; bool is_closed; }; @@ -153,7 +153,7 @@ namespace boost private: Scheduler sch; - Executor &ex; + Executor ex; }; //end class /// Wraps a reference to a @c Scheduler providing an @c Executor that @@ -276,12 +276,12 @@ namespace boost void submit_at(work w, const time_point& tp) { - return pimpl->submit_at(w, tp); + return pimpl->submit_at(boost::move(w), tp); } void submit_after(work w, const duration& d) { - return pimpl->submit_after(w, d); + return pimpl->submit_after(boost::move(w), d); } template diff --git a/include/boost/thread/executors/work.hpp b/include/boost/thread/executors/work.hpp index e4a26d48..bdaf7651 100644 --- a/include/boost/thread/executors/work.hpp +++ b/include/boost/thread/executors/work.hpp @@ -18,8 +18,8 @@ namespace boost typedef detail::nullary_function work; #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES - //typedef detail::nullary_function work_pq; - typedef csbl::function work_pq; + typedef detail::nullary_function work_pq; + //typedef csbl::function work_pq; #else typedef csbl::function work_pq; #endif diff --git a/test/test_scheduler.cpp b/test/test_scheduler.cpp index 3fd1ca1f..c72715b8 100644 --- a/test/test_scheduler.cpp +++ b/test/test_scheduler.cpp @@ -27,7 +27,6 @@ typedef boost::executors::basic_thread_pool thread_pool; void fn(int x) { - //std::cout << "[" << __LINE__ << "] " << steady_clock::now() << std::endl; std::cout << x << std::endl; } @@ -75,7 +74,6 @@ int main() test_after(5, sch); test_at(5, sch); test_on(5, sch, tp); - boost::this_thread::sleep_for(boost::chrono::seconds(10)); std::cout << "[" << __LINE__ << "] " << std::endl; return boost::report_errors(); From b2b8684d0c89d1b7cc28b19ae72124e866274e87 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 17:04:17 +0100 Subject: [PATCH 20/28] make scheduled_thread_pool design closer to basic_thread_pool. --- .../executors/scheduled_thread_pool.hpp | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/include/boost/thread/executors/scheduled_thread_pool.hpp b/include/boost/thread/executors/scheduled_thread_pool.hpp index 408013b2..103fe6c3 100644 --- a/include/boost/thread/executors/scheduled_thread_pool.hpp +++ b/include/boost/thread/executors/scheduled_thread_pool.hpp @@ -9,6 +9,10 @@ #define BOOST_THREAD_EXECUTORS_SCHEDULED_THREAD_POOL_HPP #include +#include +#include +#include +#include namespace boost { @@ -18,21 +22,38 @@ namespace executors class scheduled_thread_pool : public detail::scheduled_executor_base<> { private: - thread_group _workers; + typedef scoped_thread<> thread_t; + typedef csbl::vector thread_vector; + thread_vector threads; + public: - scheduled_thread_pool(size_t num_threads) : super() + scheduled_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) : super() { - for(size_t i = 0; i < num_threads; i++) + + try { - _workers.create_thread(bind(&super::loop, this)); + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { +#if 1 + thread th (&super::loop, this); + threads.push_back(thread_t(boost::move(th))); +#else + threads.push_back(thread_t(&super::loop, this)); // do not compile +#endif + } + } + catch (...) + { + close(); + throw; } } ~scheduled_thread_pool() { this->close(); - _workers.join_all(); } private: From a53f31fb9965e469dc22ec4272a49490acdd259c Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 17:07:57 +0100 Subject: [PATCH 21/28] Store the Executor in scheduling_adaptor. This class must be finished as it doesn't make use of the executor :(. --- include/boost/thread/executors/scheduling_adaptor.hpp | 2 +- test/test_scheduling_adaptor.cpp | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/include/boost/thread/executors/scheduling_adaptor.hpp b/include/boost/thread/executors/scheduling_adaptor.hpp index ac0a0acb..d345558a 100644 --- a/include/boost/thread/executors/scheduling_adaptor.hpp +++ b/include/boost/thread/executors/scheduling_adaptor.hpp @@ -19,7 +19,7 @@ namespace executors class scheduling_adpator : public detail::scheduled_executor_base<> { private: - Executor& _exec; + Executor _exec; thread _scheduler; public: diff --git a/test/test_scheduling_adaptor.cpp b/test/test_scheduling_adaptor.cpp index ba43551f..c994e38c 100644 --- a/test/test_scheduling_adaptor.cpp +++ b/test/test_scheduling_adaptor.cpp @@ -28,7 +28,6 @@ typedef boost::executors::basic_thread_pool thread_pool; void fn(int x) { - //std::cout << "[" << __LINE__ << "] " << steady_clock::now() << std::endl; std::cout << x << std::endl; } @@ -41,14 +40,10 @@ void test_timing(const int n) sa.submit_after(boost::bind(fn,i),seconds(i)); sa.submit_after(boost::bind(fn,i), milliseconds(i*100)); } - boost::this_thread::sleep_for(boost::chrono::seconds(10)); } int main() { - steady_clock::time_point start = steady_clock::now(); test_timing(5); - steady_clock::duration diff = steady_clock::now() - start; - BOOST_TEST(diff > seconds(5)); return boost::report_errors(); } From 7dbd04197d379d29fd93316e04dcf7877b178f20 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sat, 28 Feb 2015 19:01:45 +0100 Subject: [PATCH 22/28] Make scheduled_thread_pool copyable. --- .../executors/scheduled_thread_pool.hpp | 112 ++++++++++++++---- test/test_scheduled_tp.cpp | 13 +- 2 files changed, 95 insertions(+), 30 deletions(-) diff --git a/include/boost/thread/executors/scheduled_thread_pool.hpp b/include/boost/thread/executors/scheduled_thread_pool.hpp index 103fe6c3..c13d79e4 100644 --- a/include/boost/thread/executors/scheduled_thread_pool.hpp +++ b/include/boost/thread/executors/scheduled_thread_pool.hpp @@ -13,53 +13,119 @@ #include #include #include +#include +#include namespace boost { namespace executors { - class scheduled_thread_pool : public detail::scheduled_executor_base<> + template + class scheduled_thread_pool { private: - typedef scoped_thread<> thread_t; - typedef csbl::vector thread_vector; - thread_vector threads; - public: + struct shared_state : public detail::scheduled_executor_base<> { - scheduled_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) : super() - { + /// basic_thread_pool is not copyable. + BOOST_THREAD_NO_COPYABLE(shared_state) - try + typedef detail::scheduled_executor_base<> super; + typedef typename super::work work; + + typedef scoped_thread<> thread_t; + typedef csbl::vector thread_vector; + thread_vector threads; + + shared_state(unsigned const thread_count = thread::hardware_concurrency()+1) : super() { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) + + try { -#if 1 - thread th (&super::loop, this); - threads.push_back(thread_t(boost::move(th))); -#else - threads.push_back(thread_t(&super::loop, this)); // do not compile -#endif + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { + #if 1 + thread th (&shared_state::loop, this); + threads.push_back(thread_t(boost::move(th))); + #else + threads.push_back(thread_t(&shared_state::loop, this)); // do not compile + #endif + } + } + catch (...) + { + close(); + throw; } } - catch (...) + + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c basic_thread_pool destructor. + */ + ~shared_state() { - close(); - throw; + this->close(); } + }; //end class + + public: + typedef typename shared_state::work work; + typedef Clock clock; + typedef typename clock::duration duration; + typedef typename clock::time_point time_point; + + + /** + * \b Effects: creates a thread pool that runs closures on \c thread_count threads. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + scheduled_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) + : pimpl(make_shared(thread_count)) + { } + /** + * \b Effects: Destroys the thread pool. + * + * \b Synchronization: The completion of all the closures happen before the completion of the \c basic_thread_pool destructor. + */ ~scheduled_thread_pool() { - this->close(); + } + /** + * \b Effects: close the \c serial_executor for submissions. + * The loop will work until there is no more closures to run. + */ + void close() + { + pimpl->close(); } - private: - typedef detail::scheduled_executor_base<> super; - }; //end class + /** + * \b Returns: whether the pool is closed for submissions. + */ + bool closed() + { + return pimpl->closed(); + } + void submit_at(work w, const time_point& tp) + { + return pimpl->submit_at(boost::move(w), tp); + } + + void submit_after(work w, const duration& d) + { + return pimpl->submit_after(boost::move(w), d); + } + private: + shared_ptr pimpl; + }; } //end executors namespace using executors::scheduled_thread_pool; diff --git a/test/test_scheduled_tp.cpp b/test/test_scheduled_tp.cpp index b4503a8b..ca0f3f39 100644 --- a/test/test_scheduled_tp.cpp +++ b/test/test_scheduled_tp.cpp @@ -24,7 +24,7 @@ using namespace boost::chrono; -typedef boost::scheduled_thread_pool scheduled_tp; +typedef boost::scheduled_thread_pool<> scheduled_tp; void fn(int x) { @@ -46,19 +46,18 @@ void func2(scheduled_tp* tp, steady_clock::duration d) void test_timing(const int n) { //This function should take n seconds to execute. - boost::scheduled_thread_pool se(4); + boost::scheduled_thread_pool<> se(4); for(int i = 1; i <= n; i++) { se.submit_after(boost::bind(fn,i), milliseconds(i*100)); } - boost::this_thread::sleep_for(boost::chrono::seconds(10)); //dtor is called here so all task will have to be executed before we return } void test_deque_timing() { - boost::scheduled_thread_pool se(4); + boost::scheduled_thread_pool<> se(4); for(int i = 0; i < 10; i++) { steady_clock::duration d = milliseconds(i*100); @@ -85,10 +84,10 @@ void test_deque_multi(const int n) int main() { - steady_clock::time_point start = steady_clock::now(); + //steady_clock::time_point start = steady_clock::now(); test_timing(5); - steady_clock::duration diff = steady_clock::now() - start; - BOOST_TEST(diff > milliseconds(500)); + //steady_clock::duration diff = steady_clock::now() - start; + //BOOST_TEST(diff > milliseconds(500)); test_deque_timing(); test_deque_multi(4); test_deque_multi(8); From 33ee3445af1888723c492c827f1ff8ca51143985 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sun, 1 Mar 2015 18:00:58 +0100 Subject: [PATCH 23/28] refactor basic_thread_pool. It doesn't works yet for at_thread_exit. Needs to replace function. --- doc/async_executors.qbk | 2 +- example/executor.cpp | 2 +- example/generic_executor.cpp | 2 +- example/generic_executor_ref.cpp | 2 +- example/generic_serial_executor.cpp | 2 +- example/generic_serial_executor_cont.cpp | 2 +- .../thread/executors/basic_thread_pool.hpp | 145 +++++++----------- 7 files changed, 65 insertions(+), 92 deletions(-) diff --git a/doc/async_executors.qbk b/doc/async_executors.qbk index e5586793..532260c7 100644 --- a/doc/async_executors.qbk +++ b/doc/async_executors.qbk @@ -174,7 +174,7 @@ This has several advantages: * The scheduled operations are available for all the executors via wrappers. * The template functions could accept any chrono `time_point` and `duration` respectively as we are not working with virtual functions. -In order to manage with all the clocks, this library propose generic solution. `scheduler` know how to manage with the `submit_at`/`submit_after` `Clock::time_point`/`Clock::duration` tasks. Note that the durations on different clocks differ. +In order to manage with all the clocks, this library propose a generic solution. `scheduler` know how to manage with the `submit_at`/`submit_after` `Clock::time_point`/`Clock::duration` tasks. Note that the durations on different clocks differ. [heading Not Handled Exceptions] As in N3785 and based on the same design decision than `std`/`boost::thread` if a user closure throws an exception, the executor must call the `std::terminate` function. diff --git a/example/executor.cpp b/example/executor.cpp index f0404ecf..b9c91fbf 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -65,7 +65,7 @@ void submit_some(boost::executor& tp) } -void at_th_entry( ) +void at_th_entry(boost::basic_thread_pool ) { } diff --git a/example/generic_executor.cpp b/example/generic_executor.cpp index 2dd5c2e3..67429b5e 100644 --- a/example/generic_executor.cpp +++ b/example/generic_executor.cpp @@ -89,7 +89,7 @@ void submit_some3(boost::serial_executor& tp) } } -void at_th_entry() +void at_th_entry(boost::basic_thread_pool) { } diff --git a/example/generic_executor_ref.cpp b/example/generic_executor_ref.cpp index e79ea9c4..83ea2535 100644 --- a/example/generic_executor_ref.cpp +++ b/example/generic_executor_ref.cpp @@ -65,7 +65,7 @@ void submit_some(boost::generic_executor_ref tp) } -void at_th_entry() +void at_th_entry(boost::basic_thread_pool) { } diff --git a/example/generic_serial_executor.cpp b/example/generic_serial_executor.cpp index c6fa9e87..e3941a19 100644 --- a/example/generic_serial_executor.cpp +++ b/example/generic_serial_executor.cpp @@ -67,7 +67,7 @@ void submit_some(boost::generic_serial_executor& tp) } -void at_th_entry(boost::basic_thread_pool& ) +void at_th_entry(boost::basic_thread_pool ) { } diff --git a/example/generic_serial_executor_cont.cpp b/example/generic_serial_executor_cont.cpp index c21e1d89..49e03165 100644 --- a/example/generic_serial_executor_cont.cpp +++ b/example/generic_serial_executor_cont.cpp @@ -68,7 +68,7 @@ void submit_some(boost::generic_serial_executor_cont& tp) } -void at_th_entry(boost::basic_thread_pool& ) +void at_th_entry(boost::basic_thread_pool) { } diff --git a/include/boost/thread/executors/basic_thread_pool.hpp b/include/boost/thread/executors/basic_thread_pool.hpp index c8283d19..d03a31e0 100644 --- a/include/boost/thread/executors/basic_thread_pool.hpp +++ b/include/boost/thread/executors/basic_thread_pool.hpp @@ -18,6 +18,11 @@ #include #include +#include +#include +#include +#include + #include namespace boost @@ -31,17 +36,22 @@ namespace executors typedef executors::work work; private: - struct shared_state { + struct shared_state : enable_shared_from_this { typedef executors::work work; /// the kind of stored threads are scoped threads to ensure that the threads are joined. /// A move aware vector type - typedef scoped_thread<> thread_t; + //typedef scoped_thread<> thread_t; + typedef thread thread_t; typedef csbl::vector thread_vector; /// the thread safe work queue concurrent::sync_queue work_queue; /// A move aware vector thread_vector threads; + unsigned const thread_count; + boost::function at_thread_entry; + friend class basic_thread_pool; + public: /** @@ -85,13 +95,15 @@ namespace executors */ void worker_thread() { + // fixme: this call results on segmentation fault + //at_thread_entry(basic_thread_pool(this->shared_from_this())); try { for(;;) { work task; queue_op_status st = work_queue.wait_pull(task); - if (st == queue_op_status::closed) return; + if (st == queue_op_status::closed) break; task(); } } @@ -101,26 +113,26 @@ namespace executors return; } } - #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) - template - void worker_thread1(AtThreadEntry& at_thread_entry) + + static void do_nothing_at_thread_entry(basic_thread_pool) {} + + void init() { - at_thread_entry(); - worker_thread(); + try + { + threads.reserve(thread_count); + for (unsigned i = 0; i < thread_count; ++i) + { + thread th (&shared_state::worker_thread, this); + threads.push_back(thread_t(boost::move(th))); + } + } + catch (...) + { + close(); + throw; + } } - #endif - void worker_thread2(void(*at_thread_entry)()) - { - at_thread_entry(); - worker_thread(); - } - template - void worker_thread3(BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) - { - at_thread_entry(); - worker_thread(); - } - static void do_nothing_at_thread_entry() {} public: /// basic_thread_pool is not copyable. @@ -132,25 +144,9 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ shared_state(unsigned const thread_count = thread::hardware_concurrency()+1) + : thread_count(thread_count), + at_thread_entry(do_nothing_at_thread_entry) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - #if 1 - thread th (&shared_state::worker_thread, this); - threads.push_back(thread_t(boost::move(th))); - #else - threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile - #endif - } - } - catch (...) - { - close(); - throw; - } } /** * \b Effects: creates a thread pool that runs closures on \c thread_count threads @@ -161,60 +157,21 @@ namespace executors #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) template shared_state( unsigned const thread_count, AtThreadEntry& at_thread_entry) + : thread_count(thread_count), + at_thread_entry(at_thread_entry) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - thread th (&shared_state::worker_thread1, this, at_thread_entry); - threads.push_back(thread_t(boost::move(th))); - //threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile - } - } - catch (...) - { - close(); - throw; - } } #endif - shared_state( unsigned const thread_count, void(*at_thread_entry)()) + shared_state( unsigned const thread_count, void(*at_thread_entry)(basic_thread_pool)) + : thread_count(thread_count), + at_thread_entry(at_thread_entry) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - thread th (&shared_state::worker_thread2, this, at_thread_entry); - threads.push_back(thread_t(boost::move(th))); - //threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile - } - } - catch (...) - { - close(); - throw; - } } template shared_state( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) + : thread_count(thread_count), + at_thread_entry(boost::move(at_thread_entry)) { - try - { - threads.reserve(thread_count); - for (unsigned i = 0; i < thread_count; ++i) - { - thread th (&shared_state::worker_thread3, this, boost::forward(at_thread_entry)); - threads.push_back(thread_t(boost::move(th))); - //threads.push_back(thread_t(&shared_state::worker_thread, this)); // do not compile - } - } - catch (...) - { - close(); - throw; - } } /** * \b Effects: Destroys the thread pool. @@ -225,6 +182,7 @@ namespace executors { // signal to all the worker threads that there will be no more submissions. close(); + join(); // joins all the threads as the threads were scoped_threads } @@ -235,6 +193,7 @@ namespace executors { for (unsigned i = 0; i < threads.size(); ++i) { + if (this_thread::get_id() == threads[i].get_id()) continue; threads[i].join(); } } @@ -304,6 +263,16 @@ namespace executors } }; + /** + * \b Effects: creates a thread pool with this shared state. + * + * \b Throws: Whatever exception is thrown while initializing the needed resources. + */ + friend struct shared_state; + basic_thread_pool(shared_ptr ptr) + : pimpl(ptr) + { + } public: /** * \b Effects: creates a thread pool that runs closures on \c thread_count threads. @@ -313,6 +282,7 @@ namespace executors basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) : pimpl(make_shared(thread_count)) { + pimpl->init(); } /** @@ -326,17 +296,20 @@ namespace executors basic_thread_pool( unsigned const thread_count, AtThreadEntry& at_thread_entry) : pimpl(make_shared(thread_count, at_thread_entry)) { + pimpl->init(); } #endif - basic_thread_pool( unsigned const thread_count, void(*at_thread_entry)()) + basic_thread_pool( unsigned const thread_count, void(*at_thread_entry)(basic_thread_pool)) : pimpl(make_shared(thread_count, at_thread_entry)) { + pimpl->init(); } template basic_thread_pool( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) : pimpl(make_shared(thread_count, at_thread_entry)) { + pimpl->init(); } /** From 5a1de7a722e2e36046689ea5d22437ef0160124f Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Tue, 3 Mar 2015 00:50:48 +0100 Subject: [PATCH 24/28] ensure that generic executors are copyable. --- example/generic_serial_executor.cpp | 8 +++++++- example/generic_serial_executor_cont.cpp | 6 ++++++ include/boost/thread/executors/generic_executor.hpp | 3 ++- .../boost/thread/executors/generic_executor_ref.hpp | 4 ++-- .../thread/executors/generic_serial_executor.hpp | 12 ++++++++++-- .../executors/generic_serial_executor_cont.hpp | 11 +++++++++-- 6 files changed, 36 insertions(+), 8 deletions(-) diff --git a/example/generic_serial_executor.cpp b/example/generic_serial_executor.cpp index e3941a19..453c02f5 100644 --- a/example/generic_serial_executor.cpp +++ b/example/generic_serial_executor.cpp @@ -74,7 +74,13 @@ void at_th_entry(boost::basic_thread_pool ) int test_executor_adaptor() { - // std::cout << BOOST_CONTEXTOF << std::endl; + std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool tp; + boost::generic_serial_executor e1(tp); + boost::generic_serial_executor e2 = e1; + } + std::cout << BOOST_CONTEXTOF << std::endl; { try { diff --git a/example/generic_serial_executor_cont.cpp b/example/generic_serial_executor_cont.cpp index 49e03165..f39ad46b 100644 --- a/example/generic_serial_executor_cont.cpp +++ b/example/generic_serial_executor_cont.cpp @@ -75,6 +75,12 @@ void at_th_entry(boost::basic_thread_pool) int test_executor_adaptor() { + std::cout << BOOST_CONTEXTOF << std::endl; + { + boost::basic_thread_pool tp; + boost::generic_serial_executor_cont e1(tp); + boost::generic_serial_executor_cont e2 = e1; + } // std::cout << BOOST_CONTEXTOF << std::endl; { try diff --git a/include/boost/thread/executors/generic_executor.hpp b/include/boost/thread/executors/generic_executor.hpp index 28faae0a..31721628 100644 --- a/include/boost/thread/executors/generic_executor.hpp +++ b/include/boost/thread/executors/generic_executor.hpp @@ -15,6 +15,7 @@ #include #include +#include #include @@ -36,7 +37,7 @@ namespace boost template generic_executor(Executor& ex) //: ex(make_shared >(ex)) // todo check why this doesn't works with C++03 - : ex( new executor_adaptor(ex) ) + : ex( new executor_adaptor::type>(ex) ) { } diff --git a/include/boost/thread/executors/generic_executor_ref.hpp b/include/boost/thread/executors/generic_executor_ref.hpp index 8c9c865e..d1df0e93 100644 --- a/include/boost/thread/executors/generic_executor_ref.hpp +++ b/include/boost/thread/executors/generic_executor_ref.hpp @@ -100,8 +100,8 @@ namespace boost template generic_executor_ref(Executor& ex) - //: ex(make_shared >(ex)) // todo check why this doesn't works with C++03 - : ex( new executor_ref(ex) ) + //: ex(make_shared::type> >(ex)) // todo check why this doesn't works with C++03 + : ex( new executor_ref::type>(ex) ) { } diff --git a/include/boost/thread/executors/generic_serial_executor.hpp b/include/boost/thread/executors/generic_serial_executor.hpp index 5d2c758f..283915da 100644 --- a/include/boost/thread/executors/generic_serial_executor.hpp +++ b/include/boost/thread/executors/generic_serial_executor.hpp @@ -20,6 +20,11 @@ #include #include +#include +#include +#include + +#include #include @@ -178,8 +183,11 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ template - generic_serial_executor(Executor& ex) - : pimpl(make_shared(ex)) + generic_serial_executor(Executor& ex, + typename boost::disable_if::type, generic_serial_executor>, + int* >::type = (int*)0) + //: pimpl(make_shared(ex)) // // todo check why this doesn't works with C++03 + : pimpl(new shared_state(ex)) { } /** diff --git a/include/boost/thread/executors/generic_serial_executor_cont.hpp b/include/boost/thread/executors/generic_serial_executor_cont.hpp index 43fc0de1..938e5308 100644 --- a/include/boost/thread/executors/generic_serial_executor_cont.hpp +++ b/include/boost/thread/executors/generic_serial_executor_cont.hpp @@ -19,6 +19,9 @@ #include #include +#include +#include +#include #include @@ -161,8 +164,12 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ template - generic_serial_executor_cont(Executor& ex) - : pimpl(make_shared(ex)) + generic_serial_executor_cont(Executor& ex, + typename boost::disable_if::type, generic_serial_executor_cont>, + int* >::type = (int*)0) + //: pimpl(make_shared(ex)) // todo check why this doesn't works with C++03 + : pimpl(new shared_state(ex)) + { } /** From 62bffed368ac2625ba1a5281fc689a9871e2a76b Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Tue, 3 Mar 2015 08:27:17 +0100 Subject: [PATCH 25/28] More fixes to make executor copyable. --- example/executor.cpp | 30 +++++++++++-------- .../thread/executors/basic_thread_pool.hpp | 3 +- .../executors/generic_serial_executor.hpp | 2 +- 3 files changed, 20 insertions(+), 15 deletions(-) diff --git a/example/executor.cpp b/example/executor.cpp index b9c91fbf..15c43804 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -111,25 +111,29 @@ int test_executor_adaptor() submit_some( ea2); ea2.underlying_executor().run_queued_closures(); } -#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) // std::cout << BOOST_CONTEXTOF << std::endl; -// { -// boost::basic_thread_pool tp; -// boost::generic_serial_executor e1(tp); -// boost::generic_serial_executor e2 = e1; -// } + { + boost::basic_thread_pool tp; + boost::generic_serial_executor e1(tp); + boost::generic_serial_executor e2 = e1; + } + { + boost::basic_thread_pool ea1(4); + boost::generic_serial_executor ea2(ea1); + boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); + submit_some(ea3); + } // { // boost::basic_thread_pool ea1(4); // boost::executor_adaptor < boost::generic_serial_executor > ea2(ea1); // submit_some(ea2); // } -// { -// boost::basic_thread_pool ea1(4); -// boost::generic_serial_executor ea2(ea1); -// boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); -// submit_some(ea3); -// } -#endif +// { +// boost::basic_thread_pool ea1(4); +// boost::generic_serial_executor ea2(ea1); +// boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); +// submit_some(ea3); +// } // std::cout << BOOST_CONTEXTOF << std::endl; { boost::inline_executor e1; diff --git a/include/boost/thread/executors/basic_thread_pool.hpp b/include/boost/thread/executors/basic_thread_pool.hpp index d03a31e0..ab943f3d 100644 --- a/include/boost/thread/executors/basic_thread_pool.hpp +++ b/include/boost/thread/executors/basic_thread_pool.hpp @@ -280,7 +280,8 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) - : pimpl(make_shared(thread_count)) + //: pimpl(make_shared(thread_count)) // todo check why this doesn't works with C++03 + : pimpl(new shared_state(thread_count)) { pimpl->init(); } diff --git a/include/boost/thread/executors/generic_serial_executor.hpp b/include/boost/thread/executors/generic_serial_executor.hpp index 283915da..2c4aca07 100644 --- a/include/boost/thread/executors/generic_serial_executor.hpp +++ b/include/boost/thread/executors/generic_serial_executor.hpp @@ -186,7 +186,7 @@ namespace executors generic_serial_executor(Executor& ex, typename boost::disable_if::type, generic_serial_executor>, int* >::type = (int*)0) - //: pimpl(make_shared(ex)) // // todo check why this doesn't works with C++03 + //: pimpl(make_shared(ex)) // todo check why this doesn't works with C++03 : pimpl(new shared_state(ex)) { } From 7ffcec448cdb62700ff400baea8a6c7d5bfe5410 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Tue, 3 Mar 2015 19:29:50 +0100 Subject: [PATCH 26/28] uncomment more tests. --- example/executor.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/example/executor.cpp b/example/executor.cpp index 15c43804..f548c066 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -123,17 +123,19 @@ int test_executor_adaptor() boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); submit_some(ea3); } -// { -// boost::basic_thread_pool ea1(4); -// boost::executor_adaptor < boost::generic_serial_executor > ea2(ea1); -// submit_some(ea2); -// } -// { -// boost::basic_thread_pool ea1(4); -// boost::generic_serial_executor ea2(ea1); -// boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); -// submit_some(ea3); -// } + { + boost::basic_thread_pool ea1(4); + boost::generic_serial_executor ea2(ea1); + boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); + submit_some(ea3); + } +#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + { + boost::basic_thread_pool ea1(4); + boost::executor_adaptor < boost::generic_serial_executor > ea2(ea1); + submit_some(ea2); + } +#endif // std::cout << BOOST_CONTEXTOF << std::endl; { boost::inline_executor e1; From 9a05211faadf75b8dac872581ce9d67cb89e69e2 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Wed, 4 Mar 2015 07:59:27 +0100 Subject: [PATCH 27/28] fix issue with c++03 compilers. Pass Executors by const& instead of by &. --- example/executor.cpp | 15 +++++++++++++-- .../boost/thread/executors/basic_thread_pool.hpp | 5 ++--- .../boost/thread/executors/generic_executor.hpp | 9 ++++++--- .../thread/executors/generic_executor_ref.hpp | 2 +- .../thread/executors/generic_serial_executor.hpp | 9 ++++----- .../executors/generic_serial_executor_cont.hpp | 10 ++++------ 6 files changed, 30 insertions(+), 20 deletions(-) diff --git a/example/executor.cpp b/example/executor.cpp index f548c066..8770d1fb 100644 --- a/example/executor.cpp +++ b/example/executor.cpp @@ -105,6 +105,9 @@ int test_executor_adaptor() { boost::loop_executor e1; boost::loop_executor e2 = e1; + boost::executor_adaptor < boost::loop_executor > ea2(e2); + submit_some( ea2); + ea2.underlying_executor().run_queued_closures(); } { boost::executor_adaptor < boost::loop_executor > ea2; @@ -129,17 +132,19 @@ int test_executor_adaptor() boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2); submit_some(ea3); } -#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +//#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) { boost::basic_thread_pool ea1(4); boost::executor_adaptor < boost::generic_serial_executor > ea2(ea1); submit_some(ea2); } -#endif +//#endif // std::cout << BOOST_CONTEXTOF << std::endl; { boost::inline_executor e1; boost::inline_executor e2 = e1; + boost::executor_adaptor < boost::inline_executor > ea2(e2); + submit_some(ea2); } { boost::executor_adaptor < boost::inline_executor > ea1; @@ -150,6 +155,12 @@ int test_executor_adaptor() boost::thread_executor e1; boost::thread_executor e2 = e1; } + { + boost::thread_executor e1; + boost::executor_adaptor < boost::generic_executor > ea2(e1); + submit_some(ea2); + } + { boost::executor_adaptor < boost::thread_executor > ea1; submit_some(ea1); diff --git a/include/boost/thread/executors/basic_thread_pool.hpp b/include/boost/thread/executors/basic_thread_pool.hpp index ab943f3d..afab0795 100644 --- a/include/boost/thread/executors/basic_thread_pool.hpp +++ b/include/boost/thread/executors/basic_thread_pool.hpp @@ -280,8 +280,7 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) - //: pimpl(make_shared(thread_count)) // todo check why this doesn't works with C++03 - : pimpl(new shared_state(thread_count)) + : pimpl(make_shared(thread_count)) { pimpl->init(); } @@ -308,7 +307,7 @@ namespace executors } template basic_thread_pool( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) - : pimpl(make_shared(thread_count, at_thread_entry)) + : pimpl(make_shared(thread_count, boost::forward(at_thread_entry))) { pimpl->init(); } diff --git a/include/boost/thread/executors/generic_executor.hpp b/include/boost/thread/executors/generic_executor.hpp index 31721628..0513371c 100644 --- a/include/boost/thread/executors/generic_executor.hpp +++ b/include/boost/thread/executors/generic_executor.hpp @@ -35,9 +35,12 @@ namespace boost //generic_executor(generic_executor &&) = default; template - generic_executor(Executor& ex) - //: ex(make_shared >(ex)) // todo check why this doesn't works with C++03 - : ex( new executor_adaptor::type>(ex) ) + generic_executor(Executor const& ex + , typename boost::disable_if, + int* >::type = (int*)0 + ) + //: ex(make_shared >(ex)) // todo check why this doesn't work with C++03 + : ex( new executor_adaptor(ex) ) { } diff --git a/include/boost/thread/executors/generic_executor_ref.hpp b/include/boost/thread/executors/generic_executor_ref.hpp index d1df0e93..b2080e01 100644 --- a/include/boost/thread/executors/generic_executor_ref.hpp +++ b/include/boost/thread/executors/generic_executor_ref.hpp @@ -100,7 +100,7 @@ namespace boost template generic_executor_ref(Executor& ex) - //: ex(make_shared::type> >(ex)) // todo check why this doesn't works with C++03 + //: ex(make_shared::type> >(ex)) // todo check why this doesn't work with C++03 : ex( new executor_ref::type>(ex) ) { } diff --git a/include/boost/thread/executors/generic_serial_executor.hpp b/include/boost/thread/executors/generic_serial_executor.hpp index 2c4aca07..efa9077a 100644 --- a/include/boost/thread/executors/generic_serial_executor.hpp +++ b/include/boost/thread/executors/generic_serial_executor.hpp @@ -108,7 +108,7 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ template - shared_state(Executor& ex) + shared_state(Executor const& ex) : ex(ex), thr(&shared_state::worker_thread, this) { } @@ -183,11 +183,10 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ template - generic_serial_executor(Executor& ex, - typename boost::disable_if::type, generic_serial_executor>, + generic_serial_executor(Executor const& ex + , typename boost::disable_if, int* >::type = (int*)0) - //: pimpl(make_shared(ex)) // todo check why this doesn't works with C++03 - : pimpl(new shared_state(ex)) + : pimpl(make_shared(ex)) { } /** diff --git a/include/boost/thread/executors/generic_serial_executor_cont.hpp b/include/boost/thread/executors/generic_serial_executor_cont.hpp index 938e5308..1c6cf1fe 100644 --- a/include/boost/thread/executors/generic_serial_executor_cont.hpp +++ b/include/boost/thread/executors/generic_serial_executor_cont.hpp @@ -88,7 +88,7 @@ namespace executors * - the continuation can not submit to this serial executor. */ template - shared_state(Executor& ex) + shared_state(Executor const& ex) : ex_(ex), fut_(make_ready_future()), closed_(false) { } @@ -164,12 +164,10 @@ namespace executors * \b Throws: Whatever exception is thrown while initializing the needed resources. */ template - generic_serial_executor_cont(Executor& ex, - typename boost::disable_if::type, generic_serial_executor_cont>, + generic_serial_executor_cont(Executor const& ex + , typename boost::disable_if, int* >::type = (int*)0) - //: pimpl(make_shared(ex)) // todo check why this doesn't works with C++03 - : pimpl(new shared_state(ex)) - + : pimpl(make_shared(ex)) { } /** From caaa7b4cc290acc4238e3f36f9a66d82c1082081 Mon Sep 17 00:00:00 2001 From: "Vicente J. Botet Escriba" Date: Sun, 8 Mar 2015 23:30:41 +0100 Subject: [PATCH 28/28] store executor by value. --- .../experimental/parallel/v2/task_region.hpp | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/include/boost/thread/experimental/parallel/v2/task_region.hpp b/include/boost/thread/experimental/parallel/v2/task_region.hpp index 7f3d0e2b..8d1ac085 100755 --- a/include/boost/thread/experimental/parallel/v2/task_region.hpp +++ b/include/boost/thread/experimental/parallel/v2/task_region.hpp @@ -115,9 +115,9 @@ BOOST_THREAD_INLINE_NAMESPACE(v2) template friend void task_region_final(BOOST_THREAD_FWD_REF(F) f); template - friend void task_region(Ex&, BOOST_THREAD_FWD_REF(F) f); + friend void task_region(Ex const&, BOOST_THREAD_FWD_REF(F) f); template - friend void task_region_final(Ex&, BOOST_THREAD_FWD_REF(F) f); + friend void task_region_final(Ex const&, BOOST_THREAD_FWD_REF(F) f); void wait_all() { @@ -156,21 +156,20 @@ protected: #if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS task_region_handle_gen() : canceled(false) - , ex(0) {} - task_region_handle_gen(Executor& ex) + task_region_handle_gen(Executor const& ex) : canceled(false) - , ex(&ex) + , ex(ex) {} #endif #if ! defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS task_region_handle_gen() - : ex(0) + //: ex(0) {} - task_region_handle_gen(Executor& ex) - : ex(&ex) + task_region_handle_gen(Executor const& ex) + : ex(ex) {} #endif @@ -190,7 +189,7 @@ protected: bool canceled; #endif #if defined BOOST_THREAD_PROVIDES_EXECUTORS - Executor* ex; + Executor ex; #endif exception_list exs; typedef csbl::vector > group_type; @@ -214,13 +213,13 @@ protected: //throw task_canceled_exception(); } #if defined BOOST_THREAD_PROVIDES_EXECUTORS - group.push_back(async(*ex, detail::wrapped, F>(*this, forward(f)))); + group.push_back(async(ex, detail::wrapped, F>(*this, forward(f)))); #else group.push_back(async(detail::wrapped, F>(*this, forward(f)))); #endif #else #if defined BOOST_THREAD_PROVIDES_EXECUTORS - group.push_back(async(*ex, forward(f))); + group.push_back(async(ex, forward(f))); #else group.push_back(async(forward(f))); #endif @@ -247,17 +246,18 @@ protected: class task_region_handle : public task_region_handle_gen { - default_executor tp; + //default_executor tp; template friend void task_region(BOOST_THREAD_FWD_REF(F) f); template friend void task_region_final(BOOST_THREAD_FWD_REF(F) f); protected: - task_region_handle() : task_region_handle_gen() + task_region_handle() + : task_region_handle_gen() { #if defined BOOST_THREAD_PROVIDES_EXECUTORS - ex = &tp; + //ex = &tp; #endif } BOOST_DELETED_FUNCTION(task_region_handle(const task_region_handle&)) @@ -267,7 +267,7 @@ protected: }; template - void task_region_final(Executor& ex, BOOST_THREAD_FWD_REF(F) f) + void task_region_final(Executor const& ex, BOOST_THREAD_FWD_REF(F) f) { task_region_handle_gen tr(ex); try @@ -283,7 +283,7 @@ protected: } template - void task_region(Executor& ex, BOOST_THREAD_FWD_REF(F) f) + void task_region(Executor const& ex, BOOST_THREAD_FWD_REF(F) f) { task_region_final(ex, forward(f)); }