Chris@102: // Copyright (C) 2013-2014 Vicente J. Botet Escriba Chris@102: // Chris@102: // Distributed under the Boost Software License, Version 1.0. (See accompanying Chris@102: // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) Chris@102: // Chris@102: // 2013/09 Vicente J. Botet Escriba Chris@102: // Adapt to boost from CCIA C++11 implementation Chris@102: // first implementation of a simple pool thread using a vector of threads and a sync_queue. Chris@102: Chris@102: #ifndef BOOST_THREAD_EXECUTORS_BASIC_THREAD_POOL_HPP Chris@102: #define BOOST_THREAD_EXECUTORS_BASIC_THREAD_POOL_HPP Chris@102: Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: #include Chris@102: Chris@102: #include Chris@102: Chris@102: namespace boost Chris@102: { Chris@102: namespace executors Chris@102: { Chris@102: class basic_thread_pool Chris@102: { Chris@102: public: Chris@102: /// type-erasure to store the works to do Chris@102: typedef executors::work work; Chris@102: private: Chris@102: /// the kind of stored threads are scoped threads to ensure that the threads are joined. Chris@102: /// A move aware vector type Chris@102: typedef scoped_thread<> thread_t; Chris@102: typedef csbl::vector thread_vector; Chris@102: Chris@102: /// the thread safe work queue Chris@102: concurrent::sync_queue work_queue; Chris@102: /// A move aware vector Chris@102: thread_vector threads; Chris@102: Chris@102: public: Chris@102: /** Chris@102: * Effects: try to execute one task. Chris@102: * Returns: whether a task has been executed. Chris@102: * Throws: whatever the current task constructor throws or the task() throws. Chris@102: */ Chris@102: bool try_executing_one() Chris@102: { Chris@102: try Chris@102: { Chris@102: work task; Chris@102: if (work_queue.try_pull(task) == queue_op_status::success) Chris@102: { Chris@102: task(); Chris@102: return true; Chris@102: } Chris@102: return false; Chris@102: } Chris@102: catch (...) Chris@102: { Chris@102: std::terminate(); Chris@102: return false; Chris@102: } Chris@102: } Chris@102: /** Chris@102: * Effects: schedule one task or yields Chris@102: * Throws: whatever the current task constructor throws or the task() throws. Chris@102: */ Chris@102: void schedule_one_or_yield() Chris@102: { Chris@102: if ( ! try_executing_one()) Chris@102: { Chris@102: this_thread::yield(); Chris@102: } Chris@102: } Chris@102: private: Chris@102: Chris@102: /** Chris@102: * The main loop of the worker threads Chris@102: */ Chris@102: void worker_thread() Chris@102: { Chris@102: try Chris@102: { Chris@102: for(;;) Chris@102: { Chris@102: work task; Chris@102: queue_op_status st = work_queue.wait_pull(task); Chris@102: if (st == queue_op_status::closed) return; Chris@102: task(); Chris@102: } Chris@102: } Chris@102: catch (...) Chris@102: { Chris@102: std::terminate(); Chris@102: return; Chris@102: } Chris@102: } Chris@102: #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) Chris@102: template Chris@102: void worker_thread1(AtThreadEntry& at_thread_entry) Chris@102: { Chris@102: at_thread_entry(*this); Chris@102: worker_thread(); Chris@102: } Chris@102: #endif Chris@102: void worker_thread2(void(*at_thread_entry)(basic_thread_pool&)) Chris@102: { Chris@102: at_thread_entry(*this); Chris@102: worker_thread(); Chris@102: } Chris@102: template Chris@102: void worker_thread3(BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) Chris@102: { Chris@102: at_thread_entry(*this); Chris@102: worker_thread(); Chris@102: } Chris@102: static void do_nothing_at_thread_entry(basic_thread_pool&) {} Chris@102: Chris@102: public: Chris@102: /// basic_thread_pool is not copyable. Chris@102: BOOST_THREAD_NO_COPYABLE(basic_thread_pool) Chris@102: Chris@102: /** Chris@102: * \b Effects: creates a thread pool that runs closures on \c thread_count threads. Chris@102: * Chris@102: * \b Throws: Whatever exception is thrown while initializing the needed resources. Chris@102: */ Chris@102: basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) Chris@102: { Chris@102: try Chris@102: { Chris@102: threads.reserve(thread_count); Chris@102: for (unsigned i = 0; i < thread_count; ++i) Chris@102: { Chris@102: #if 1 Chris@102: thread th (&basic_thread_pool::worker_thread, this); Chris@102: threads.push_back(thread_t(boost::move(th))); Chris@102: #else Chris@102: threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile Chris@102: #endif Chris@102: } Chris@102: } Chris@102: catch (...) Chris@102: { Chris@102: close(); Chris@102: throw; Chris@102: } Chris@102: } Chris@102: /** Chris@102: * \b Effects: creates a thread pool that runs closures on \c thread_count threads Chris@102: * and executes the at_thread_entry function at the entry of each created thread. . Chris@102: * Chris@102: * \b Throws: Whatever exception is thrown while initializing the needed resources. Chris@102: */ Chris@102: #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) Chris@102: template Chris@102: basic_thread_pool( unsigned const thread_count, AtThreadEntry& at_thread_entry) Chris@102: { Chris@102: try Chris@102: { Chris@102: threads.reserve(thread_count); Chris@102: for (unsigned i = 0; i < thread_count; ++i) Chris@102: { Chris@102: thread th (&basic_thread_pool::worker_thread1, this, at_thread_entry); Chris@102: threads.push_back(thread_t(boost::move(th))); Chris@102: //threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile Chris@102: } Chris@102: } Chris@102: catch (...) Chris@102: { Chris@102: close(); Chris@102: throw; Chris@102: } Chris@102: } Chris@102: #endif Chris@102: basic_thread_pool( unsigned const thread_count, void(*at_thread_entry)(basic_thread_pool&)) Chris@102: { Chris@102: try Chris@102: { Chris@102: threads.reserve(thread_count); Chris@102: for (unsigned i = 0; i < thread_count; ++i) Chris@102: { Chris@102: thread th (&basic_thread_pool::worker_thread2, this, at_thread_entry); Chris@102: threads.push_back(thread_t(boost::move(th))); Chris@102: //threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile Chris@102: } Chris@102: } Chris@102: catch (...) Chris@102: { Chris@102: close(); Chris@102: throw; Chris@102: } Chris@102: } Chris@102: template Chris@102: basic_thread_pool( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) Chris@102: { Chris@102: try Chris@102: { Chris@102: threads.reserve(thread_count); Chris@102: for (unsigned i = 0; i < thread_count; ++i) Chris@102: { Chris@102: thread th (&basic_thread_pool::worker_thread3, this, boost::forward(at_thread_entry)); Chris@102: threads.push_back(thread_t(boost::move(th))); Chris@102: //threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile Chris@102: } Chris@102: } Chris@102: catch (...) Chris@102: { Chris@102: close(); Chris@102: throw; Chris@102: } Chris@102: } Chris@102: /** Chris@102: * \b Effects: Destroys the thread pool. Chris@102: * Chris@102: * \b Synchronization: The completion of all the closures happen before the completion of the \c basic_thread_pool destructor. Chris@102: */ Chris@102: ~basic_thread_pool() Chris@102: { Chris@102: // signal to all the worker threads that there will be no more submissions. Chris@102: close(); Chris@102: // joins all the threads as the threads were scoped_threads Chris@102: } Chris@102: Chris@102: /** Chris@102: * \b Effects: join all the threads. Chris@102: */ Chris@102: void join() Chris@102: { Chris@102: for (unsigned i = 0; i < threads.size(); ++i) Chris@102: { Chris@102: threads[i].join(); Chris@102: } Chris@102: } Chris@102: Chris@102: /** Chris@102: * \b Effects: close the \c basic_thread_pool for submissions. Chris@102: * The worker threads will work until there is no more closures to run. Chris@102: */ Chris@102: void close() Chris@102: { Chris@102: work_queue.close(); Chris@102: } Chris@102: Chris@102: /** Chris@102: * \b Returns: whether the pool is closed for submissions. Chris@102: */ Chris@102: bool closed() Chris@102: { Chris@102: return work_queue.closed(); Chris@102: } Chris@102: Chris@102: /** Chris@102: * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible. Chris@102: * Chris@102: * \b Effects: The specified \c closure will be scheduled for execution at some point in the future. Chris@102: * If invoked closure throws an exception the \c basic_thread_pool will call \c std::terminate, as is the case with threads. Chris@102: * Chris@102: * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables. Chris@102: * Chris@102: * \b Throws: \c sync_queue_is_closed if the thread pool is closed. Chris@102: * Whatever exception that can be throw while storing the closure. Chris@102: */ Chris@102: Chris@102: #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) Chris@102: template Chris@102: void submit(Closure & closure) Chris@102: { Chris@102: work_queue.push(work(closure)); Chris@102: } Chris@102: #endif Chris@102: void submit(void (*closure)()) Chris@102: { Chris@102: work_queue.push(work(closure)); Chris@102: } Chris@102: Chris@102: template Chris@102: void submit(BOOST_THREAD_RV_REF(Closure) closure) Chris@102: { Chris@102: work_queue.push(work(boost::forward(closure))); Chris@102: } Chris@102: Chris@102: /** Chris@102: * \b Requires: This must be called from an scheduled task. Chris@102: * Chris@102: * \b Effects: reschedule functions until pred() Chris@102: */ Chris@102: template Chris@102: bool reschedule_until(Pred const& pred) Chris@102: { Chris@102: do { Chris@102: if ( ! try_executing_one()) Chris@102: { Chris@102: return false; Chris@102: } Chris@102: } while (! pred()); Chris@102: return true; Chris@102: } Chris@102: Chris@102: }; Chris@102: } Chris@102: using executors::basic_thread_pool; Chris@102: Chris@102: } Chris@102: Chris@102: #include Chris@102: Chris@102: #endif