2
0
mirror of https://github.com/boostorg/thread.git synced 2026-02-09 23:42:18 +00:00

Compare commits

...

70 Commits

Author SHA1 Message Date
Vicente J. Botet Escriba
bde5a1ef55 merge from develop. 2015-05-28 19:59:13 +02:00
Vicente J. Botet Escriba
4d45da6c06 Apply patch #11302. 2015-05-18 21:26:46 +02:00
Vicente J. Botet Escriba
e39703ff18 Merge branch 'develop' of github.com:boostorg/thread into develop 2015-05-15 15:07:08 +02:00
Vicente J. Botet Escriba
aafd5ca726 applie patch for #11266. 2015-05-15 15:05:09 +02:00
Vicente J. Botet Escriba
9b69912801 Merge pull request #54 from tempoz/patch-5
Remove unused parameter
2015-05-14 18:18:31 +02:00
Vicente J. Botet Escriba
259c08473d Merge branch 'develop' of github.com:boostorg/thread into develop 2015-05-14 18:08:52 +02:00
Vicente J. Botet Escriba
6ca3a99cbc Merge pull request #56 from danieljames/link-fix
Link to archived copy of article.
2015-05-12 22:39:59 +02:00
Daniel James
3dcd875944 Link to archived copy of article.
The original is no longer available.

Fixes: https://svn.boost.org/trac/boost/ticket/11185
2015-05-12 18:52:23 +01:00
Vicente J. Botet Escriba
58e8addad6 Try to get rid of bad link issue while defining global new. 2015-05-06 13:51:21 +02:00
Vicente J. Botet Escriba
87615d54dd cleanup unused function set_exceptional_if_invalid. 2015-05-06 09:48:54 +02:00
Vicente J. Botet Escriba
d322aba077 Merge pull request #55 from jessicah/tests-fix-error-message
Tests: correct #error message in recursive_mutex/native_handle_pass
2015-05-05 20:50:32 +02:00
Jessica Hamilton
c0afe2d873 Tests: correct #error message in recursive_mutex/native_handle_pass 2015-05-06 02:14:13 +12:00
Vicente J. Botet Escriba
5488482a44 simplify launch_continuation interface. 2015-05-04 23:06:39 +02:00
Vicente J. Botet Escriba
71d9a0a120 fix issue with unwrap shared state. Take care of exceptions on the unwrapped future. 2015-05-03 22:52:52 +02:00
Vicente J. Botet Escriba
b7b2a463cf fix issue with unwrap shared state. 2015-05-03 22:32:17 +02:00
Vicente J. Botet Escriba
1b2736012c Merge branch 'fix/basic_thread_pool_bad_use_of_scoped_thread' into develop 2015-05-03 14:36:57 +02:00
Vicente J. Botet Escriba
5205fa71bf Fixed some missing lock.unlock(). 2015-05-03 14:30:52 +02:00
Vicente J. Botet Escriba
ba2988a8f7 Fixed issue with basic_thread_pool scoped threads. 2015-05-03 10:42:42 +02:00
Vicente J. Botet Escriba
e598796eaf Add assertion on future continuation parameter is ready. As noted in #11256, there some serious issues with the parameter passed and with lock on locked mutextes :(. 2015-05-02 16:29:06 +02:00
Vicente J. Botet Escriba
bce7eabba2 fix make_exceptional issue. 2015-04-30 01:11:39 +02:00
Vicente J. Botet Escriba
0218136ed7 Try to see if share_from_this helps on the thread sanitize data race. 2015-04-25 15:41:49 +02:00
Vicente J. Botet Escriba
461bf803fc Avoid data race in std::cout. 2015-04-25 11:17:20 +02:00
Vicente J. Botet Escriba
1bd78bbeea cleanup commented code. 2015-04-25 11:16:03 +02:00
Vicente J. Botet Escriba
348da6b7e4 fix memory leack in test. 2015-04-25 10:11:01 +02:00
Vicente J. Botet Escriba
e850218c49 ref #11174 - boost::condition_variable::timed_wait with predicate unexpectedly wakes up while should wait infinite. 2015-04-25 00:20:53 +02:00
Vicente J. Botet Escriba
385eefd3b3 ref #11174 - boost::condition_variable::timed_wait with predicate unexpectedly wakes up while should wait infinite. 2015-04-25 00:20:05 +02:00
Zoey Greer
2ddf7aad0b Remove unused parameter
lk is unused in get_state, naming it causes build warnings
2015-04-22 19:37:36 -04:00
Vicente J. Botet Escriba
c9433c2a5b rollback ref #11174 - boost::condition_variable::timed_wait with predicate unexpectedly wakes up while should wait infinite. 2015-04-22 22:49:26 +02:00
Vicente J. Botet Escriba
8853a4cbdf ref #11174 - boost::condition_variable::timed_wait with predicate unexpectedly wakes up while should wait infinite. 2015-04-22 07:23:38 +02:00
Vicente J. Botet Escriba
6f53279b50 ref #11174 - boost::condition_variable::timed_wait with predicate unexpectedly wakes up while should wait infinite. 2015-04-18 19:26:07 +02:00
Vicente J. Botet Escriba
a741bd1bba Merge branch 'develop' into fix/make_executors_copyable 2015-04-18 07:11:12 +02:00
Vicente J. Botet Escriba
5c442e068c update compliance. 2015-04-18 07:10:43 +02:00
Vicente J. Botet Escriba
0bed674233 ref #11192- boost::future<>::then() with an executor doesn't compile when the callback returns a future 2015-04-18 07:04:50 +02:00
Vicente J. Botet Escriba
66193b0d38 Test with generic lambdas. 2015-04-17 18:31:34 +02:00
Vicente J. Botet Escriba
45c9a1d7fd ref #11192- boost::future<>::then() with an executor doesn't compile when the callback returns a future 2015-04-17 18:30:49 +02:00
Niall Douglas
0d8ddfe378 Merge pull request #51 from MarcelRaad/patch-1
Respect BOOST_USE_WINDOWS_H again
2015-04-03 17:11:17 +01:00
Vicente J. Botet Escriba
74f479d5c9 Fix non_copyable class in queue/dequeue view tests. 2015-03-29 19:34:28 +02:00
Vicente J. Botet Escriba
dbf793e7eb Don't execute test for launch::deferred if BOOST_THREAD_PROVIDES_VARIADIC_THREAD is not defined. 2015-03-29 19:28:05 +02:00
Marcel Raad
b5c6f760c5 Respect BOOST_USE_WINDOWS_H again
With the GetTickCount64 and WinRT fixes, the Windows API functions were always re-declared regardless of BOOST_USE_WINDOWS_H. This breaks clang-cl, which complains about conflicting definitions.
2015-03-26 13:43:44 +01:00
Vicente J. Botet Escriba
caaa7b4cc2 store executor by value. 2015-03-08 23:30:41 +01:00
Vicente J. Botet Escriba
9a05211faa fix issue with c++03 compilers. Pass Executors by const& instead of by &. 2015-03-04 07:59:27 +01:00
Vicente J. Botet Escriba
7ffcec448c uncomment more tests. 2015-03-03 19:29:50 +01:00
Vicente J. Botet Escriba
62bffed368 More fixes to make executor copyable. 2015-03-03 08:27:17 +01:00
Vicente J. Botet Escriba
5a1de7a722 ensure that generic executors are copyable. 2015-03-03 00:50:48 +01:00
Vicente J. Botet Escriba
33ee3445af refactor basic_thread_pool. It doesn't works yet for at_thread_exit. Needs to replace function<void(basic_thread_pool)>. 2015-03-01 18:00:58 +01:00
Vicente J. Botet Escriba
8511771816 Merge branch 'develop' into fix/make_executors_copyable 2015-03-01 01:40:23 +01:00
Vicente J. Botet Escriba
7dbd04197d Make scheduled_thread_pool copyable. 2015-02-28 19:01:45 +01:00
Vicente J. Botet Escriba
a53f31fb99 Store the Executor in scheduling_adaptor. This class must be finished as it doesn't make use of the executor :(. 2015-02-28 17:07:57 +01:00
Vicente J. Botet Escriba
b2b8684d0c make scheduled_thread_pool design closer to basic_thread_pool. 2015-02-28 17:04:17 +01:00
Vicente J. Botet Escriba
df14c8ac18 fix the move(w) on scheduler and store copies of the Executors. 2015-02-28 16:29:00 +01:00
Vicente J. Botet Escriba
6e5a46c16f merge from develop. 2015-02-28 15:32:18 +01:00
Vicente J. Botet Escriba
264ed4c308 move the work parameter. 2015-02-28 10:44:44 +01:00
Vicente J. Botet Escriba
65c4693c87 Add missing push(movable&&) and Run some failing tests that work when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined. 2015-02-28 10:41:20 +01:00
Vicente J. Botet Escriba
05d6eca09d Run some failing tests that work when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined. 2015-02-28 10:38:15 +01:00
Vicente J. Botet Escriba
c192777aef fix missing include in caller_context.hpp and let the possibility to dump function at compile time. 2015-02-28 09:53:09 +01:00
Vicente J. Botet Escriba
fdd1db970d cleanup work and store by value scheduler. 2015-02-28 09:06:57 +01:00
Vicente J. Botet Escriba
3bc5fb1725 fix a lot of things for c++11 compilers. There is yet a lot to do :( 2015-02-26 08:16:11 +01:00
Vicente J. Botet Escriba
9481562b5c update executors doc removing the move-only constraint. 2015-02-21 16:51:55 +01:00
Vicente J. Botet Escriba
e44b5309ae rename serial_executors to generic_serial_executors and let serial_executor be the template form. 2015-02-21 16:17:11 +01:00
Vicente J. Botet Escriba
eecf8f6c36 Allow polymorphic executors to be copiable. 2015-02-21 14:29:51 +01:00
Vicente J. Botet Escriba
532d215de9 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. 2015-02-21 12:26:40 +01:00
Vicente J. Botet Escriba
71bce54c71 fix serial_exeutor: reschedule_until and try_executing_one must return false, as a serial executor can not re-enter. 2015-02-21 12:25:29 +01:00
Vicente J. Botet Escriba
41bde57707 Make scheduler copyable. 2015-02-21 11:21:20 +01:00
Vicente J. Botet Escriba
ff7e394084 remove last sleep as now the tasks block the executors shared state lifetime as it is copied. 2015-02-21 11:20:42 +01:00
Vicente J. Botet Escriba
81f67eeb54 Change copyright date. 2015-02-21 11:18:08 +01:00
Vicente J. Botet Escriba
a4827a31f3 Change copyright date. 2015-02-21 11:16:19 +01:00
Vicente J. Botet Escriba
cd31e9c34f Make executor_adaptor copyable. 2015-02-21 01:00:12 +01:00
Vicente J. Botet Escriba
9492bcd485 Make serial_executor copyable. Replace generic_executor_ref by generic_executor. 2015-02-20 22:26:12 +01:00
Vicente J. Botet Escriba
ff9457e79c make basic_thread_pool copyable. 2015-02-20 20:47:30 +01:00
Vicente J. Botet Escriba
de580474a3 make inline_executor, loop_executor and thread_executor copyable. 2015-02-20 19:11:08 +01:00
61 changed files with 3406 additions and 898 deletions

View File

@@ -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 / 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) / file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -174,7 +174,7 @@ This has several advantages:
* The scheduled operations are available for all the executors via wrappers. * 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. * 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<Clock>` 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<Clock>` 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] [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. 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.
@@ -506,9 +506,6 @@ Executor abstract base class.
public: public:
typedef boost::work work; typedef boost::work work;
executor(executor const&) = delete;
executor& operator=(executor const&) = delete;
executor(); executor();
virtual ~executor() {}; virtual ~executor() {};
@@ -573,9 +570,6 @@ Polymorphic adaptor of a model of Executor to an executor.
public: public:
typedef executor::work work; typedef executor::work work;
executor_adaptor(executor_adaptor const&) = delete;
executor_adaptor& operator=(executor_adaptor const&) = delete;
template <typename ...Args> template <typename ...Args>
executor_adaptor(Args&& ... args); executor_adaptor(Args&& ... args);
@@ -641,20 +635,24 @@ Polymorphic adaptor of a model of Executor to an executor.
[/////////////////////////////////] [/////////////////////////////////]
[section:generic_executor_ref Class `generic_executor_ref`] [section:generic_executor_ref Class `generic_executor_ref`]
Executor abstract base class. Type erased executor class.
#include <boost/thread/generic_executor_ref.hpp> #include <boost/thread/generic_executor_ref.hpp>
namespace boost { namespace boost {
class generic_executor_ref class generic_executor_ref
{ {
public: public:
generic_executor_ref(generic_executor_ref const&);
generic_executor_ref& operator=(generic_executor_ref const&);
template <class Executor> template <class Executor>
generic_executor_ref(Executor& ex); 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; void close() = 0;
bool closed() = 0; bool closed() = 0;
@@ -669,6 +667,44 @@ Executor abstract base class.
[endsect] [endsect]
[/////////////////////////////////]
[section:generic_executor Class `generic_executor`]
Type erased executor class.
#include <boost/thread/generic_executor.hpp>
namespace boost {
class generic_executor
{
public:
template <class Executor>
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 <typename Closure>
void submit(Closure&& closure);
virtual bool try_executing_one() = 0;
template <typename Pred>
bool reschedule_until(Pred const& pred);
};
}
[endsect]
[//////////////////////////////////////////////////////////] [//////////////////////////////////////////////////////////]
[section: scheduler Template Class `scheduler `] [section: scheduler Template Class `scheduler `]
@@ -683,10 +719,7 @@ Scheduler providing time related functions. Note that `scheduler` is not an Exec
public: public:
using work = boost::function<void()> ; using work = boost::function<void()> ;
using clock = Clock; using clock = Clock;
scheduler(scheduler const&) = delete;
scheduler& operator=(scheduler const&) = delete;
scheduler(); scheduler();
~scheduler(); ~scheduler();
@@ -733,7 +766,7 @@ Scheduler providing time related functions. Note that `scheduler` is not an Exec
[[Effects:] [Destroys the scheduler.]] [[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.]]
] ]
@@ -1339,10 +1372,6 @@ A serial executor ensuring that there are no two work units that executes concur
class serial_executor class serial_executor
{ {
public: public:
serial_executor(serial_executor const&) = delete;
serial_executor& operator=(serial_executor const&) = delete;
template <class Executor>
serial_executor(Executor& ex); serial_executor(Executor& ex);
Executor& underlying_executor() noexcept; Executor& underlying_executor() noexcept;
@@ -1393,7 +1422,7 @@ A serial executor ensuring that there are no two work units that executes concur
[/////////////////////////////////////] [/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`] [section:underlying_executor Function member `underlying_executor()`]
generic_executor_ref& underlying_executor() noexcept; Executor& underlying_executor() noexcept;
[variablelist [variablelist
@@ -1418,13 +1447,10 @@ A serial executor ensuring that there are no two work units that executes concur
class generic_serial_executor class generic_serial_executor
{ {
public: public:
generic_serial_executor(generic_serial_executor const&) = delete;
generic_serial_executor& operator=(generic_serial_executor const&) = delete;
template <class Executor> template <class Executor>
generic_serial_executor(Executor& ex); generic_serial_executor(Executor& ex);
generic_executor_ref& underlying_executor() noexcept; generic_executor& underlying_executor() noexcept;
void close(); void close();
bool closed(); bool closed();
@@ -1433,6 +1459,7 @@ A serial executor ensuring that there are no two work units that executes concur
void submit(Closure&& closure); void submit(Closure&& closure);
bool try_executing_one(); bool try_executing_one();
template <typename Pred> template <typename Pred>
bool reschedule_until(Pred const& pred); bool reschedule_until(Pred const& pred);
@@ -1456,7 +1483,7 @@ A serial executor ensuring that there are no two work units that executes concur
[endsect] [endsect]
[/////////////////////////////////////] [/////////////////////////////////////]
[section:destructor Destructor `~serial_executor()`] [section:destructor Destructor `~generic_serial_executor()`]
~generic_serial_executor(); ~generic_serial_executor();
@@ -1468,6 +1495,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 <boost/thread/serial_executor_cont.hpp>
namespace boost {
template <class Executor>
class serial_executor_cont
{
public:
serial_executor_cont(Executor& ex);
Executor& underlying_executor() noexcept;
void close();
bool closed();
template <typename Closure>
void submit(Closure&& closure);
bool try_executing_one();
template <typename Pred>
bool reschedule_until(Pred const& pred);
};
}
[/////////////////////////////////////]
[section:constructor Constructor `serial_executor_cont(Executor&)`]
template <class Executor>
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] [endsect]
[/////////////////////////////////////] [/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`] [section:underlying_executor Function member `underlying_executor()`]
@@ -1478,6 +1579,8 @@ A serial executor ensuring that there are no two work units that executes concur
[[Return:] [The underlying executor instance. ]] [[Return:] [The underlying executor instance. ]]
[[Throws:] [Nothing.]]
] ]
@@ -1485,6 +1588,82 @@ A serial executor ensuring that there are no two work units that executes concur
[endsect] [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 <boost/thread/generic_serial_cont_executor.hpp>
namespace boost {
class generic_serial_cont_executor
{
public:
template <class Executor>
generic_serial_cont_executor(Executor& ex);
generic_executor& underlying_executor() noexcept;
void close();
bool closed();
template <typename Closure>
void submit(Closure&& closure);
bool try_executing_one();
template <typename Pred>
bool reschedule_until(Pred const& pred);
};
}
[/////////////////////////////////////]
[section:constructor Constructor `generic_serial_cont_executor(Executor&)`]
template <class Executor>
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`] [section:inline_executor Class `inline_executor`]
@@ -1496,10 +1675,8 @@ A serial executor ensuring that there are no two work units that executes concur
class inline_executor class inline_executor
{ {
public: public:
inline_executor(inline_executor const&) = delete;
inline_executor& operator=(inline_executor const&) = delete;
inline_executor(); inline_executor();
~inline_executor();
void close(); void close();
bool closed(); bool closed();
@@ -1560,9 +1737,6 @@ A thread pool with up to a fixed number of threads.
class basic_thread_pool class basic_thread_pool
{ {
public: 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()); basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency());
template <class AtThreadEntry> template <class AtThreadEntry>
@@ -1623,9 +1797,6 @@ A thread_executor with a threads for each task.
{ {
public: public:
thread_executor(thread_executor const&) = delete;
thread_executor& operator=(thread_executor const&) = delete;
thread_executor(); thread_executor();
template <class AtThreadEntry> template <class AtThreadEntry>
basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry); basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
@@ -1680,9 +1851,6 @@ A user scheduled executor.
class loop_executor class loop_executor
{ {
public: public:
loop_executor(loop_executor const&) = delete;
loop_executor& operator=(loop_executor const&) = delete;
loop_executor(); loop_executor();
~loop_executor(); ~loop_executor();

View File

@@ -1,13 +1,15 @@
[/ [/
(C) Copyright 2011-2013 Vicente J. Botet Escriba. (C) Copyright 2011-2015 Vicente J. Botet Escriba.
Distributed under the Boost Software License, Version 1.0. Distributed under the Boost Software License, Version 1.0.
(See accompanying file LICENSE_1_0.txt or copy at (See accompanying file LICENSE_1_0.txt or copy at
http://www.boost.org/LICENSE_1_0.txt). http://www.boost.org/LICENSE_1_0.txt).
] ]
[section:compliance Conformance and Extension] [section:compliance Conformance and Extension]
[////////////////////////////////////////////]
[section:cpp11 C++11 standard Thread library] [section:cpp11 C++11 standard Thread library]
[///////////////////////////////////////////]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3376.html C++11 - Standard for Programming Language C++]]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3376.html C++11 - Standard for Programming Language C++]]]
@@ -88,6 +90,7 @@
] ]
[endsect] [endsect]
[section:cxx14 C++14 standard Thread library - accepted changes] [section:cxx14 C++14 standard Thread library - accepted changes]
[//////////////////////////////////////////////////////////////]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.html Working Draft, Standard for Programming Language C++]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3797.html Working Draft, Standard for Programming Language C++]]
@@ -103,6 +106,7 @@
[section:cxx1y C++14 TS Extensions for Concurrency V1 ] [section:cxx1y C++14 TS Extensions for Concurrency V1 ]
[/////////////////////////////////////////////////////]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4107.html N4107-Extensions for Concurrency]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4107.html N4107-Extensions for Concurrency]]
@@ -126,8 +130,10 @@
[endsect] [endsect]
[section:cxx1y C++1z TS Concurrency - On going proposals] [section:cxx1y C++1z TS Concurrency - On going proposals]
[///////////////////////////////////////////////////////]
[section:latch C++ Latches and Barriers] [section:latch C++ Latches and Barriers]
[//////////////////////////////////////]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3600.html N3600 C++ Latches and Barriers]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3600.html N3600 C++ Latches and Barriers]]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3817.html N3817 C++ Latches and Barriers]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3817.html N3817 C++ Latches and Barriers]]
@@ -140,6 +146,7 @@
[endsect] [endsect]
[section:queue C++ Concurrent Queues] [section:queue C++ Concurrent Queues]
[///////////////////////////////////]
[note [@ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3533.html N3533 C++ Concurrent Queues]] [note [@ http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3533.html N3533 C++ Concurrent Queues]]
@@ -183,24 +190,25 @@
[[X.3.4] [Managed Indirection] [No] [ - ]] [[X.3.4] [Managed Indirection] [No] [ - ]]
] ]
[endsect] [endsect]
[section:executors Asynchronous Executors] [section:executors Executors and Schedulers]
[//////////////////////////////////////////]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3785.pdf N3785 Executors and Schedulers]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3785.pdf N3785 Executors and Schedulers]]
[table Asynchronous Executors [table Executors and Schedulers
[[Section] [Description] [Status] [Comments]] [[Section] [Description] [Status] [Comments]]
[[V.1.1] [Class executor] [Yes] [ - ]] [[V.1.1] [Class `executor`] [Yes] [ - ]]
[[V.1.1] [add] [Yes] [ renamed with a function template submit ]] [[V.1.1] [`add`] [Yes] [ renamed with a function template `submit` ]]
[[V.1.1] [num_of_pendin_closures] [No] [ ]] [[V.1.1] [`num_of_pendin_closures`] [No] [ ]]
[[V.1.2] [Class sceduled_executor] [No] [ - ]] [[V.1.2] [Class sceduled_executor] [No] [ - ]]
[[V.1.2] [add_at] [No] [ renamed with a function template submit_at ]] [[V.1.2] [`add_at`] [No] [ renamed with a function template `scheduler::submit_at` ]]
[[V.1.2] [add_after] [No] [ renamed with a function template submit_after ]] [[V.1.2] [`add_after`] [No] [ renamed with a function template `scheduler::submit_after` ]]
[[V.2] [Concrete executor classes] [No] [ - ]] [[V.2] [Concrete executor classes] [No] [ - ]]
[[V.2.1] [thread_pool] [Yes] [ static version Basic_thread_pool, dynamic one execduler_adaptor<basic_thread_pool> ]] [[V.2.1] [`thread_pool`] [Yes] [ static version `basic_thread_pool`, dynamic one `execduler_adaptor<basic_thread_pool>` ]]
[[V.2.2] [serial_executor] [yes] [ - ]] [[V.2.2] [`serial_executor`] [yes] [ - ]]
[[V.2.3] [loop_executor] [Yes] [ static version loop_scheduler, dynamic one execduler_adaptor<loop_scheduler> ]] [[V.2.3] [`loop_executor`] [Yes] [ static version loop_scheduler, dynamic one `execduler_adaptor<loop_scheduler>` ]]
[[V.2.4] [inline_executor] [Yes] [ static version inline_executor, dynamic one execduler_adaptor<inline_executor> ]] [[V.2.4] [`inline_executor`] [Yes] [ static version `inline_executor`, dynamic one `execduler_adaptor<inline_executor>` ]]
[[V.2.5] [thread_executor] [Yes] [ static version thread_executor, dynamic one execduler_adaptor<thread_executor> ]] [[V.2.5] [`thread_executor`] [Yes] [ static version `thread_executor`, dynamic one `execduler_adaptor<thread_executor>` ]]
] ]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3784.pdf N3784-Improvements to `std::future<T> and Related APIs]] [note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3784.pdf N3784-Improvements to `std::future<T> and Related APIs]]
@@ -212,8 +220,31 @@
[[30.6.8] [`async`] [Yes] [ - ]] [[30.6.8] [`async`] [Yes] [ - ]]
] ]
[note [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n4143.pdf N4143-Executors and schedulers, revision 4]]
[table Executors and Schedulers - revision 4
[[Section] [Description] [Status] [Comments]]
[[VI.A] [Executor Concept] [Yes] [ `wrapper_type` renamed by `work` and `spawn by `submit` ]]
[[VI.A.1] [`thread_per_task_executor] [Yes] [ renamed `thread_executor`]]
[[VI.A.2] [`thread_pool_executor`] [Yes] [ renamed `basic_thread_pool`]]
[[VI.A.3] [`system_executor`] [No] [ - ]]
[[VI.A.4] [`loop_executor`] [Yes] [ - ]]
[[VI.A.5] [`serial_executor`] [yes] [ - ]]
[[VI.B] [`executor_ref`] [yes] [ - ]]
[[VI.C] [`executor`] [yes] [ renamed `gen_executor_ref` ]]
[[VI.D] [Free Functions and Helper Objects] [partial] [ - ]]
[[VI.D] [`make_package`] [No] [ - ]]
[[VI.D] [`spawn_future`] [No] [ `async(Ex&, ...)` is similar but returns a blocking future. ]]
[[VI.D] [`spawn`] [No] [ - ]]
[[VI.D] [`task_wrapper`] [No] [ renamed `resubmitter` ]]
[[VI.D] [`set_executor`] [No] [ renamed `resubmit` ]]
[[VI.D] [`function_wrapper`] [Partial] [ renamed `work` ]]
]
[endsect] [endsect]
[//////////////////////////////////////////////////////////////
[section:stream_mutex C++ Stream Mutexes - C++ Stream Guards] [section:stream_mutex C++ Stream Mutexes - C++ Stream Guards]
[/////////////////////////////////////////////////////////////]
While Boost.Thread implementation of stream mutexes differ in the approach, it is worth comparing with the current trend on the standard. While Boost.Thread implementation of stream mutexes differ in the approach, it is worth comparing with the current trend on the standard.
@@ -241,7 +272,7 @@ While Boost.Thread implementation of stream mutexes differ in the approach, it i
[endsect] [endsect]
///////////////////////////////]
[endsect] [endsect]
[endsect] [endsect]

View File

@@ -7,7 +7,7 @@
[section:tutorial Tutorial] [section:tutorial Tutorial]
[@http://home.roadrunner.com/~hinnant/mutexes/locking.html Handling mutexes in C++] is an excellent tutorial. You need just replace std and ting by boost. [@http://web.archive.org/web/20140531071228/http://home.roadrunner.com/~hinnant/mutexes/locking.html Handling mutexes in C++] is an excellent tutorial. You need just replace std and ting by boost.
[@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html Mutex, Lock, Condition Variable Rationale] adds rationale for the design decisions made for mutexes, locks and condition variables. [@http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2406.html Mutex, Lock, Condition Variable Rationale] adds rationale for the design decisions made for mutexes, locks and condition variables.

View File

@@ -17,7 +17,7 @@
#include <boost/thread/caller_context.hpp> #include <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp> #include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/loop_executor.hpp> #include <boost/thread/executors/loop_executor.hpp>
#include <boost/thread/executors/serial_executor.hpp> #include <boost/thread/executors/generic_serial_executor.hpp>
#include <boost/thread/executors/inline_executor.hpp> #include <boost/thread/executors/inline_executor.hpp>
#include <boost/thread/executors/thread_executor.hpp> #include <boost/thread/executors/thread_executor.hpp>
#include <boost/thread/executors/executor.hpp> #include <boost/thread/executors/executor.hpp>
@@ -27,6 +27,12 @@
#include <boost/assert.hpp> #include <boost/assert.hpp>
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <cassert>
boost::future<void> p(boost::future<void> f) {
assert(f.is_ready());
return boost::make_ready_future();
}
void p1() void p1()
{ {
@@ -65,7 +71,7 @@ void submit_some(boost::executor& tp)
} }
void at_th_entry(boost::basic_thread_pool& ) void at_th_entry(boost::basic_thread_pool )
{ {
} }
@@ -76,6 +82,10 @@ int test_executor_adaptor()
{ {
try try
{ {
{
boost::basic_thread_pool e1;
boost::basic_thread_pool e2 = e1;
}
{ {
boost::executor_adaptor < boost::basic_thread_pool > ea(4); boost::executor_adaptor < boost::basic_thread_pool > ea(4);
submit_some( ea); submit_some( ea);
@@ -98,25 +108,65 @@ int test_executor_adaptor()
submit_some(ea); submit_some(ea);
} }
// std::cout << BOOST_CONTEXTOF << std::endl; // std::cout << BOOST_CONTEXTOF << std::endl;
{
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; boost::executor_adaptor < boost::loop_executor > ea2;
submit_some( ea2); submit_some( ea2);
ea2.underlying_executor().run_queued_closures(); ea2.underlying_executor().run_queued_closures();
} }
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
// std::cout << BOOST_CONTEXTOF << std::endl; // std::cout << BOOST_CONTEXTOF << std::endl;
{ {
boost::executor_adaptor < boost::basic_thread_pool > ea1(4); boost::basic_thread_pool tp;
boost::executor_adaptor < boost::serial_executor > ea2(ea1); 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::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); submit_some(ea2);
} }
#endif //#endif
// std::cout << BOOST_CONTEXTOF << std::endl; // 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; boost::executor_adaptor < boost::inline_executor > ea1;
submit_some(ea1); submit_some(ea1);
} }
// std::cout << BOOST_CONTEXTOF << std::endl; // std::cout << BOOST_CONTEXTOF << std::endl;
{
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; boost::executor_adaptor < boost::thread_executor > ea1;
submit_some(ea1); submit_some(ea1);
@@ -147,4 +197,19 @@ int test_executor_adaptor()
int main() int main()
{ {
return test_executor_adaptor(); return test_executor_adaptor();
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION \
&& defined BOOST_THREAD_PROVIDES_EXECUTORS \
&& ! defined BOOST_NO_CXX11_RVALUE_REFERENCES
boost::basic_thread_pool executor;
// compiles
boost::make_ready_future().then(&p);
// ??
boost::make_ready_future().then(executor, &p);
// doesn't compile
boost::make_ready_future().then(executor, &p);
#endif
} }

View File

@@ -0,0 +1,184 @@
// 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 <boost/config.hpp>
#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 <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/loop_executor.hpp>
#include <boost/thread/executors/generic_serial_executor.hpp>
#include <boost/thread/executors/serial_executor.hpp>
#include <boost/thread/executors/inline_executor.hpp>
#include <boost/thread/executors/thread_executor.hpp>
#include <boost/thread/executors/executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/executors/generic_executor.hpp>
#include <boost/thread/executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/assert.hpp>
#include <string>
#include <iostream>
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);
}
}
template < class Executor>
void submit_some2(Executor& tp)
{
for (int i = 0; i < 3; ++i) {
tp.submit(&p2);
}
for (int i = 0; i < 3; ++i) {
tp.submit(&p1);
}
}
template <class Executor>
void submit_some3(boost::serial_executor<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(boost::basic_thread_pool)
{
}
int test_generic_executor()
{
// std::cout << BOOST_CONTEXTOF << std::endl;
{
try
{
{
boost::basic_thread_pool ea(4);
submit_some( ea);
{
boost::future<int> t1 = boost::async(ea, &f1);
boost::future<int> 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<int> t1 = boost::async(ea3, &f1);
boost::future<int> t2 = boost::async(ea3, &f1);
//boost::future<int> t2 = boost::async(ea3, f2, 1); // todo this doesn't compiles yet on C++11
//boost::future<int> 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::generic_serial_executor ea2(ea1);
// submit_some(ea2);
// }
// std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::basic_thread_pool ea1(4);
boost::serial_executor<boost::basic_thread_pool> ea2(ea1);
submit_some3(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<int> 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();
}

View File

@@ -17,11 +17,12 @@
#include <boost/thread/caller_context.hpp> #include <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp> #include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/loop_executor.hpp> #include <boost/thread/executors/loop_executor.hpp>
#include <boost/thread/executors/serial_executor.hpp> #include <boost/thread/executors/generic_serial_executor.hpp>
#include <boost/thread/executors/inline_executor.hpp> #include <boost/thread/executors/inline_executor.hpp>
#include <boost/thread/executors/thread_executor.hpp> #include <boost/thread/executors/thread_executor.hpp>
#include <boost/thread/executors/executor.hpp> #include <boost/thread/executors/executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp> #include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/executors/generic_executor_ref.hpp>
#include <boost/thread/executor.hpp> #include <boost/thread/executor.hpp>
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/assert.hpp> #include <boost/assert.hpp>
@@ -64,7 +65,7 @@ void submit_some(boost::generic_executor_ref tp)
} }
void at_th_entry(boost::basic_thread_pool& ) void at_th_entry(boost::basic_thread_pool)
{ {
} }
@@ -108,7 +109,7 @@ int test_generic_executor_ref()
// std::cout << BOOST_CONTEXTOF << std::endl; // std::cout << BOOST_CONTEXTOF << std::endl;
{ {
boost::basic_thread_pool ea1(4); boost::basic_thread_pool ea1(4);
boost::serial_executor ea2(ea1); boost::generic_serial_executor ea2(ea1);
submit_some(ea2); submit_some(ea2);
} }
#endif #endif

View File

@@ -0,0 +1,117 @@
// 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 <boost/config.hpp>
#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 <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/generic_serial_executor.hpp>
#include <boost/thread/executors/executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/assert.hpp>
#include <string>
#include <iostream>
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;
{
boost::basic_thread_pool tp;
boost::generic_serial_executor e1(tp);
boost::generic_serial_executor e2 = e1;
}
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();
}

View File

@@ -0,0 +1,118 @@
// 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 <boost/config.hpp>
#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 <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/generic_serial_executor_cont.hpp>
#include <boost/thread/executors/executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/assert.hpp>
#include <string>
#include <iostream>
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;
{
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
{
#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();
}

View File

@@ -42,6 +42,14 @@ int main()
int result = f2.get(); int result = f2.get();
BOOST_THREAD_LOG << "f2 " << result << BOOST_THREAD_END_LOG; BOOST_THREAD_LOG << "f2 " << result << BOOST_THREAD_END_LOG;
} }
#if ! defined BOOST_NO_CXX14_GENERIC_LAMBDAS
{
boost::future<int> f1 = boost::async(boost::launch::async, []() {return 123;});
boost::future<int> f2 = f1.then([](auto f) {return 2*f.get(); });
int result = f2.get();
BOOST_THREAD_LOG << "f2 " << result << BOOST_THREAD_END_LOG;
}
#endif
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {

View File

@@ -94,6 +94,11 @@ int main()
f.get(); f.get();
} }
#endif #endif
{
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
boost::future<int> f = compute(-1);
f.wait();
}
{ {
std::cout << __FILE__ << " "<< __LINE__ << std::endl; std::cout << __FILE__ << " "<< __LINE__ << std::endl;
boost::future<int> f = compute(0); boost::future<int> f = compute(0);
@@ -124,11 +129,11 @@ int main()
boost::future<int const&> f = boost::make_ready_future(boost::cref(i)); boost::future<int const&> f = boost::make_ready_future(boost::cref(i));
std::cout << f.get() << std::endl; std::cout << f.get() << std::endl;
} }
// { {
// std::cout << __FILE__ << " "<< __LINE__ << std::endl; std::cout << __FILE__ << " "<< __LINE__ << std::endl;
// boost::future<int> f = compute(2); boost::future<int> f = compute(2);
// std::cout << f.get() << std::endl; std::cout << f.get() << std::endl;
// } }
{ {
std::cout << __FILE__ << " "<< __LINE__ << std::endl; std::cout << __FILE__ << " "<< __LINE__ << std::endl;
boost::shared_future<int> f = shared_compute(0); boost::shared_future<int> f = shared_compute(0);

View File

@@ -24,7 +24,7 @@
#endif #endif
#include <boost/thread/concurrent_queues/sync_queue.hpp> #include <boost/thread/concurrent_queues/sync_queue.hpp>
void producer(the_ostream &mos, boost::sync_queue<int> & sbq) void producer(the_ostream & /*mos*/, boost::sync_queue<int> & sbq)
{ {
using namespace boost; using namespace boost;
try { try {
@@ -32,22 +32,22 @@ void producer(the_ostream &mos, boost::sync_queue<int> & sbq)
{ {
sbq.push(i); sbq.push(i);
//sbq << i; //sbq << i;
mos << "push(" << i << ") "<< sbq.size()<<"\n"; //mos << "push(" << i << ") "<< sbq.size()<<"\n";
this_thread::sleep_for(chrono::milliseconds(200)); this_thread::sleep_for(chrono::milliseconds(200));
} }
} }
catch(sync_queue_is_closed&) catch(sync_queue_is_closed&)
{ {
mos << "closed !!!\n"; //mos << "closed !!!\n";
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }
void consumer( void consumer(
the_ostream &mos, the_ostream & /*mos*/,
boost::sync_queue<int> & sbq) boost::sync_queue<int> & sbq)
{ {
using namespace boost; using namespace boost;
@@ -57,21 +57,21 @@ void consumer(
int r; int r;
sbq.pull(r); sbq.pull(r);
//sbq >> r; //sbq >> r;
mos << i << " pull(" << r << ") "<< sbq.size()<<"\n"; //mos << i << " pull(" << r << ") "<< sbq.size()<<"\n";
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
} }
catch(sync_queue_is_closed&) catch(sync_queue_is_closed&)
{ {
mos << "closed !!!\n"; //mos << "closed !!!\n";
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }
void consumer2(the_ostream &mos, boost::sync_queue<int> & sbq) void consumer2(the_ostream &/*mos*/, boost::sync_queue<int> & sbq)
{ {
using namespace boost; using namespace boost;
try { try {
@@ -81,17 +81,17 @@ void consumer2(the_ostream &mos, boost::sync_queue<int> & sbq)
queue_op_status st = sbq.try_pull(r); queue_op_status st = sbq.try_pull(r);
if (queue_op_status::closed == st) break; if (queue_op_status::closed == st) break;
if (queue_op_status::success == st) { if (queue_op_status::success == st) {
mos << i << " pull(" << r << ")\n"; //mos << i << " pull(" << r << ")\n";
} }
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }
void consumer3(the_ostream &mos, boost::sync_queue<int> & sbq) void consumer3(the_ostream &/*mos*/, boost::sync_queue<int> & sbq)
{ {
using namespace boost; using namespace boost;
try { try {
@@ -100,13 +100,13 @@ void consumer3(the_ostream &mos, boost::sync_queue<int> & sbq)
int r; int r;
queue_op_status res = sbq.wait_pull(r); queue_op_status res = sbq.wait_pull(r);
if (res==queue_op_status::closed) break; if (res==queue_op_status::closed) break;
mos << i << " wait_pull(" << r << ")\n"; //mos << i << " wait_pull(" << r << ")\n";
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }

View File

@@ -27,7 +27,7 @@
#include <boost/static_assert.hpp> #include <boost/static_assert.hpp>
#include <boost/type_traits.hpp> #include <boost/type_traits.hpp>
void producer(the_ostream &mos, boost::queue_back<int> sbq) void producer(the_ostream &/*mos*/, boost::queue_back<int> sbq)
{ {
using namespace boost; using namespace boost;
try { try {
@@ -35,22 +35,22 @@ void producer(the_ostream &mos, boost::queue_back<int> sbq)
{ {
sbq.push(i); sbq.push(i);
//sbq << i; //sbq << i;
mos << "push(" << i << ") " << sbq.size() <<"\n"; //mos << "push(" << i << ") " << sbq.size() <<"\n";
this_thread::sleep_for(chrono::milliseconds(200)); this_thread::sleep_for(chrono::milliseconds(200));
} }
} }
catch(sync_queue_is_closed&) catch(sync_queue_is_closed&)
{ {
mos << "closed !!!\n"; //mos << "closed !!!\n";
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }
void consumer( void consumer(
the_ostream &mos, the_ostream &/*mos*/,
boost::queue_front<int> sbq) boost::queue_front<int> sbq)
{ {
using namespace boost; using namespace boost;
@@ -60,21 +60,21 @@ void consumer(
int r; int r;
sbq.pull(r); sbq.pull(r);
//sbq >> r; //sbq >> r;
mos << i << " pull(" << r << ") " << sbq.size() <<"\n"; //mos << i << " pull(" << r << ") " << sbq.size() <<"\n";
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
} }
catch(sync_queue_is_closed&) catch(sync_queue_is_closed&)
{ {
mos << "closed !!!\n"; //mos << "closed !!!\n";
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }
void consumer2(the_ostream &mos, boost::queue_front<int> sbq) void consumer2(the_ostream &/*mos*/, boost::queue_front<int> sbq)
{ {
using namespace boost; using namespace boost;
try { try {
@@ -84,17 +84,17 @@ void consumer2(the_ostream &mos, boost::queue_front<int> sbq)
queue_op_status st = sbq.try_pull(r); queue_op_status st = sbq.try_pull(r);
if (queue_op_status::closed == st) break; if (queue_op_status::closed == st) break;
if (queue_op_status::success == st) { if (queue_op_status::success == st) {
mos << i << " try_pull(" << r << ")\n"; //mos << i << " try_pull(" << r << ")\n";
} }
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }
void consumer3(the_ostream &mos, boost::queue_front<int> sbq) void consumer3(the_ostream &/*mos*/, boost::queue_front<int> sbq)
{ {
using namespace boost; using namespace boost;
try { try {
@@ -103,13 +103,13 @@ void consumer3(the_ostream &mos, boost::queue_front<int> sbq)
int r; int r;
queue_op_status res = sbq.wait_pull(r); queue_op_status res = sbq.wait_pull(r);
if (res==queue_op_status::closed) break; if (res==queue_op_status::closed) break;
mos << i << " wait_pull(" << r << ")\n"; //mos << i << " wait_pull(" << r << ")\n";
this_thread::sleep_for(chrono::milliseconds(250)); this_thread::sleep_for(chrono::milliseconds(250));
} }
} }
catch(...) catch(...)
{ {
mos << "exception !!!\n"; //mos << "exception !!!\n";
} }
} }

View File

@@ -32,7 +32,7 @@ counter c;
void change_count() void change_count()
{ {
std::cout << "count == " << c.increment() << std::endl; //std::cout << "count == " << c.increment() << std::endl;
} }
int main(int, char*[]) int main(int, char*[])

View File

@@ -27,16 +27,16 @@
void p1() void p1()
{ {
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(30)); boost::this_thread::sleep_for(boost::chrono::milliseconds(30));
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
} }
void p2() void p2()
{ {
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(10)); boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
} }
int f1() int f1()
@@ -51,10 +51,9 @@ int f2(int i)
boost::this_thread::sleep_for(boost::chrono::seconds(2)); boost::this_thread::sleep_for(boost::chrono::seconds(2));
return i + 1; return i + 1;
} }
template <class Executor>
void submit_some(boost::serial_executor& tp) void submit_some(boost::serial_executor<Executor>& tp)
{ {
std::cout << BOOST_CONTEXTOF << std::endl;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
std::cout << BOOST_CONTEXTOF << std::endl; std::cout << BOOST_CONTEXTOF << std::endl;
tp.submit(&p2); tp.submit(&p2);
@@ -63,7 +62,6 @@ void submit_some(boost::serial_executor& tp)
std::cout << BOOST_CONTEXTOF << std::endl; std::cout << BOOST_CONTEXTOF << std::endl;
tp.submit(&p1); tp.submit(&p1);
} }
std::cout << BOOST_CONTEXTOF << std::endl;
} }
@@ -75,21 +73,17 @@ void at_th_entry(boost::basic_thread_pool& )
int test_executor_adaptor() int test_executor_adaptor()
{ {
// std::cout << BOOST_CONTEXTOF << std::endl;
{ {
try try
{ {
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
// std::cout << BOOST_CONTEXTOF << std::endl;
{ {
boost::basic_thread_pool ea1(4); boost::basic_thread_pool ea1(4);
boost::serial_executor ea2(ea1); boost::serial_executor<boost::basic_thread_pool> ea2(ea1);
submit_some(ea2); submit_some(ea2);
boost::this_thread::sleep_for(boost::chrono::seconds(10));
} }
#endif #endif
// std::cout << BOOST_CONTEXTOF << std::endl;
} }
catch (std::exception& ex) catch (std::exception& ex)
{ {
@@ -102,7 +96,6 @@ int test_executor_adaptor()
return 2; return 2;
} }
} }
// std::cout << BOOST_CONTEXTOF << std::endl;
return 0; return 0;
} }

View File

@@ -52,18 +52,19 @@ int f2(int i)
return i + 1; return i + 1;
} }
void submit_some(boost::serial_executor_cont& tp) template < class Executor>
void submit_some(boost::serial_executor_cont<Executor>& tp)
{ {
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
tp.submit(&p2); tp.submit(&p2);
} }
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
tp.submit(&p1); tp.submit(&p1);
} }
std::cout << BOOST_CONTEXTOF << std::endl; //std::cout << BOOST_CONTEXTOF << std::endl;
} }
@@ -84,9 +85,8 @@ int test_executor_adaptor()
// std::cout << BOOST_CONTEXTOF << std::endl; // std::cout << BOOST_CONTEXTOF << std::endl;
{ {
boost::basic_thread_pool ea1(4); boost::basic_thread_pool ea1(4);
boost::serial_executor_cont ea2(ea1); boost::serial_executor_cont<boost::basic_thread_pool> ea2(ea1);
submit_some(ea2); submit_some(ea2);
boost::this_thread::sleep_for(boost::chrono::seconds(10));
} }
#endif #endif
// std::cout << BOOST_CONTEXTOF << std::endl; // std::cout << BOOST_CONTEXTOF << std::endl;

View File

@@ -59,6 +59,7 @@ int main()
threads.remove_thread(th); threads.remove_thread(th);
BOOST_TEST(! threads.is_thread_in(th)); BOOST_TEST(! threads.is_thread_in(th));
th->join(); th->join();
delete th;
} }
{ {
{ {

View File

@@ -173,7 +173,6 @@ namespace boost
private: private:
bool start_thread_noexcept(); bool start_thread_noexcept();
bool start_thread_noexcept(const attributes& attr); bool start_thread_noexcept(const attributes& attr);
//public:
void start_thread() void start_thread()
{ {
if (!start_thread_noexcept()) if (!start_thread_noexcept())

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -13,11 +13,16 @@
#include <boost/thread/detail/config.hpp> #include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp> #include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp> #include <boost/thread/detail/move.hpp>
#include <boost/thread/scoped_thread.hpp> #include <boost/thread/thread.hpp>
#include <boost/thread/concurrent_queues/sync_queue.hpp> #include <boost/thread/concurrent_queues/sync_queue.hpp>
#include <boost/thread/executors/work.hpp> #include <boost/thread/executors/work.hpp>
#include <boost/thread/csbl/vector.hpp> #include <boost/thread/csbl/vector.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/smart_ptr/enable_shared_from_this.hpp>
#include <boost/function.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
namespace boost namespace boost
@@ -30,125 +35,256 @@ namespace executors
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executors::work work; typedef executors::work work;
private: 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_t> thread_vector;
/// the thread safe work queue struct shared_state : enable_shared_from_this<shared_state> {
concurrent::sync_queue<work > work_queue; typedef executors::work work;
/// A move aware vector /// the kind of stored threads are scoped threads to ensure that the threads are joined.
thread_vector threads; /// A move aware vector type
//typedef scoped_thread<> thread_t;
typedef thread thread_t;
typedef csbl::vector<thread_t> thread_vector;
public: /// the thread safe work queue
/** concurrent::sync_queue<work > work_queue;
* Effects: try to execute one task. /// A move aware vector
* Returns: whether a task has been executed. thread_vector threads;
* Throws: whatever the current task constructor throws or the task() throws. unsigned const thread_count;
*/ boost::function<void(basic_thread_pool)> at_thread_entry;
bool try_executing_one() friend class basic_thread_pool;
{
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 main loop of the worker threads public:
*/ /**
void worker_thread() * Effects: try to execute one task.
{ * Returns: whether a task has been executed.
try * Throws: whatever the current task constructor throws or the task() throws.
*/
bool try_executing_one()
{ {
for(;;) try
{ {
work task; work task;
queue_op_status st = work_queue.wait_pull(task); if (work_queue.try_pull(task) == queue_op_status::success)
if (st == queue_op_status::closed) return; {
task(); 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(); if ( ! try_executing_one())
return; {
this_thread::yield();
}
} }
} private:
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <class AtThreadEntry>
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 <class AtThreadEntry>
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&) {}
/**
* The main loop of the worker threads
*/
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) break;
task();
}
}
catch (...)
{
std::terminate();
return;
}
}
static void do_nothing_at_thread_entry(basic_thread_pool) {}
void init()
{
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;
}
}
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)
: thread_count(thread_count),
at_thread_entry(do_nothing_at_thread_entry)
{
}
/**
* \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 <class AtThreadEntry>
shared_state( unsigned const thread_count, AtThreadEntry& at_thread_entry)
: thread_count(thread_count),
at_thread_entry(at_thread_entry)
{
}
#endif
shared_state( unsigned const thread_count, void(*at_thread_entry)(basic_thread_pool))
: thread_count(thread_count),
at_thread_entry(at_thread_entry)
{
}
template <class AtThreadEntry>
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))
{
}
/**
* \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();
join();
// 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)
{
if (this_thread::get_id() == threads[i].get_id()) continue;
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 <typename Closure>
void submit(Closure & closure)
{
work_queue.push(work(closure));
}
#endif
void submit(void (*closure)())
{
work_queue.push(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
work_queue.push(work(boost::forward<Closure>(closure)));
}
/**
* \b Requires: This must be called from an scheduled task.
*
* \b Effects: reschedule functions until pred()
*/
template <typename Pred>
bool reschedule_until(Pred const& pred)
{
do {
if ( ! try_executing_one())
{
return false;
}
} while (! pred());
return true;
}
};
/**
* \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<shared_state> ptr)
: pimpl(ptr)
{
}
public: 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 Effects: creates a thread pool that runs closures on \c thread_count threads.
* *
* \b Throws: Whatever exception is thrown while initializing the needed resources. * \b Throws: Whatever exception is thrown while initializing the needed resources.
*/ */
basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1) basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1)
: pimpl(make_shared<shared_state>(thread_count))
{ {
try pimpl->init();
{
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 * \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. . * and executes the at_thread_entry function at the entry of each created thread. .
@@ -158,61 +294,24 @@ namespace executors
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <class AtThreadEntry> template <class AtThreadEntry>
basic_thread_pool( unsigned const thread_count, AtThreadEntry& at_thread_entry) basic_thread_pool( unsigned const thread_count, AtThreadEntry& at_thread_entry)
: pimpl(make_shared<shared_state>(thread_count, at_thread_entry))
{ {
try pimpl->init();
{
threads.reserve(thread_count);
for (unsigned i = 0; i < thread_count; ++i)
{
thread th (&basic_thread_pool::worker_thread1<AtThreadEntry>, 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 #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)(basic_thread_pool))
: pimpl(make_shared<shared_state>(thread_count, at_thread_entry))
{ {
try pimpl->init();
{
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 <class AtThreadEntry> template <class AtThreadEntry>
basic_thread_pool( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry) basic_thread_pool( unsigned const thread_count, BOOST_THREAD_FWD_REF(AtThreadEntry) at_thread_entry)
: pimpl(make_shared<shared_state>(thread_count, boost::forward<AtThreadEntry>(at_thread_entry)))
{ {
try pimpl->init();
{
threads.reserve(thread_count);
for (unsigned i = 0; i < thread_count; ++i)
{
thread th (&basic_thread_pool::worker_thread3<AtThreadEntry>, this, boost::forward<AtThreadEntry>(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. * \b Effects: Destroys the thread pool.
* *
@@ -220,9 +319,16 @@ namespace executors
*/ */
~basic_thread_pool() ~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 +336,7 @@ namespace executors
*/ */
void join() void join()
{ {
for (unsigned i = 0; i < threads.size(); ++i) pimpl->join();
{
threads[i].join();
}
} }
/** /**
@@ -242,7 +345,7 @@ namespace executors
*/ */
void close() void close()
{ {
work_queue.close(); pimpl->close();
} }
/** /**
@@ -250,7 +353,7 @@ namespace executors
*/ */
bool closed() bool closed()
{ {
return work_queue.closed(); return pimpl->closed();
} }
/** /**
@@ -269,18 +372,18 @@ namespace executors
template <typename Closure> template <typename Closure>
void submit(Closure & closure) void submit(Closure & closure)
{ {
work_queue.push(work(closure)); pimpl->submit(closure);
} }
#endif #endif
void submit(void (*closure)()) void submit(void (*closure)())
{ {
work_queue.push(work(closure)); pimpl->submit(closure);
} }
template <typename Closure> template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure) void submit(BOOST_THREAD_RV_REF(Closure) closure)
{ {
work_queue.push(work(boost::forward<Closure>(closure))); pimpl->submit(boost::forward<Closure>(closure));
} }
/** /**
@@ -291,15 +394,16 @@ namespace executors
template <typename Pred> template <typename Pred>
bool reschedule_until(Pred const& pred) bool reschedule_until(Pred const& pred)
{ {
do { return pimpl->reschedule_until(pred);
if ( ! try_executing_one())
{
return false;
}
} while (! pred());
return true;
} }
void schedule_one_or_yield()
{
return pimpl->schedule_one_or_yield();
}
private:
shared_ptr<shared_state> pimpl;
}; };
} }
using executors::basic_thread_pool; using executors::basic_thread_pool;

View File

@@ -24,7 +24,6 @@ namespace detail
class priority_executor_base class priority_executor_base
{ {
public: public:
//typedef boost::function<void()> work;
typedef executors::work_pq work; typedef executors::work_pq work;
protected: protected:
typedef Queue queue_type; typedef Queue queue_type;

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -27,8 +27,6 @@ namespace boost
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executors::work work; typedef executors::work work;
/// executor is not copyable.
BOOST_THREAD_NO_COPYABLE(executor)
executor() {} executor() {}
/** /**
@@ -128,7 +126,6 @@ namespace boost
bool reschedule_until(Pred const& pred) bool reschedule_until(Pred const& pred)
{ {
do { do {
//schedule_one_or_yield();
if ( ! try_executing_one()) if ( ! try_executing_one())
{ {
return false; return false;

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -30,9 +30,8 @@ namespace executors
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executor::work work; typedef executor::work work;
/// executor is not copyable. // executor_adaptor(executor_adaptor const&) = default;
BOOST_THREAD_NO_COPYABLE(executor_adaptor) // executor_adaptor(executor_adaptor &&) = default;
/** /**
* executor_adaptor constructor * executor_adaptor constructor
*/ */

View File

@@ -0,0 +1,150 @@
// 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)
//
#ifndef BOOST_THREAD_EXECUTORS_GENERIC_EXECUTOR_HPP
#define BOOST_THREAD_EXECUTORS_GENERIC_EXECUTOR_HPP
#include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/type_traits/decay.hpp>
#include <boost/config/abi_prefix.hpp>
namespace boost
{
namespace executors
{
class generic_executor
{
shared_ptr<executor> ex;
public:
/// type-erasure to store the works to do
typedef executors::work work;
//generic_executor(generic_executor const&) = default;
//generic_executor(generic_executor &&) = default;
template<typename Executor>
generic_executor(Executor const& ex
, typename boost::disable_if<is_same<Executor, generic_executor>,
int* >::type = (int*)0
)
//: ex(make_shared<executor_adaptor<Executor> >(ex)) // todo check why this doesn't work with C++03
: ex( new executor_adaptor<Executor>(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<work>(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 <typename Closure>
void submit(Closure & closure)
{
work w ((closure));
submit(boost::move(w));
}
#endif
void submit(void (*closure)())
{
work w ((closure));
submit(boost::move(w));
}
template <typename Closure>
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 <typename Pred>
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 <boost/config/abi_suffix.hpp>
#endif

View File

@@ -13,7 +13,8 @@
#include <boost/thread/detail/move.hpp> #include <boost/thread/detail/move.hpp>
#include <boost/thread/executors/executor.hpp> #include <boost/thread/executors/executor.hpp>
#include <boost/shared_ptr.hpp> #include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
@@ -99,8 +100,8 @@ namespace boost
template<typename Executor> template<typename Executor>
generic_executor_ref(Executor& ex) generic_executor_ref(Executor& ex)
//: ex(make_shared<executor_ref<Executor> >(ex)) // todo check why this doesn't works with C++03 //: ex(make_shared<executor_ref<typename decay<Executor>::type> >(ex)) // todo check why this doesn't work with C++03
: ex( new executor_ref<Executor>(ex) ) : ex( new executor_ref<typename decay<Executor>::type>(ex) )
{ {
} }

View File

@@ -0,0 +1,284 @@
// 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 <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/thread/concurrent_queues/sync_queue.hpp>
#include <boost/thread/executors/work.hpp>
#include <boost/thread/executors/generic_executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/decay.hpp>
#include <boost/thread/caller_context.hpp>
#include <boost/config/abi_prefix.hpp>
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 > work_queue;
generic_executor ex;
thread_t thr;
struct try_executing_one_task {
work& task;
boost::promise<void> &p;
try_executing_one_task(work& task, boost::promise<void> &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<void> 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 <class Executor>
shared_state(Executor const& 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 <typename Closure>
void submit(Closure & closure)
{
work_queue.push(work(closure));
}
#endif
void submit(void (*closure)())
{
work_queue.push(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
work_queue.push(work(boost::forward<Closure>(closure)));
}
};
public:
// generic_serial_executor(generic_serial_executor const&) = default;
// generic_serial_executor(generic_serial_executor &&) = default;
/**
* \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 <class Executor>
generic_serial_executor(Executor const& ex
, typename boost::disable_if<is_same<Executor, generic_serial_executor>,
int* >::type = (int*)0)
: pimpl(make_shared<shared_state>(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 <typename Closure>
void submit(Closure & closure)
{
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
pimpl->submit(boost::forward<Closure>(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 <typename Pred>
bool reschedule_until(Pred const& pred)
{
return false;
}
private:
shared_ptr<shared_state> pimpl;
};
}
using executors::generic_serial_executor;
}
#include <boost/config/abi_suffix.hpp>
#endif

View File

@@ -0,0 +1,265 @@
// 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 <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/thread/executors/work.hpp>
#include <boost/thread/executors/generic_executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/decay.hpp>
#include <boost/config/abi_prefix.hpp>
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<void> fut_; // protected by mtx_
bool closed_; // protected by mtx_
mutex mtx_;
struct continuation {
work task;
template <class X>
struct result {
typedef void type;
};
continuation(BOOST_THREAD_RV_REF(work) tsk)
: task(boost::move(tsk)) {}
void operator()(future<void> f)
{
try {
task();
} catch (...) {
std::terminate();
}
}
};
bool closed(lock_guard<mutex>&) 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 <class Executor>
shared_state(Executor const& 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<mutex> lk(mtx_);
closed_ = true;;
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
lock_guard<mutex> 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 <typename Closure>
void submit(Closure & closure)
{
lock_guard<mutex> 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<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(closure)));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
lock_guard<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(boost::forward<Closure>(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 <class Executor>
generic_serial_executor_cont(Executor const& ex
, typename boost::disable_if<is_same<Executor, generic_serial_executor_cont>,
int* >::type = (int*)0)
: pimpl(make_shared<shared_state>(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 <typename Closure>
void submit(Closure & closure)
{
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
pimpl->submit(boost::forward<Closure>(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 <typename Pred>
bool reschedule_until(Pred const& pred)
{
return false;
}
private:
shared_ptr<shared_state> pimpl;
};
}
using executors::generic_serial_executor_cont;
}
#include <boost/config/abi_suffix.hpp>
#endif

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,6 +14,9 @@
#include <boost/thread/detail/move.hpp> #include <boost/thread/detail/move.hpp>
#include <boost/thread/executors/work.hpp> #include <boost/thread/executors/work.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
namespace boost namespace boost
@@ -25,142 +28,227 @@ namespace executors
public: public:
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executors::work work; typedef executors::work work;
bool closed_; private:
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;
}
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<mutex> lk(mtx_);
closed_ = true;
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed(lock_guard<mutex>& ) const
{
return closed_;
}
bool closed() const
{
lock_guard<mutex> 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 <typename Closure>
void submit(Closure & closure)
{
{
lock_guard<mutex> 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<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
}
try
{
closure();
}
catch (...)
{
std::terminate();
return;
}
}
template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
{
lock_guard<mutex> 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 <typename Pred>
bool reschedule_until(Pred const& )
{
return false;
}
};
public: 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<shared_state>())
{
}
/** /**
* \b Effects: creates a inline executor that runs closures immediately. * \b Effects: close the \c inline_executor for submissions.
* * The loop will work until there is no more closures to run.
* \b Throws: Nothing. */
*/ void close()
inline_executor() {
: closed_(false) pimpl->close();
{ }
}
/**
* \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. * \b Returns: whether the executor is closed for submissions.
* The loop will work until there is no more closures to run. */
*/ bool closed() const
void close() {
{ return pimpl->closed();
lock_guard<mutex> lk(mtx_); }
closed_ = true;
}
/** /**
* \b Returns: whether the pool is closed for submissions. * \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
*/ *
bool closed(lock_guard<mutex>& ) * \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.
return closed_; *
} * \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
bool closed() *
{ * \b Throws: \c sync_queue_is_closed if the thread pool is closed.
lock_guard<mutex> lk(mtx_); * Whatever exception that can be throw while storing the closure.
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) #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure> template <typename Closure>
void submit(Closure & closure) void submit(Closure & closure)
{ {
{ pimpl->submit(closure);
lock_guard<mutex> lk(mtx_); }
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
}
try
{
closure();
}
catch (...)
{
std::terminate();
return;
}
}
#endif #endif
void submit(void (*closure)()) void submit(void (*closure)())
{ {
{ pimpl->submit(closure);
lock_guard<mutex> lk(mtx_); }
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
}
try
{
closure();
}
catch (...)
{
std::terminate();
return;
}
}
template <typename Closure> template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure) void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{ {
{ pimpl->submit(boost::forward<Closure>(closure));
lock_guard<mutex> 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. * Effects: try to execute one task.
* * Returns: whether a task has been executed.
* \b Effects: reschedule functions until pred() * Throws: whatever the current task constructor throws or the task() throws.
*/ */
template <typename Pred> bool try_executing_one()
bool reschedule_until(Pred const& ) {
{ return pimpl->try_executing_one();
return false; }
}
/**
* \b Requires: This must be called from an scheduled task.
*
* \b Effects: reschedule functions until pred()
*/
template <typename Pred>
bool reschedule_until(Pred const& p)
{
return pimpl->reschedule_until(p);
}
private:
shared_ptr<shared_state> pimpl;
}; };
} }
using executors::inline_executor; using executors::inline_executor;

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -17,6 +17,9 @@
#include <boost/thread/concurrent_queues/sync_queue.hpp> #include <boost/thread/concurrent_queues/sync_queue.hpp>
#include <boost/thread/executors/work.hpp> #include <boost/thread/executors/work.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
namespace boost namespace boost
@@ -30,59 +33,170 @@ namespace executors
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executors::work work; typedef executors::work work;
private: private:
/// the thread safe work queue
concurrent::sync_queue<work > work_queue;
public: struct shared_state {
/** typedef executors::work work;
* Effects: try to execute one task. /// the thread safe work queue
* Returns: whether a task has been executed. concurrent::sync_queue<work > work_queue;
* Throws: whatever the current task constructor throws or the task() throws.
*/ /**
bool try_executing_one() * Effects: try to execute one task.
{ * Returns: whether a task has been executed.
work task; * Throws: whatever the current task constructor throws or the task() throws.
try */
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 <typename Closure>
void submit(Closure & closure)
{
work_queue.push(work(closure));
}
#endif
void submit(void (*closure)())
{
work_queue.push(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
work_queue.push(work(boost::forward<Closure>(closure)));
}
/**
* \b Requires: This must be called from an scheduled task.
*
* \b Effects: reschedule functions until pred()
*/
template <typename Pred>
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<work>::underlying_queue_type q = work_queue.underlying_queue();
while (! q.empty())
{
work& task = q.front();
task(); 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: 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 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. * \b Throws: Whatever exception is thrown while initializing the needed resources.
*/ */
loop_executor() loop_executor()
: pimpl(make_shared<shared_state>())
{ {
} }
/** /**
@@ -92,22 +206,33 @@ namespace executors
*/ */
~loop_executor() ~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 * The main loop of the worker thread
*/ */
void loop() void loop()
{ {
while (!closed()) pimpl->loop();
{
schedule_one_or_yield();
}
while (try_executing_one())
{
}
} }
/** /**
@@ -116,7 +241,7 @@ namespace executors
*/ */
void close() void close()
{ {
work_queue.close(); pimpl->close();
} }
/** /**
@@ -124,7 +249,7 @@ namespace executors
*/ */
bool closed() bool closed()
{ {
return work_queue.closed(); return pimpl->closed();
} }
/** /**
@@ -143,18 +268,18 @@ namespace executors
template <typename Closure> template <typename Closure>
void submit(Closure & closure) void submit(Closure & closure)
{ {
work_queue.push(work(closure)); pimpl->submit(closure);
} }
#endif #endif
void submit(void (*closure)()) void submit(void (*closure)())
{ {
work_queue.push(work(closure)); pimpl->submit(closure);
} }
template <typename Closure> template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure) void submit(BOOST_THREAD_RV_REF(Closure) closure)
{ {
work_queue.push(work(boost::forward<Closure>(closure))); pimpl->submit(boost::forward<Closure>(closure));
} }
/** /**
@@ -165,13 +290,7 @@ namespace executors
template <typename Pred> template <typename Pred>
bool reschedule_until(Pred const& pred) bool reschedule_until(Pred const& pred)
{ {
do { return pimpl->reschedule_until(pred);
if ( ! try_executing_one())
{
return false;
}
} while (! pred());
return true;
} }
/** /**
@@ -179,15 +298,10 @@ namespace executors
*/ */
void run_queued_closures() void run_queued_closures()
{ {
sync_queue<work>::underlying_queue_type q = work_queue.underlying_queue(); pimpl->run_queued_closures();
while (! q.empty())
{
work& task = q.front();
task();
q.pop_front();
}
} }
private:
shared_ptr<shared_state> pimpl;
}; };
} }
using executors::loop_executor; using executors::loop_executor;

View File

@@ -9,36 +9,123 @@
#define BOOST_THREAD_EXECUTORS_SCHEDULED_THREAD_POOL_HPP #define BOOST_THREAD_EXECUTORS_SCHEDULED_THREAD_POOL_HPP
#include <boost/thread/executors/detail/scheduled_executor_base.hpp> #include <boost/thread/executors/detail/scheduled_executor_base.hpp>
#include <boost/thread/executors/work.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/thread/csbl/vector.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
namespace boost namespace boost
{ {
namespace executors namespace executors
{ {
class scheduled_thread_pool : public detail::scheduled_executor_base<> template <class Clock = chrono::steady_clock>
class scheduled_thread_pool
{ {
private: private:
thread_group _workers;
public:
scheduled_thread_pool(size_t num_threads) : super() struct shared_state : public detail::scheduled_executor_base<> {
{
for(size_t i = 0; i < num_threads; i++) /// basic_thread_pool is not copyable.
BOOST_THREAD_NO_COPYABLE(shared_state)
typedef detail::scheduled_executor_base<> super;
typedef typename super::work work;
typedef scoped_thread<> thread_t;
typedef csbl::vector<thread_t> thread_vector;
thread_vector threads;
shared_state(unsigned const thread_count = thread::hardware_concurrency()+1) : super()
{ {
_workers.create_thread(bind(&super::loop, this));
try
{
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;
}
} }
/**
* \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()
{
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<shared_state>(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() ~scheduled_thread_pool()
{ {
this->close(); }
_workers.join_all(); /**
* \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; * \b Returns: whether the pool is closed for submissions.
}; //end class */
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<shared_state> pimpl;
};
} //end executors namespace } //end executors namespace
using executors::scheduled_thread_pool; using executors::scheduled_thread_pool;

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -13,6 +13,8 @@
#include <boost/chrono/time_point.hpp> #include <boost/chrono/time_point.hpp>
#include <boost/chrono/duration.hpp> #include <boost/chrono/duration.hpp>
#include <boost/chrono/system_clocks.hpp> #include <boost/chrono/system_clocks.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
@@ -36,7 +38,7 @@ namespace boost
} }
private: private:
Executor& ex; Executor ex;
Function funct; Function funct;
}; };
@@ -100,8 +102,8 @@ namespace boost
} }
private: private:
Scheduler& sch; Scheduler sch;
Executor& ex; Executor ex;
typename clock::time_point tp; typename clock::time_point tp;
bool is_closed; bool is_closed;
}; };
@@ -150,8 +152,8 @@ namespace boost
} }
private: private:
Scheduler& sch; Scheduler sch;
Executor& ex; Executor ex;
}; //end class }; //end class
/// Wraps a reference to a @c Scheduler providing an @c Executor that /// Wraps a reference to a @c Scheduler providing an @c Executor that
@@ -208,7 +210,7 @@ namespace boost
} }
private: private:
Scheduler& sch; Scheduler sch;
time_point tp; time_point tp;
bool is_closed; bool is_closed;
}; //end class }; //end class
@@ -217,22 +219,71 @@ namespace boost
/// It provides factory helper functions such as at/after that convert a @c Scheduler into an @c Executor /// 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. /// that submit the work at/after a specific time/duration respectively.
template <class Clock = chrono::steady_clock> template <class Clock = chrono::steady_clock>
class scheduler : public detail::scheduled_executor_base<Clock> class scheduler
{ {
public: private:
typedef typename detail::scheduled_executor_base<Clock>::work work;
struct shared_state : public detail::scheduled_executor_base<Clock> {
typedef detail::scheduled_executor_base<Clock> 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 Clock clock;
typedef typename clock::duration duration;
typedef typename clock::time_point time_point;
scheduler() scheduler()
: super(), : pimpl(make_shared<shared_state>())
thr(&super::loop, this) {} {}
~scheduler() ~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(boost::move(w), tp);
}
void submit_after(work w, const duration& d)
{
return pimpl->submit_after(boost::move(w), d);
}
template <class Ex> template <class Ex>
scheduler_executor_wrapper<scheduler, Ex> on(Ex& ex) scheduler_executor_wrapper<scheduler, Ex> on(Ex& ex)
{ {
@@ -250,13 +301,10 @@ namespace boost
{ {
return at_executor<scheduler>(*this, tp); return at_executor<scheduler>(*this, tp);
} }
private: private:
typedef detail::scheduled_executor_base<Clock> super; shared_ptr<shared_state> pimpl;
thread thr;
}; };
} }
using executors::resubmitter; using executors::resubmitter;
using executors::resubmit; using executors::resubmit;

View File

@@ -19,7 +19,7 @@ namespace executors
class scheduling_adpator : public detail::scheduled_executor_base<> class scheduling_adpator : public detail::scheduled_executor_base<>
{ {
private: private:
Executor& _exec; Executor _exec;
thread _scheduler; thread _scheduler;
public: public:

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,116 +14,168 @@
#include <boost/thread/detail/move.hpp> #include <boost/thread/detail/move.hpp>
#include <boost/thread/concurrent_queues/sync_queue.hpp> #include <boost/thread/concurrent_queues/sync_queue.hpp>
#include <boost/thread/executors/work.hpp> #include <boost/thread/executors/work.hpp>
#include <boost/thread/executors/generic_executor_ref.hpp> #include <boost/thread/executors/generic_executor.hpp>
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/thread/scoped_thread.hpp> #include <boost/thread/scoped_thread.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
namespace boost namespace boost
{ {
namespace executors namespace executors
{ {
template <class Executor>
class serial_executor class serial_executor
{ {
public: public:
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executors::work work; typedef executors::work work;
private: private:
typedef scoped_thread<> thread_t;
/// the thread safe work queue struct shared_state {
concurrent::sync_queue<work > work_queue; typedef executors::work work;
generic_executor_ref ex; typedef scoped_thread<> thread_t;
thread_t thr;
struct try_executing_one_task { /// the thread safe work queue
work& task; concurrent::sync_queue<work > work_queue;
boost::promise<void> &p; Executor ex;
try_executing_one_task(work& task, boost::promise<void> &p) thread_t thr;
: task(task), p(p) {}
void operator()() { struct try_executing_one_task {
try { work& task;
task(); boost::promise<void> &p;
p.set_value(); try_executing_one_task(work& task, boost::promise<void> &p)
} catch (...) : 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.
*/
Executor& underlying_executor() BOOST_NOEXCEPT { return ex; }
private:
/**
* The main loop of the worker thread
*/
void worker_thread()
{
try
{ {
p.set_exception(current_exception()); for(;;)
{
work task;
queue_op_status st = work_queue.wait_pull(task);
if (st == queue_op_status::closed) return;
boost::promise<void> 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.
*/
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 <typename Closure>
void submit(Closure & closure)
{
work_queue.push(work(closure));
}
#endif
void submit(void (*closure)())
{
work_queue.push(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
work_queue.push(work(boost::forward<Closure>(closure)));
}
}; };
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<void> 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: 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. * \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. * \b Throws: Whatever exception is thrown while initializing the needed resources.
*/ */
template <class Executor>
serial_executor(Executor& ex) serial_executor(Executor& ex)
: ex(ex), thr(&serial_executor::worker_thread, this) : pimpl(make_shared<shared_state>(ex))
{ {
} }
/** /**
@@ -133,8 +185,24 @@ namespace executors
*/ */
~serial_executor() ~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.
*/
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;
} }
/** /**
@@ -143,7 +211,7 @@ namespace executors
*/ */
void close() void close()
{ {
work_queue.close(); pimpl->close();
} }
/** /**
@@ -151,7 +219,7 @@ namespace executors
*/ */
bool closed() bool closed()
{ {
return work_queue.closed(); return pimpl->closed();
} }
/** /**
@@ -170,37 +238,31 @@ namespace executors
template <typename Closure> template <typename Closure>
void submit(Closure & closure) void submit(Closure & closure)
{ {
work_queue.push(work(closure)); pimpl->submit(closure);
} }
#endif #endif
void submit(void (*closure)()) void submit(void (*closure)())
{ {
work_queue.push(work(closure)); pimpl->submit(closure);
} }
template <typename Closure> template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure) void submit(BOOST_THREAD_RV_REF(Closure) closure)
{ {
work_queue.push(work(boost::forward<Closure>(closure))); pimpl->submit(boost::forward<Closure>(closure));
} }
/** /**
* \b Requires: This must be called from an scheduled task. * \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.
* \b Effects: reschedule functions until pred()
*/ */
template <typename Pred> template <typename Pred>
bool reschedule_until(Pred const& pred) bool reschedule_until(Pred const& pred)
{ {
do { return false;
if ( ! try_executing_one())
{
return false;
}
} while (! pred());
return true;
} }
private:
shared_ptr<shared_state> pimpl;
}; };
} }
using executors::serial_executor; using executors::serial_executor;

View File

@@ -12,18 +12,20 @@
#include <boost/thread/detail/config.hpp> #include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp> #include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp> #include <boost/thread/detail/move.hpp>
#include <boost/thread/concurrent_queues/sync_queue.hpp>
#include <boost/thread/executors/work.hpp> #include <boost/thread/executors/work.hpp>
#include <boost/thread/executors/generic_executor_ref.hpp>
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/thread/scoped_thread.hpp> #include <boost/thread/scoped_thread.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
namespace boost namespace boost
{ {
namespace executors namespace executors
{ {
template <class Executor>
class serial_executor_cont class serial_executor_cont
{ {
public: public:
@@ -31,93 +33,156 @@ namespace executors
typedef executors::work work; typedef executors::work work;
private: private:
generic_executor_ref ex_; struct shared_state {
future<void> fut_; // protected by mtx_ typedef executors::work work;
bool closed_; // protected by mtx_
mutex mtx_;
struct continuation { Executor ex_;
work task; future<void> fut_; // protected by mtx_
template <class X> bool closed_; // protected by mtx_
struct result { mutex mtx_;
typedef void type;
}; struct continuation {
continuation(BOOST_THREAD_RV_REF(work) tsk) work task;
: task(boost::move(tsk)) {} template <class X>
void operator()(future<void> f) struct result {
{ typedef void type;
try { };
task(); continuation(BOOST_THREAD_RV_REF(work) tsk)
} catch (...) { : task(boost::move(tsk)) {}
std::terminate(); void operator()(future<void> f)
{
try {
task();
} catch (...) {
std::terminate();
}
} }
};
bool closed(lock_guard<mutex>&) const
{
return closed_;
}
public:
/**
* \par Returns
* The underlying executor wrapped on a generic executor reference.
*/
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.
*/
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<mutex> lk(mtx_);
closed_ = true;;
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
lock_guard<mutex> 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.
*
*/
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
lock_guard<mutex> 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<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(closure)));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
lock_guard<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(boost::forward<Closure>(closure))));
} }
}; };
bool closed(lock_guard<mutex>&) const
{
return closed_;
}
public: public:
/** /**
* \par Returns * \b Effects: creates a thread pool that runs closures using one of its closure-executing methods.
* The underlying executor wrapped on a generic executor reference.
*/
generic_executor_ref& underlying_executor() BOOST_NOEXCEPT { return ex_; }
/// serial_executor_cont is not copyable.
BOOST_THREAD_NO_COPYABLE(serial_executor_cont)
/**
* \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 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 <class Executor>
serial_executor_cont(Executor& ex) serial_executor_cont(Executor& ex)
: ex_(ex), fut_(make_ready_future()), closed_(false) : pimpl(make_shared<shared_state>(ex))
{ {
} }
/** /**
* \b Effects: Destroys the thread pool. * \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. * \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor destructor.
*/ */
~serial_executor_cont() ~serial_executor_cont()
{ {
// signal to the worker thread that there will be no more submissions.
close();
} }
/** /**
* \b Effects: close the \c serial_executor_cont for submissions. * \par Returns
* The loop will work until there is no more closures to run. * The underlying executor wrapped on a generic executor reference.
*/ */
void close() Executor& underlying_executor() BOOST_NOEXCEPT
{ {
lock_guard<mutex> lk(mtx_); return pimpl->underlying_executor();
closed_ = true;;
} }
/** /**
* \b Returns: whether the pool is closed for submissions. * \b Returns: always false as a serial executor can not re-enter.
*/
bool closed()
{
lock_guard<mutex> lk(mtx_);
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. * Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks.
*/ */
bool try_executing_one() bool try_executing_one()
@@ -125,41 +190,64 @@ namespace executors
return false; 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 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. * \b Effects: The specified \c closure will be scheduled for execution at some point in the future.
* If the invoked closure throws an exception the \c serial_executor_cont will call \c std::terminate, as is the case with threads. * If invoked closure throws an exception the \c serial_executor will call \c std::terminate, as is the case with threads.
* *
* \b Throws: \c sync_queue_is_closed if the executor is closed. * \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. * Whatever exception that can be throw while storing the closure.
*
*/ */
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES) #if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure> template <typename Closure>
void submit(Closure & closure) void submit(Closure & closure)
{ {
lock_guard<mutex> lk(mtx_); pimpl->submit(closure);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(closure)));
} }
#endif #endif
void submit(void (*closure)()) void submit(void (*closure)())
{ {
lock_guard<mutex> lk(mtx_); pimpl->submit(closure);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(closure)));
} }
template <typename Closure> template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure) void submit(BOOST_THREAD_RV_REF(Closure) closure)
{ {
lock_guard<mutex> lk(mtx_); pimpl->submit(boost::forward<Closure>(closure));
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(boost::forward<Closure>(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 <typename Pred>
bool reschedule_until(Pred const& pred)
{
return false;
}
private:
shared_ptr<shared_state> pimpl;
}; };
} }
using executors::serial_executor_cont; using executors::serial_executor_cont;

View File

@@ -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 // 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) // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -18,6 +18,9 @@
#include <boost/thread/scoped_thread.hpp> #include <boost/thread/scoped_thread.hpp>
#include <boost/thread/csbl/vector.hpp> #include <boost/thread/csbl/vector.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp> #include <boost/config/abi_prefix.hpp>
namespace boost namespace boost
@@ -29,33 +32,134 @@ namespace executors
public: public:
/// type-erasure to store the works to do /// type-erasure to store the works to do
typedef executors::work work; typedef executors::work work;
bool closed_; private:
typedef scoped_thread<> thread_t;
typedef csbl::vector<thread_t> threads_type;
threads_type threads_;
mutable mutex mtx_;
/** struct shared_state {
* Effects: try to execute one task. typedef executors::work work;
* Returns: whether a task has been executed. bool closed_;
* Throws: whatever the current task constructor throws or the task() throws. typedef scoped_thread<> thread_t;
*/ typedef csbl::vector<thread_t> threads_type;
bool try_executing_one() threads_type threads_;
{ mutable mutex mtx_;
return false;
}
/// 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<mutex> lk(mtx_);
closed_ = true;
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed(lock_guard<mutex>& )
{
return closed_;
}
bool closed()
{
lock_guard<mutex> 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 <typename Closure>
void submit(Closure & closure)
{
lock_guard<mutex> 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<mutex> 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 <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
lock_guard<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
threads_.reserve(threads_.size() + 1);
thread th(boost::forward<Closure>(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 <typename Pred>
bool reschedule_until(Pred const&)
{
return false;
}
};
public: public:
/// thread_executor is not copyable.
BOOST_THREAD_NO_COPYABLE(thread_executor)
/** /**
* \b Effects: creates a inline executor that runs closures immediately. * \b Effects: creates a inline executor that runs closures immediately.
* *
* \b Throws: Nothing. * \b Throws: Nothing.
*/ */
thread_executor() thread_executor()
: closed_(false) : pimpl(make_shared<shared_state>())
{ {
} }
/** /**
@@ -65,9 +169,16 @@ namespace executors
*/ */
~thread_executor() ~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 +187,15 @@ namespace executors
*/ */
void close() void close()
{ {
lock_guard<mutex> lk(mtx_); pimpl->close();
closed_ = true;
} }
/** /**
* \b Returns: whether the pool is closed for submissions. * \b Returns: whether the pool is closed for submissions.
*/ */
bool closed(lock_guard<mutex>& )
{
return closed_;
}
bool closed() bool closed()
{ {
lock_guard<mutex> lk(mtx_); return pimpl->closed();
return closed(lk);
} }
/** /**
@@ -109,30 +214,18 @@ namespace executors
template <typename Closure> template <typename Closure>
void submit(Closure & closure) void submit(Closure & closure)
{ {
lock_guard<mutex> lk(mtx_); pimpl->submit(closure);
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 #endif
void submit(void (*closure)()) void submit(void (*closure)())
{ {
lock_guard<mutex> lk(mtx_); pimpl->submit(closure);
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 <typename Closure> template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure) void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{ {
lock_guard<mutex> lk(mtx_); pimpl->submit(boost::forward<Closure>(closure));
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
threads_.reserve(threads_.size() + 1);
thread th(boost::forward<Closure>(closure));
threads_.push_back(thread_t(boost::move(th)));
} }
/** /**
@@ -141,12 +234,14 @@ namespace executors
* \b Effects: reschedule functions until pred() * \b Effects: reschedule functions until pred()
*/ */
template <typename Pred> template <typename Pred>
bool reschedule_until(Pred const&) bool reschedule_until(Pred const& p)
{ {
return false; return pimpl->reschedule_until(p);
} }
private:
shared_ptr<shared_state> pimpl;
}; };
} }
using executors::thread_executor; using executors::thread_executor;
} }

View File

@@ -116,9 +116,9 @@ BOOST_THREAD_INLINE_NAMESPACE(v2)
template<typename F> template<typename F>
friend void task_region_final(BOOST_THREAD_FWD_REF(F) f); friend void task_region_final(BOOST_THREAD_FWD_REF(F) f);
template <class Ex, typename F> template <class Ex, typename F>
friend void task_region(Ex&, BOOST_THREAD_FWD_REF(F) f); friend void task_region(Ex const&, BOOST_THREAD_FWD_REF(F) f);
template<class Ex, typename F> template<class Ex, typename F>
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() void wait_all()
{ {
@@ -153,21 +153,20 @@ protected:
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS #if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS
task_region_handle_gen() task_region_handle_gen()
: canceled(false) : canceled(false)
, ex(0)
{} {}
task_region_handle_gen(Executor& ex) task_region_handle_gen(Executor const& ex)
: canceled(false) : canceled(false)
, ex(&ex) , ex(ex)
{} {}
#endif #endif
#if ! defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS #if ! defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS
task_region_handle_gen() task_region_handle_gen()
: ex(0) //: ex(0)
{} {}
task_region_handle_gen(Executor& ex) task_region_handle_gen(Executor const& ex)
: ex(&ex) : ex(ex)
{} {}
#endif #endif
@@ -188,7 +187,7 @@ protected:
bool canceled; bool canceled;
#endif #endif
#if defined BOOST_THREAD_PROVIDES_EXECUTORS #if defined BOOST_THREAD_PROVIDES_EXECUTORS
Executor* ex; Executor ex;
#endif #endif
exception_list exs; exception_list exs;
typedef csbl::vector<future<void> > group_type; typedef csbl::vector<future<void> > group_type;
@@ -211,13 +210,13 @@ protected:
} }
} }
#if defined BOOST_THREAD_PROVIDES_EXECUTORS #if defined BOOST_THREAD_PROVIDES_EXECUTORS
group.push_back(async(*ex, detail::wrapped<task_region_handle_gen<Executor>, F>(*this, forward<F>(f)))); group.push_back(async(ex, detail::wrapped<task_region_handle_gen<Executor>, F>(*this, forward<F>(f))));
#else #else
group.push_back(async(detail::wrapped<task_region_handle_gen<Executor>, F>(*this, forward<F>(f)))); group.push_back(async(detail::wrapped<task_region_handle_gen<Executor>, F>(*this, forward<F>(f))));
#endif #endif
#else #else
#if defined BOOST_THREAD_PROVIDES_EXECUTORS #if defined BOOST_THREAD_PROVIDES_EXECUTORS
group.push_back(async(*ex, forward<F>(f))); group.push_back(async(ex, forward<F>(f)));
#else #else
group.push_back(async(forward<F>(f))); group.push_back(async(forward<F>(f)));
#endif #endif
@@ -245,17 +244,18 @@ protected:
class task_region_handle : class task_region_handle :
public task_region_handle_gen<default_executor> public task_region_handle_gen<default_executor>
{ {
default_executor tp; //default_executor tp;
template <typename F> template <typename F>
friend void task_region(BOOST_THREAD_FWD_REF(F) f); friend void task_region(BOOST_THREAD_FWD_REF(F) f);
template<typename F> template<typename F>
friend void task_region_final(BOOST_THREAD_FWD_REF(F) f); friend void task_region_final(BOOST_THREAD_FWD_REF(F) f);
protected: protected:
task_region_handle() : task_region_handle_gen<default_executor>() task_region_handle()
: task_region_handle_gen<default_executor>()
{ {
#if defined BOOST_THREAD_PROVIDES_EXECUTORS #if defined BOOST_THREAD_PROVIDES_EXECUTORS
ex = &tp; //ex = &tp;
#endif #endif
} }
BOOST_DELETED_FUNCTION(task_region_handle(const task_region_handle&)) BOOST_DELETED_FUNCTION(task_region_handle(const task_region_handle&))
@@ -265,7 +265,7 @@ protected:
}; };
template <typename Executor, typename F> template <typename Executor, typename F>
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<Executor> tr(ex); task_region_handle_gen<Executor> tr(ex);
try try
@@ -280,7 +280,7 @@ protected:
} }
template <typename Executor, typename F> template <typename Executor, typename F>
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>(f)); task_region_final(ex, forward<F>(f));
} }

View File

@@ -137,7 +137,7 @@ namespace boost
continuations_type continuations; continuations_type continuations;
// This declaration should be only included conditionally, but is included to maintain the same layout. // This declaration should be only included conditionally, but is included to maintain the same layout.
virtual void launch_continuation(boost::unique_lock<boost::mutex>&, shared_ptr<shared_state_base>) virtual void launch_continuation()
{ {
} }
@@ -150,6 +150,19 @@ namespace boost
policy_(launch::none), policy_(launch::none),
continuations() continuations()
{} {}
shared_state_base(exceptional_ptr const& ex):
exception(ex.ptr_),
done(true),
is_valid_(true),
is_deferred_(false),
is_constructed(false),
cnt_(0),
policy_(launch::none),
continuations()
{}
virtual ~shared_state_base() virtual ~shared_state_base()
{ {
BOOST_ASSERT(cnt_==0); BOOST_ASSERT(cnt_==0);
@@ -221,8 +234,7 @@ namespace boost
continuations.clear(); continuations.clear();
relocker rlk(lock); relocker rlk(lock);
for (continuations_type::iterator it = the_continuations.begin(); it != the_continuations.end(); ++it) { for (continuations_type::iterator it = the_continuations.begin(); it != the_continuations.end(); ++it) {
boost::unique_lock<boost::mutex> cont_lock((*it)->mutex); (*it)->launch_continuation();
(*it)->launch_continuation(cont_lock, *it);
} }
} }
} }
@@ -407,7 +419,7 @@ namespace boost
return policy_; return policy_;
} }
future_state::state get_state(boost::unique_lock<boost::mutex>& lk) const future_state::state get_state(boost::unique_lock<boost::mutex>&) const
{ {
if(!done) if(!done)
{ {
@@ -483,6 +495,10 @@ namespace boost
shared_state(): shared_state():
result() result()
{} {}
shared_state(exceptional_ptr const& ex):
detail::shared_state_base(ex), result()
{}
~shared_state() ~shared_state()
{} {}
@@ -624,6 +640,10 @@ namespace boost
result(0) result(0)
{} {}
shared_state(exceptional_ptr const& ex):
detail::shared_state_base(ex), result(0)
{}
~shared_state() ~shared_state()
{ {
} }
@@ -687,6 +707,10 @@ namespace boost
shared_state() shared_state()
{} {}
shared_state(exceptional_ptr const& ex):
detail::shared_state_base(ex)
{}
void mark_finished_with_result_internal(boost::unique_lock<boost::mutex>& lock) void mark_finished_with_result_internal(boost::unique_lock<boost::mutex>& lock)
{ {
mark_finished_internal(lock); mark_finished_internal(lock);
@@ -1150,16 +1174,7 @@ namespace boost
static //BOOST_CONSTEXPR static //BOOST_CONSTEXPR
future_ptr make_exceptional_future_ptr(exceptional_ptr const& ex) { future_ptr make_exceptional_future_ptr(exceptional_ptr const& ex) {
promise<R> p; return future_ptr(new detail::shared_state<R>(ex));
p.set_exception(ex.ptr_);
return p.get_future().future_;
}
void set_exceptional_if_invalid() {
if (valid()) return;
promise<R> p;
p.set_exception(future_uninitialized());
future_ = p.get_future().future_;
} }
future_ptr future_; future_ptr future_;
@@ -1363,6 +1378,28 @@ namespace boost
template <class F, class Rp, class Fp> template <class F, class Rp, class Fp>
BOOST_THREAD_FUTURE<Rp> BOOST_THREAD_FUTURE<Rp>
make_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c); make_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename F, typename Rp, typename Fp>
BOOST_THREAD_FUTURE<Rp>
make_shared_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename F, typename Rp, typename Fp>
BOOST_THREAD_FUTURE<Rp>
make_shared_future_async_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
#ifdef BOOST_THREAD_PROVIDES_EXECUTORS
template<typename Ex, typename F, typename Rp, typename Fp>
BOOST_THREAD_FUTURE<Rp>
make_future_executor_continuation_shared_state(Ex& ex, boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename Ex, typename F, typename Rp, typename Fp>
BOOST_THREAD_FUTURE<Rp>
make_shared_future_executor_continuation_shared_state(Ex& ex, boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
template <class Rp, class Fp, class Executor>
BOOST_THREAD_FUTURE<Rp>
make_future_executor_shared_state(Executor& ex, BOOST_THREAD_FWD_REF(Fp) f);
#endif
#endif #endif
#if defined BOOST_THREAD_PROVIDES_FUTURE_UNWRAP #if defined BOOST_THREAD_PROVIDES_FUTURE_UNWRAP
template<typename F, typename Rp> template<typename F, typename Rp>
@@ -1372,6 +1409,36 @@ namespace boost
make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f); make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f);
#endif #endif
} }
#if defined(BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY)
template< typename InputIterator>
typename boost::disable_if<is_future_type<InputIterator>,
BOOST_THREAD_FUTURE<csbl::vector<typename InputIterator::value_type> >
>::type
when_all(InputIterator first, InputIterator last);
inline BOOST_THREAD_FUTURE<csbl::tuple<> > when_all();
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template< typename T0, typename ...T>
BOOST_THREAD_FUTURE<csbl::tuple<typename decay<T0>::type, typename decay<T>::type...> >
when_all(BOOST_THREAD_FWD_REF(T0) f, BOOST_THREAD_FWD_REF(T) ... futures);
#endif
template< typename InputIterator>
typename boost::disable_if<is_future_type<InputIterator>,
BOOST_THREAD_FUTURE<csbl::vector<typename InputIterator::value_type> >
>::type
when_any(InputIterator first, InputIterator last);
inline BOOST_THREAD_FUTURE<csbl::tuple<> > when_any();
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template< typename T0, typename ...T>
BOOST_THREAD_FUTURE<csbl::tuple<typename decay<T0>::type, typename decay<T>::type...> >
when_any(BOOST_THREAD_FWD_REF(T0) f, BOOST_THREAD_FWD_REF(T) ... futures);
#endif
#endif // BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY
template <typename R> template <typename R>
class BOOST_THREAD_FUTURE : public detail::basic_future<R> class BOOST_THREAD_FUTURE : public detail::basic_future<R>
@@ -1395,6 +1462,28 @@ namespace boost
template <class F, class Rp, class Fp> template <class F, class Rp, class Fp>
friend BOOST_THREAD_FUTURE<Rp> friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c); detail::make_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_shared_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_shared_future_async_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
#ifdef BOOST_THREAD_PROVIDES_EXECUTORS
template<typename Ex, typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_executor_continuation_shared_state(Ex& ex, boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename Ex, typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_shared_future_executor_continuation_shared_state(Ex& ex, boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
template <class Rp, class Fp, class Executor>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_executor_shared_state(Executor& ex, BOOST_THREAD_FWD_REF(Fp) f);
#endif
#endif #endif
#if defined BOOST_THREAD_PROVIDES_FUTURE_UNWRAP #if defined BOOST_THREAD_PROVIDES_FUTURE_UNWRAP
template<typename F, typename Rp> template<typename F, typename Rp>
@@ -1403,6 +1492,35 @@ namespace boost
friend BOOST_THREAD_FUTURE<Rp> friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f); detail::make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f);
#endif #endif
#if defined(BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY)
template< typename InputIterator>
friend typename boost::disable_if<is_future_type<InputIterator>,
BOOST_THREAD_FUTURE<csbl::vector<typename InputIterator::value_type> >
>::type
when_all(InputIterator first, InputIterator last);
//friend inline BOOST_THREAD_FUTURE<csbl::tuple<> > when_all();
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template< typename T0, typename ...T>
friend BOOST_THREAD_FUTURE<csbl::tuple<typename decay<T0>::type, typename decay<T>::type...> >
when_all(BOOST_THREAD_FWD_REF(T0) f, BOOST_THREAD_FWD_REF(T) ... futures);
#endif
template< typename InputIterator>
friend typename boost::disable_if<is_future_type<InputIterator>,
BOOST_THREAD_FUTURE<csbl::vector<typename InputIterator::value_type> >
>::type
when_any(InputIterator first, InputIterator last);
//friend inline BOOST_THREAD_FUTURE<csbl::tuple<> > when_any();
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template< typename T0, typename ...T>
friend BOOST_THREAD_FUTURE<csbl::tuple<typename decay<T0>::type, typename decay<T>::type...> >
when_any(BOOST_THREAD_FWD_REF(T0) f, BOOST_THREAD_FWD_REF(T) ... futures);
#endif
#endif // BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK
template <class> friend class packaged_task; // todo check if this works in windows template <class> friend class packaged_task; // todo check if this works in windows
#else #else
@@ -1418,9 +1536,7 @@ namespace boost
friend BOOST_THREAD_FUTURE<Rp> friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_deferred_shared_state(BOOST_THREAD_FWD_REF(Fp) f); detail::make_future_deferred_shared_state(BOOST_THREAD_FWD_REF(Fp) f);
typedef typename base_type::move_dest_type move_dest_type; typedef typename base_type::move_dest_type move_dest_type;
public: // when_all
BOOST_THREAD_FUTURE(future_ptr a_future): BOOST_THREAD_FUTURE(future_ptr a_future):
base_type(a_future) base_type(a_future)
@@ -1588,7 +1704,7 @@ namespace boost
friend class shared_future<R>; friend class shared_future<R>;
friend class promise<R>; friend class promise<R>;
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
template <typename, typename, typename> template <typename, typename, typename>
friend struct detail::future_async_continuation_shared_state; friend struct detail::future_async_continuation_shared_state;
template <typename, typename, typename> template <typename, typename, typename>
@@ -1601,7 +1717,30 @@ namespace boost
template <class F, class Rp, class Fp> template <class F, class Rp, class Fp>
friend BOOST_THREAD_FUTURE<Rp> friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c); detail::make_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c);
#endif
template<typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_shared_future_deferred_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_shared_future_async_continuation_shared_state(boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
#ifdef BOOST_THREAD_PROVIDES_EXECUTORS
template<typename Ex, typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_executor_continuation_shared_state(Ex& ex, boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f, BOOST_THREAD_FWD_REF(Fp) c);
template<typename Ex, typename F, typename Rp, typename Fp>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_shared_future_executor_continuation_shared_state(Ex& ex, boost::unique_lock<boost::mutex> &lock, F f, BOOST_THREAD_FWD_REF(Fp) c);
template <class Rp, class Fp, class Executor>
friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_executor_shared_state(Executor& ex, BOOST_THREAD_FWD_REF(Fp) f);
#endif
#endif
#if defined BOOST_THREAD_PROVIDES_FUTURE_UNWRAP #if defined BOOST_THREAD_PROVIDES_FUTURE_UNWRAP
template<typename F, typename Rp> template<typename F, typename Rp>
friend struct detail::future_unwrap_shared_state; friend struct detail::future_unwrap_shared_state;
@@ -1609,6 +1748,36 @@ namespace boost
friend BOOST_THREAD_FUTURE<Rp> friend BOOST_THREAD_FUTURE<Rp>
detail::make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f); detail::make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f);
#endif #endif
#if defined(BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY)
template< typename InputIterator>
friend typename boost::disable_if<is_future_type<InputIterator>,
BOOST_THREAD_FUTURE<csbl::vector<typename InputIterator::value_type> >
>::type
when_all(InputIterator first, InputIterator last);
friend inline BOOST_THREAD_FUTURE<csbl::tuple<> > when_all();
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template< typename T0, typename ...T>
friend BOOST_THREAD_FUTURE<csbl::tuple<typename decay<T0>::type, typename decay<T>::type...> >
when_all(BOOST_THREAD_FWD_REF(T0) f, BOOST_THREAD_FWD_REF(T) ... futures);
#endif
template< typename InputIterator>
friend typename boost::disable_if<is_future_type<InputIterator>,
BOOST_THREAD_FUTURE<csbl::vector<typename InputIterator::value_type> >
>::type
when_any(InputIterator first, InputIterator last);
friend inline BOOST_THREAD_FUTURE<csbl::tuple<> > when_any();
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template< typename T0, typename ...T>
friend BOOST_THREAD_FUTURE<csbl::tuple<typename decay<T0>::type, typename decay<T>::type...> >
when_any(BOOST_THREAD_FWD_REF(T0) f, BOOST_THREAD_FWD_REF(T) ... futures);
#endif
#endif // BOOST_THREAD_PROVIDES_FUTURE_WHEN_ALL_WHEN_ANY
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK
template <class> friend class packaged_task; // todo check if this works in windows template <class> friend class packaged_task; // todo check if this works in windows
#else #else
@@ -1630,8 +1799,8 @@ namespace boost
base_type(a_future) base_type(a_future)
{ {
} }
public: public:
BOOST_THREAD_MOVABLE_ONLY(BOOST_THREAD_FUTURE) BOOST_THREAD_MOVABLE_ONLY(BOOST_THREAD_FUTURE)
typedef future_state::state state; typedef future_state::state state;
typedef R value_type; // EXTENSION typedef R value_type; // EXTENSION
@@ -2713,7 +2882,7 @@ namespace boost
private: private:
task_shared_state(task_shared_state&); task_shared_state(task_shared_state&);
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
typedef R (*CallableType)(BOOST_THREAD_RV_REF(ArgTypes) ... ); typedef R (*CallableType)(ArgTypes ... );
#else #else
typedef R (*CallableType)(); typedef R (*CallableType)();
#endif #endif
@@ -2944,7 +3113,7 @@ namespace boost
private: private:
task_shared_state(task_shared_state&); task_shared_state(task_shared_state&);
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
typedef void (*CallableType)(BOOST_THREAD_RV_REF(ArgTypes)...); typedef void (*CallableType)(ArgTypes...);
#else #else
typedef void (*CallableType)(); typedef void (*CallableType)();
#endif #endif
@@ -3267,7 +3436,7 @@ namespace boost
// execution // execution
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD) #if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
void operator()(BOOST_THREAD_RV_REF(ArgTypes)... args) { void operator()(ArgTypes... args) {
if(!task) { if(!task) {
boost::throw_exception(task_moved()); boost::throw_exception(task_moved());
} }
@@ -3938,12 +4107,6 @@ namespace detail {
} }
#endif #endif
template <typename T>
BOOST_THREAD_FUTURE<T> make_ready_future(exception_ptr ex) {
promise<T> p;
p.set_exception(ex);
return BOOST_THREAD_MAKE_RV_REF(p.get_future());
}
template <typename T> template <typename T>
BOOST_THREAD_FUTURE<T> make_exceptional_future(exception_ptr ex) { BOOST_THREAD_FUTURE<T> make_exceptional_future(exception_ptr ex) {
@@ -3965,16 +4128,9 @@ namespace detail {
p.set_exception(boost::current_exception()); p.set_exception(boost::current_exception());
return BOOST_THREAD_MAKE_RV_REF(p.get_future()); return BOOST_THREAD_MAKE_RV_REF(p.get_future());
} }
template <typename T> template <typename T>
BOOST_THREAD_FUTURE<T> make_exceptional_future_if_invalid(BOOST_THREAD_FWD_REF(BOOST_THREAD_FUTURE<T>) fut) { BOOST_THREAD_FUTURE<T> make_ready_future(exception_ptr ex) {
fut.set_exceptional_if_invalid(); return make_exceptional_future<T>(ex);
return boost::move(fut);
}
template <typename T>
shared_future<T> make_exceptional_future_if_invalid(shared_future<T> fut) {
fut.set_exceptional_if_invalid();
return fut;
} }
#if 0 #if 0
@@ -4033,8 +4189,9 @@ namespace detail
} }
void launch_continuation(boost::unique_lock<boost::mutex>&, shared_ptr<shared_state_base> that) { void launch_continuation() {
this->thr_ = thread(&future_async_continuation_shared_state::run, that); boost::lock_guard<boost::mutex> lk(this->mutex);
this->thr_ = thread(&future_async_continuation_shared_state::run, this->shared_from_this());
} }
static void run(shared_ptr<boost::detail::shared_state_base> that_) { static void run(shared_ptr<boost::detail::shared_state_base> that_) {
@@ -4064,8 +4221,9 @@ namespace detail
centinel(parent.future_) { centinel(parent.future_) {
} }
void launch_continuation(boost::unique_lock<boost::mutex>&, shared_ptr<shared_state_base> that) { void launch_continuation() {
this->thr_ = thread(&future_async_continuation_shared_state::run, that); boost::lock_guard<boost::mutex> lk(this->mutex);
this->thr_ = thread(&future_async_continuation_shared_state::run, this->shared_from_this());
} }
static void run(shared_ptr<boost::detail::shared_state_base> that_) { static void run(shared_ptr<boost::detail::shared_state_base> that_) {
@@ -4115,9 +4273,8 @@ namespace detail
this->set_executor(); this->set_executor();
} }
void launch_continuation(boost::unique_lock<boost::mutex>& lck, shared_ptr<shared_state_base> that ) { void launch_continuation() {
relocker relock(lck); run_it<future_executor_continuation_shared_state> fct(this->shared_from_this());
run_it<future_executor_continuation_shared_state> fct(that);
ex->submit(fct); ex->submit(fct);
} }
@@ -4154,9 +4311,8 @@ namespace detail
this->set_executor(); this->set_executor();
} }
void launch_continuation(boost::unique_lock<boost::mutex>& lck, shared_ptr<shared_state_base> that ) { void launch_continuation() {
relocker relock(lck); run_it<future_executor_continuation_shared_state> fct(this->shared_from_this());
run_it<future_executor_continuation_shared_state> fct(that);
ex->submit(fct); ex->submit(fct);
} }
@@ -4197,8 +4353,9 @@ namespace detail
centinel(parent.future_) { centinel(parent.future_) {
} }
void launch_continuation(boost::unique_lock<boost::mutex>&, shared_ptr<shared_state_base> that) { void launch_continuation() {
this->thr_ = thread(&shared_future_async_continuation_shared_state::run, that); boost::lock_guard<boost::mutex> lk(this->mutex);
this->thr_ = thread(&shared_future_async_continuation_shared_state::run, this->shared_from_this());
} }
static void run(shared_ptr<boost::detail::shared_state_base> that_) { static void run(shared_ptr<boost::detail::shared_state_base> that_) {
@@ -4227,8 +4384,9 @@ namespace detail
centinel(parent.future_) { centinel(parent.future_) {
} }
void launch_continuation(boost::unique_lock<boost::mutex>&, shared_ptr<shared_state_base> that) { void launch_continuation() {
this->thr_ = thread(&shared_future_async_continuation_shared_state::run, that); boost::lock_guard<boost::mutex> lk(this->mutex);
this->thr_ = thread(&shared_future_async_continuation_shared_state::run, this->shared_from_this());
} }
static void run(shared_ptr<boost::detail::shared_state_base> that_) { static void run(shared_ptr<boost::detail::shared_state_base> that_) {
@@ -4265,9 +4423,8 @@ namespace detail
this->set_executor(); this->set_executor();
} }
void launch_continuation(boost::unique_lock<boost::mutex>& lck, shared_ptr<shared_state_base> that) { void launch_continuation() {
relocker relock(lck); run_it<shared_future_executor_continuation_shared_state> fct(this->shared_from_this());
run_it<shared_future_executor_continuation_shared_state> fct(that);
ex->submit(fct); ex->submit(fct);
} }
@@ -4303,9 +4460,8 @@ namespace detail
centinel(parent.future_) { centinel(parent.future_) {
} }
void launch_continuation(boost::unique_lock<boost::mutex>& lck, shared_ptr<shared_state_base> that) { void launch_continuation() {
relocker relock(lck); run_it<shared_future_executor_continuation_shared_state> fct(this->shared_from_this());
run_it<shared_future_executor_continuation_shared_state> fct(that);
ex->submit(fct); ex->submit(fct);
} }
@@ -4345,7 +4501,8 @@ namespace detail
this->set_deferred(); this->set_deferred();
} }
virtual void launch_continuation(boost::unique_lock<boost::mutex>&lk, shared_ptr<shared_state_base> ) { virtual void launch_continuation() {
boost::unique_lock<boost::mutex> lk(this->mutex);
if (this->is_deferred_) { if (this->is_deferred_) {
this->is_deferred_=false; this->is_deferred_=false;
this->execute(lk); this->execute(lk);
@@ -4383,7 +4540,8 @@ namespace detail
~future_deferred_continuation_shared_state() { ~future_deferred_continuation_shared_state() {
} }
virtual void launch_continuation(boost::unique_lock<boost::mutex>& lk, shared_ptr<shared_state_base> ) { virtual void launch_continuation() {
boost::unique_lock<boost::mutex> lk(this->mutex);
if (this->is_deferred_) { if (this->is_deferred_) {
this->is_deferred_=false; this->is_deferred_=false;
this->execute(lk); this->execute(lk);
@@ -4422,7 +4580,8 @@ namespace detail
this->set_deferred(); this->set_deferred();
} }
virtual void launch_continuation(boost::unique_lock<boost::mutex>& lk, shared_ptr<shared_state_base> ) { virtual void launch_continuation() {
boost::unique_lock<boost::mutex> lk(this->mutex);
if (this->is_deferred_) { if (this->is_deferred_) {
this->is_deferred_=false; this->is_deferred_=false;
this->execute(lk); this->execute(lk);
@@ -4458,7 +4617,8 @@ namespace detail
this->set_deferred(); this->set_deferred();
} }
virtual void launch_continuation(boost::unique_lock<boost::mutex>& lk, shared_ptr<shared_state_base> ) { virtual void launch_continuation() {
boost::unique_lock<boost::mutex> lk(this->mutex);
if (this->is_deferred_) { if (this->is_deferred_) {
this->is_deferred_=false; this->is_deferred_=false;
this->execute(lk); this->execute(lk);
@@ -4672,14 +4832,17 @@ namespace detail
boost::unique_lock<boost::mutex> lock(this->future_->mutex); boost::unique_lock<boost::mutex> lock(this->future_->mutex);
if (underlying_cast<int>(policy) & int(launch::async)) { if (underlying_cast<int>(policy) & int(launch::async)) {
lock.unlock();
return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>( return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>(
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
))); )));
} else if (underlying_cast<int>(policy) & int(launch::deferred)) { } else if (underlying_cast<int>(policy) & int(launch::deferred)) {
lock.unlock();
return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_deferred_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>( return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_deferred_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>(
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
))); )));
} else { } else {
lock.unlock();
return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>( return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>(
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
))); )));
@@ -4695,6 +4858,7 @@ namespace detail
BOOST_THREAD_ASSERT_PRECONDITION(this->future_!=0, future_uninitialized()); BOOST_THREAD_ASSERT_PRECONDITION(this->future_!=0, future_uninitialized());
boost::unique_lock<boost::mutex> lock(this->future_->mutex); boost::unique_lock<boost::mutex> lock(this->future_->mutex);
lock.unlock();
return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_executor_continuation_shared_state<Ex, BOOST_THREAD_FUTURE<R>, future_type, F>(ex, return BOOST_THREAD_MAKE_RV_REF((boost::detail::make_future_executor_continuation_shared_state<Ex, BOOST_THREAD_FUTURE<R>, future_type, F>(ex,
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
))); )));
@@ -4710,15 +4874,18 @@ namespace detail
boost::unique_lock<boost::mutex> lock(this->future_->mutex); boost::unique_lock<boost::mutex> lock(this->future_->mutex);
if (underlying_cast<int>(this->launch_policy(lock)) & int(launch::async)) { if (underlying_cast<int>(this->launch_policy(lock)) & int(launch::async)) {
lock.unlock();
return boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>( return boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>(
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
); );
} else if (underlying_cast<int>(this->launch_policy(lock)) & int(launch::deferred)) { } else if (underlying_cast<int>(this->launch_policy(lock)) & int(launch::deferred)) {
this->future_->wait_internal(lock); this->future_->wait_internal(lock);
lock.unlock();
return boost::detail::make_future_deferred_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>( return boost::detail::make_future_deferred_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>(
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
); );
} else { } else {
lock.unlock();
return boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>( return boost::detail::make_future_async_continuation_shared_state<BOOST_THREAD_FUTURE<R>, future_type, F>(
lock, boost::move(*this), boost::forward<F>(func) lock, boost::move(*this), boost::forward<F>(func)
); );
@@ -4859,41 +5026,87 @@ namespace detail
template<typename F, typename Rp> template<typename F, typename Rp>
struct future_unwrap_shared_state: shared_state<Rp> struct future_unwrap_shared_state: shared_state<Rp>
{ {
F parent; F wrapped;
typename F::value_type unwrapped;
public: public:
explicit future_unwrap_shared_state(BOOST_THREAD_RV_REF(F) f) explicit future_unwrap_shared_state(BOOST_THREAD_RV_REF(F) f)
: parent(boost::move(f)) {} : wrapped(boost::move(f)) {
typename F::value_type parent_value(boost::unique_lock<boost::mutex>& ) {
typename F::value_type r = parent.get();
r.set_exceptional_if_invalid();
return boost::move(r);
} }
virtual void wait(boost::unique_lock<boost::mutex>& lk, bool ) { // todo see if rethrow must be used void launch_continuation()
parent_value(lk).wait();
}
virtual Rp get(boost::unique_lock<boost::mutex>& lk) {
return parent_value(lk).get();
}
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
typedef shared_ptr<shared_state_base> continuation_ptr_type;
virtual void set_continuation_ptr(continuation_ptr_type continuation, boost::unique_lock<boost::mutex>& lock)
{ {
boost::unique_lock<boost::mutex> lk(parent.future_->mutex); boost::unique_lock<boost::mutex> lk(this->mutex);
parent.future_->set_continuation_ptr(continuation, lk); if (! unwrapped.valid() )
{
if (wrapped.has_exception()) {
this->mark_exceptional_finish_internal(wrapped.get_exception_ptr(), lk);
} else {
unwrapped = wrapped.get();
if (unwrapped.valid())
{
lk.unlock();
boost::unique_lock<boost::mutex> lk2(unwrapped.future_->mutex);
unwrapped.future_->set_continuation_ptr(this->shared_from_this(), lk2);
} else {
this->mark_exceptional_finish_internal(boost::copy_exception(future_uninitialized()), lk);
}
}
} else {
if (unwrapped.has_exception()) {
this->mark_exceptional_finish_internal(unwrapped.get_exception_ptr(), lk);
} else {
this->mark_finished_with_result_internal(unwrapped.get(), lk);
}
}
} }
#endif
}; };
template<typename F>
struct future_unwrap_shared_state<F,void>: shared_state<void>
{
F wrapped;
typename F::value_type unwrapped;
public:
explicit future_unwrap_shared_state(BOOST_THREAD_RV_REF(F) f)
: wrapped(boost::move(f)) {
}
void launch_continuation()
{
boost::unique_lock<boost::mutex> lk(this->mutex);
if (! unwrapped.valid() )
{
if (wrapped.has_exception()) {
this->mark_exceptional_finish_internal(wrapped.get_exception_ptr(), lk);
} else {
unwrapped = wrapped.get();
if (unwrapped.valid())
{
lk.unlock();
boost::unique_lock<boost::mutex> lk2(unwrapped.future_->mutex);
unwrapped.future_->set_continuation_ptr(this->shared_from_this(), lk2);
} else {
this->mark_exceptional_finish_internal(boost::copy_exception(future_uninitialized()), lk);
}
}
} else {
if (unwrapped.has_exception()) {
this->mark_exceptional_finish_internal(unwrapped.get_exception_ptr(), lk);
} else {
this->mark_finished_with_result_internal(lk);
}
}
}
};
template <class F, class Rp> template <class F, class Rp>
BOOST_THREAD_FUTURE<Rp> BOOST_THREAD_FUTURE<Rp>
make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f) { make_future_unwrap_shared_state(boost::unique_lock<boost::mutex> &lock, BOOST_THREAD_RV_REF(F) f) {
shared_ptr<future_unwrap_shared_state<F, Rp> > shared_ptr<future_unwrap_shared_state<F, Rp> >
h(new future_unwrap_shared_state<F, Rp>(boost::move(f))); h(new future_unwrap_shared_state<F, Rp>(boost::move(f)));
lock.lock(); lock.lock();
h->parent.future_->set_continuation_ptr(h, lock); h->wrapped.future_->set_continuation_ptr(h, lock);
lock.unlock(); lock.unlock();
return BOOST_THREAD_FUTURE<Rp>(h); return BOOST_THREAD_FUTURE<Rp>(h);
} }

View File

@@ -120,6 +120,15 @@ namespace boost
unique_lock<mutex>& m, unique_lock<mutex>& m,
duration_type const& wait_duration) duration_type const& wait_duration)
{ {
if (wait_duration.is_pos_infinity())
{
wait(m); // or do_wait(m,detail::timeout::sentinel());
return true;
}
if (wait_duration.is_special())
{
return true;
}
return timed_wait(m,get_system_time()+wait_duration); return timed_wait(m,get_system_time()+wait_duration);
} }
@@ -149,6 +158,18 @@ namespace boost
unique_lock<mutex>& m, unique_lock<mutex>& m,
duration_type const& wait_duration,predicate_type pred) duration_type const& wait_duration,predicate_type pred)
{ {
if (wait_duration.is_pos_infinity())
{
while (!pred())
{
wait(m); // or do_wait(m,detail::timeout::sentinel());
}
return true;
}
if (wait_duration.is_special())
{
return pred();
}
return timed_wait(m,get_system_time()+wait_duration,pred); return timed_wait(m,get_system_time()+wait_duration,pred);
} }
#endif #endif

View File

@@ -339,8 +339,8 @@ namespace boost
{ {
if (wait_duration.is_pos_infinity()) if (wait_duration.is_pos_infinity())
{ {
wait(m); // or do_wait(m,detail::timeout::sentinel()); wait(m); // or do_wait(m,detail::timeout::sentinel());
return true; return true;
} }
if (wait_duration.is_special()) if (wait_duration.is_special())
{ {
@@ -362,6 +362,18 @@ namespace boost
template<typename duration_type,typename predicate_type> template<typename duration_type,typename predicate_type>
bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration,predicate_type pred) bool timed_wait(unique_lock<mutex>& m,duration_type const& wait_duration,predicate_type pred)
{ {
if (wait_duration.is_pos_infinity())
{
while (!pred())
{
wait(m); // or do_wait(m,detail::timeout::sentinel());
}
return true;
}
if (wait_duration.is_special())
{
return pred();
}
return do_wait(m,wait_duration.total_milliseconds(),pred); return do_wait(m,wait_duration.total_milliseconds(),pred);
} }
#endif #endif

View File

@@ -35,6 +35,7 @@ namespace boost
typedef HANDLE handle; typedef HANDLE handle;
typedef SYSTEM_INFO system_info; typedef SYSTEM_INFO system_info;
typedef unsigned __int64 ticks_type; typedef unsigned __int64 ticks_type;
typedef FARPROC farproc_t;
unsigned const infinite=INFINITE; unsigned const infinite=INFINITE;
unsigned const timeout=WAIT_TIMEOUT; unsigned const timeout=WAIT_TIMEOUT;
handle const invalid_handle_value=INVALID_HANDLE_VALUE; handle const invalid_handle_value=INVALID_HANDLE_VALUE;
@@ -58,17 +59,20 @@ namespace boost
using ::CreateSemaphoreExW; using ::CreateSemaphoreExW;
# endif # endif
using ::OpenEventW; using ::OpenEventW;
using ::GetModuleGandleW;
# else # else
using ::CreateMutexA; using ::CreateMutexA;
using ::CreateEventA; using ::CreateEventA;
using ::OpenEventA; using ::OpenEventA;
using ::CreateSemaphoreA; using ::CreateSemaphoreA;
using ::GetModuleHandleA;
# endif # endif
#if BOOST_PLAT_WINDOWS_RUNTIME #if BOOST_PLAT_WINDOWS_RUNTIME
using ::GetNativeSystemInfo; using ::GetNativeSystemInfo;
using ::GetTickCount64; using ::GetTickCount64;
#else #else
using ::GetSystemInfo; using ::GetSystemInfo;
using ::GetTickCount;
#endif #endif
using ::CloseHandle; using ::CloseHandle;
using ::ReleaseMutex; using ::ReleaseMutex;
@@ -86,6 +90,7 @@ namespace boost
using ::SleepEx; using ::SleepEx;
using ::Sleep; using ::Sleep;
using ::QueueUserAPC; using ::QueueUserAPC;
using ::GetProcAddress;
#endif #endif
} }
} }
@@ -135,6 +140,7 @@ namespace boost
typedef void* handle; typedef void* handle;
typedef _SYSTEM_INFO system_info; typedef _SYSTEM_INFO system_info;
typedef unsigned __int64 ticks_type; typedef unsigned __int64 ticks_type;
typedef int (__stdcall *farproc_t)();
unsigned const infinite=~0U; unsigned const infinite=~0U;
unsigned const timeout=258U; unsigned const timeout=258U;
handle const invalid_handle_value=(handle)(-1); handle const invalid_handle_value=(handle)(-1);
@@ -160,17 +166,20 @@ namespace boost
__declspec(dllimport) void* __stdcall CreateSemaphoreExW(_SECURITY_ATTRIBUTES*,long,long,wchar_t const*,unsigned long,unsigned long); __declspec(dllimport) void* __stdcall CreateSemaphoreExW(_SECURITY_ATTRIBUTES*,long,long,wchar_t const*,unsigned long,unsigned long);
# endif # endif
__declspec(dllimport) void* __stdcall OpenEventW(unsigned long,int,wchar_t const*); __declspec(dllimport) void* __stdcall OpenEventW(unsigned long,int,wchar_t const*);
__declspec(dllimport) void* __stdcall GetModuleHandleW(wchar_t const*);
# else # else
__declspec(dllimport) void* __stdcall CreateMutexA(_SECURITY_ATTRIBUTES*,int,char const*); __declspec(dllimport) void* __stdcall CreateMutexA(_SECURITY_ATTRIBUTES*,int,char const*);
__declspec(dllimport) void* __stdcall CreateSemaphoreA(_SECURITY_ATTRIBUTES*,long,long,char const*); __declspec(dllimport) void* __stdcall CreateSemaphoreA(_SECURITY_ATTRIBUTES*,long,long,char const*);
__declspec(dllimport) void* __stdcall CreateEventA(_SECURITY_ATTRIBUTES*,int,int,char const*); __declspec(dllimport) void* __stdcall CreateEventA(_SECURITY_ATTRIBUTES*,int,int,char const*);
__declspec(dllimport) void* __stdcall OpenEventA(unsigned long,int,char const*); __declspec(dllimport) void* __stdcall OpenEventA(unsigned long,int,char const*);
__declspec(dllimport) void* __stdcall GetModuleHandleA(char const*);
# endif # endif
#if BOOST_PLAT_WINDOWS_RUNTIME #if BOOST_PLAT_WINDOWS_RUNTIME
__declspec(dllimport) void __stdcall GetNativeSystemInfo(_SYSTEM_INFO*); __declspec(dllimport) void __stdcall GetNativeSystemInfo(_SYSTEM_INFO*);
__declspec(dllimport) ticks_type __stdcall GetTickCount64(); __declspec(dllimport) ticks_type __stdcall GetTickCount64();
#else #else
__declspec(dllimport) void __stdcall GetSystemInfo(_SYSTEM_INFO*); __declspec(dllimport) void __stdcall GetSystemInfo(_SYSTEM_INFO*);
__declspec(dllimport) unsigned long __stdcall GetTickCount();
#endif #endif
__declspec(dllimport) int __stdcall CloseHandle(void*); __declspec(dllimport) int __stdcall CloseHandle(void*);
__declspec(dllimport) int __stdcall ReleaseMutex(void*); __declspec(dllimport) int __stdcall ReleaseMutex(void*);
@@ -183,6 +192,7 @@ namespace boost
__declspec(dllimport) void __stdcall Sleep(unsigned long); __declspec(dllimport) void __stdcall Sleep(unsigned long);
typedef void (__stdcall *queue_user_apc_callback_function)(ulong_ptr); typedef void (__stdcall *queue_user_apc_callback_function)(ulong_ptr);
__declspec(dllimport) unsigned long __stdcall QueueUserAPC(queue_user_apc_callback_function,void*,ulong_ptr); __declspec(dllimport) unsigned long __stdcall QueueUserAPC(queue_user_apc_callback_function,void*,ulong_ptr);
__declspec(dllimport) farproc_t __stdcall GetProcAddress(void *, const char *);
#endif #endif
# ifndef UNDER_CE # ifndef UNDER_CE
@@ -216,17 +226,10 @@ namespace boost
{ {
namespace win32 namespace win32
{ {
namespace detail { typedef int (__stdcall *farproc_t)(); typedef ticks_type (__stdcall *gettickcount64_t)(); } namespace detail { typedef ticks_type (__stdcall *gettickcount64_t)(); }
#if !BOOST_PLAT_WINDOWS_RUNTIME #if !BOOST_PLAT_WINDOWS_RUNTIME
extern "C" extern "C"
{ {
__declspec(dllimport) detail::farproc_t __stdcall GetProcAddress(void *, const char *);
#if !defined(BOOST_NO_ANSI_APIS)
__declspec(dllimport) void * __stdcall GetModuleHandleA(const char *);
#else
__declspec(dllimport) void * __stdcall GetModuleHandleW(const wchar_t *);
#endif
__declspec(dllimport) unsigned long __stdcall GetTickCount();
#ifdef _MSC_VER #ifdef _MSC_VER
long _InterlockedCompareExchange(long volatile *, long, long); long _InterlockedCompareExchange(long volatile *, long, long);
#pragma intrinsic(_InterlockedCompareExchange) #pragma intrinsic(_InterlockedCompareExchange)
@@ -285,6 +288,7 @@ namespace boost
// Oops, we weren't called often enough, we're stuck // Oops, we weren't called often enough, we're stuck
return 0xFFFFFFFF; return 0xFFFFFFFF;
} }
#else
#endif #endif
inline detail::gettickcount64_t GetTickCount64_() inline detail::gettickcount64_t GetTickCount64_()
{ {
@@ -297,7 +301,7 @@ namespace boost
#if BOOST_PLAT_WINDOWS_RUNTIME #if BOOST_PLAT_WINDOWS_RUNTIME
gettickcount64impl = &GetTickCount64; gettickcount64impl = &GetTickCount64;
#else #else
detail::farproc_t addr=GetProcAddress( farproc_t addr=GetProcAddress(
#if !defined(BOOST_NO_ANSI_APIS) #if !defined(BOOST_NO_ANSI_APIS)
GetModuleHandleA("KERNEL32.DLL"), GetModuleHandleA("KERNEL32.DLL"),
#else #else

View File

@@ -15,7 +15,9 @@
#include <pthread.h> #include <pthread.h>
#include <stdlib.h> #include <stdlib.h>
#include <memory> #include <memory>
#if defined BOOST_THREAD_PATCH
#include <string.h> // memcmp.
#endif
namespace boost namespace boost
{ {
namespace thread_detail namespace thread_detail

View File

@@ -80,6 +80,8 @@ namespace boost
static void tls_destructor(void* data) static void tls_destructor(void* data)
{ {
boost::detail::thread_data_base* thread_info=static_cast<boost::detail::thread_data_base*>(data); boost::detail::thread_data_base* thread_info=static_cast<boost::detail::thread_data_base*>(data);
//boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(data)->shared_from_this();
if(thread_info) if(thread_info)
{ {
while(!thread_info->tss_data.empty() || thread_info->thread_exit_callbacks) while(!thread_info->tss_data.empty() || thread_info->thread_exit_callbacks)
@@ -124,7 +126,8 @@ namespace boost
} }
~delete_current_thread_tls_key_on_dlclose_t() ~delete_current_thread_tls_key_on_dlclose_t()
{ {
if (current_thread_tls_init_flag.epoch!=BOOST_ONCE_INITIAL_FLAG_VALUE) const boost::once_flag uninitialized = BOOST_ONCE_INIT;
if (memcmp(&current_thread_tls_init_flag, &uninitialized, sizeof(boost::once_flag)))
{ {
pthread_key_delete(current_thread_tls_key); pthread_key_delete(current_thread_tls_key);
} }
@@ -158,8 +161,9 @@ namespace boost
{ {
static void* thread_proxy(void* param) static void* thread_proxy(void* param)
{ {
boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(param)->self; //boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(param)->self;
//thread_info->self.reset(); boost::detail::thread_data_ptr thread_info = static_cast<boost::detail::thread_data_base*>(param)->shared_from_this();
thread_info->self.reset();
detail::set_current_thread_data(thread_info.get()); detail::set_current_thread_data(thread_info.get());
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS #if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
BOOST_TRY BOOST_TRY
@@ -252,7 +256,6 @@ namespace boost
{ {
thread_info->self.reset(); thread_info->self.reset();
return false; return false;
// boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_create"));
} }
return true; return true;
} }
@@ -266,7 +269,6 @@ namespace boost
{ {
thread_info->self.reset(); thread_info->self.reset();
return false; return false;
// boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_create"));
} }
int detached_state; int detached_state;
res = pthread_attr_getdetachstate(h, &detached_state); res = pthread_attr_getdetachstate(h, &detached_state);
@@ -274,7 +276,6 @@ namespace boost
{ {
thread_info->self.reset(); thread_info->self.reset();
return false; return false;
// boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_attr_getdetachstate"));
} }
if (PTHREAD_CREATE_DETACHED==detached_state) if (PTHREAD_CREATE_DETACHED==detached_state)
{ {

View File

@@ -298,6 +298,7 @@ rule thread-compile ( sources : reqs * : name )
[ thread-compile test_10963.cpp : : test_10963_c ] [ thread-compile test_10963.cpp : : test_10963_c ]
[ thread-run test_10964.cpp ] [ thread-run test_10964.cpp ]
[ thread-test test_11053.cpp ] [ thread-test test_11053.cpp ]
[ thread-run test_11266.cpp ]
; ;
@@ -809,7 +810,10 @@ rule thread-compile ( sources : reqs * : name )
[ thread-run2 ../example/user_scheduler.cpp : ex_user_scheduler ] [ thread-run2 ../example/user_scheduler.cpp : ex_user_scheduler ]
[ thread-run2 ../example/executor.cpp : ex_executor ] [ thread-run2 ../example/executor.cpp : ex_executor ]
[ thread-run2 ../example/generic_executor_ref.cpp : ex_generic_executor_ref ] [ 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/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/serial_executor_cont.cpp : ex_serial_executor_cont ]
[ thread-run2 ../example/future_when_all.cpp : ex_future_when_all ] [ thread-run2 ../example/future_when_all.cpp : ex_future_when_all ]
[ thread-run2 ../example/parallel_accumulate.cpp : ex_parallel_accumulate ] [ thread-run2 ../example/parallel_accumulate.cpp : ex_parallel_accumulate ]
@@ -947,11 +951,18 @@ rule thread-compile ( sources : reqs * : name )
[ thread-run2-noit ./experimental/parallel/v2/task_region_pass.cpp : task_region_p ] [ thread-run2-noit ./experimental/parallel/v2/task_region_pass.cpp : task_region_p ]
; ;
explicit ts_ ; explicit ts_other ;
test-suite ts_ test-suite ts_other
: :
[ thread-run2 ../example/this_executor.cpp : ex_this_executor ] [ thread-run2 ../example/this_executor.cpp : ex_this_executor ]
[ thread-run2 ../example/default_executor.cpp : ex_default_executor ] [ thread-run2 ../example/default_executor.cpp : ex_default_executor ]
; ;
explicit ts_ ;
test-suite ts_
:
#[ thread-run test_11256.cpp ]
;
} }

View File

@@ -17,6 +17,7 @@
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/detail/lightweight_test.hpp> #include <boost/detail/lightweight_test.hpp>
#include <cassert>
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
@@ -31,6 +32,8 @@ int p1()
int p2(boost::future<int> f) int p2(boost::future<int> f)
{ {
assert(f.is_ready());
BOOST_THREAD_LOG << "p2 <" << &f << BOOST_THREAD_END_LOG; BOOST_THREAD_LOG << "p2 <" << &f << BOOST_THREAD_END_LOG;
BOOST_TEST(f.valid()); BOOST_TEST(f.valid());
int i = f.get(); int i = f.get();
@@ -41,6 +44,7 @@ int p2(boost::future<int> f)
void p3(boost::future<int> f) void p3(boost::future<int> f)
{ {
assert(f.is_ready());
BOOST_THREAD_LOG << "p3 <" << &f << BOOST_THREAD_END_LOG; BOOST_THREAD_LOG << "p3 <" << &f << BOOST_THREAD_END_LOG;
BOOST_TEST(f.valid()); BOOST_TEST(f.valid());
int i = f.get(); int i = f.get();

View File

@@ -20,6 +20,7 @@
#include <boost/thread/executors/basic_thread_pool.hpp> #include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executor.hpp> #include <boost/thread/executor.hpp>
#include <boost/detail/lightweight_test.hpp> #include <boost/detail/lightweight_test.hpp>
#include <cassert>
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION #if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
@@ -34,6 +35,7 @@ int p1()
int p2(boost::future<int> f) int p2(boost::future<int> f)
{ {
assert(f.is_ready());
BOOST_THREAD_LOG << "p2 <" << &f << BOOST_THREAD_END_LOG; BOOST_THREAD_LOG << "p2 <" << &f << BOOST_THREAD_END_LOG;
BOOST_TEST(f.valid()); BOOST_TEST(f.valid());
int i = f.get(); int i = f.get();
@@ -44,6 +46,7 @@ int p2(boost::future<int> f)
void p3(boost::future<int> f) void p3(boost::future<int> f)
{ {
assert(f.is_ready());
BOOST_THREAD_LOG << "p3 <" << &f << BOOST_THREAD_END_LOG; BOOST_THREAD_LOG << "p3 <" << &f << BOOST_THREAD_END_LOG;
BOOST_TEST(f.valid()); BOOST_TEST(f.valid());
int i = f.get(); int i = f.get();

View File

@@ -281,7 +281,7 @@ int main()
BOOST_TEST(res[1].is_ready()); BOOST_TEST(res[1].is_ready());
BOOST_TEST(res[1].get() == 321); BOOST_TEST(res[1].get() == 321);
} }
#if ! defined BOOST_NO_CXX11_DECLTYPE_N3276 #if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD
// fixme darwin-4.8.0_11 terminate called without an active exception // fixme darwin-4.8.0_11 terminate called without an active exception
{ // deferred future copy-constructible { // deferred future copy-constructible
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1); boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);

View File

@@ -152,7 +152,7 @@ int main()
BOOST_TEST(boost::csbl::get<0>(res).is_ready()); BOOST_TEST(boost::csbl::get<0>(res).is_ready());
BOOST_TEST(boost::csbl::get<0>(res).get() == 123); BOOST_TEST(boost::csbl::get<0>(res).get() == 123);
} }
#if ! defined BOOST_NO_CXX11_DECLTYPE_N3276 #if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD
// fixme darwin-4.8.0_11 terminate called without an active exception // fixme darwin-4.8.0_11 terminate called without an active exception
{ // deferred future copy-constructible { // deferred future copy-constructible
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1); boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);

View File

@@ -236,7 +236,7 @@ int main()
BOOST_TEST(boost::csbl::get<1>(res).is_ready()); BOOST_TEST(boost::csbl::get<1>(res).is_ready());
BOOST_TEST(boost::csbl::get<1>(res).get() == 321); BOOST_TEST(boost::csbl::get<1>(res).get() == 321);
} }
#if ! defined BOOST_NO_CXX11_DECLTYPE_N3276 #if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD
// fixme darwin-4.8.0_11 terminate called without an active exception // fixme darwin-4.8.0_11 terminate called without an active exception
{ // deferred future copy-constructible { // deferred future copy-constructible
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1); boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);

View File

@@ -283,7 +283,7 @@ int main()
BOOST_TEST(res[1].is_ready()); BOOST_TEST(res[1].is_ready());
BOOST_TEST(res[1].get() == 321); BOOST_TEST(res[1].get() == 321);
} }
#if ! defined BOOST_NO_CXX11_DECLTYPE_N3276 #if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD
// fixme darwin-4.8.0_11 terminate called without an active exception // fixme darwin-4.8.0_11 terminate called without an active exception
{ // deferred future copy-constructible { // deferred future copy-constructible
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1); boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);

View File

@@ -125,7 +125,7 @@ int main()
BOOST_TEST(boost::csbl::get<0>(res).is_ready()); BOOST_TEST(boost::csbl::get<0>(res).is_ready());
BOOST_TEST(boost::csbl::get<0>(res).get() == 123); BOOST_TEST(boost::csbl::get<0>(res).get() == 123);
} }
#if ! defined BOOST_NO_CXX11_DECLTYPE_N3276 #if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD
// fixme darwin-4.8.0_11 terminate called without an active exception // fixme darwin-4.8.0_11 terminate called without an active exception
{ // deferred future copy-constructible { // deferred future copy-constructible
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1); boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);

View File

@@ -225,7 +225,7 @@ int main()
BOOST_TEST(boost::csbl::get<0>(res).get() == 123); BOOST_TEST(boost::csbl::get<0>(res).get() == 123);
BOOST_TEST(boost::csbl::get<1>(res).get() == 321); BOOST_TEST(boost::csbl::get<1>(res).get() == 321);
} }
#if ! defined BOOST_NO_CXX11_DECLTYPE_N3276 #if defined BOOST_THREAD_PROVIDES_VARIADIC_THREAD
// fixme darwin-4.8.0_11 terminate called without an active exception // fixme darwin-4.8.0_11 terminate called without an active exception
{ // deferred future copy-constructible { // deferred future copy-constructible
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1); boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);

View File

@@ -21,9 +21,9 @@
class non_copyable class non_copyable
{ {
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
int val; int val;
public: public:
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
non_copyable(int v) : val(v){} non_copyable(int v) : val(v){}
non_copyable(BOOST_RV_REF(non_copyable) x): val(x.val) {} non_copyable(BOOST_RV_REF(non_copyable) x): val(x.val) {}
non_copyable& operator=(BOOST_RV_REF(non_copyable) x) { val=x.val; return *this; } non_copyable& operator=(BOOST_RV_REF(non_copyable) x) { val=x.val; return *this; }

View File

@@ -21,9 +21,9 @@
class non_copyable class non_copyable
{ {
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
int val; int val;
public: public:
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
non_copyable(int v) : val(v){} non_copyable(int v) : val(v){}
non_copyable(BOOST_RV_REF(non_copyable) x): val(x.val) {} non_copyable(BOOST_RV_REF(non_copyable) x): val(x.val) {}
non_copyable& operator=(BOOST_RV_REF(non_copyable) x) { val=x.val; return *this; } non_copyable& operator=(BOOST_RV_REF(non_copyable) x) { val=x.val; return *this; }

View File

@@ -29,7 +29,7 @@ int main()
boost::recursive_mutex::native_handle_type h = m.native_handle(); boost::recursive_mutex::native_handle_type h = m.native_handle();
BOOST_TEST(h); BOOST_TEST(h);
#else #else
#error "Test not applicable: BOOST_THREAD_DEFINES_CONDITION_VARIABLE_NATIVE_HANDLE not defined for this platform as not supported" #error "Test not applicable: BOOST_THREAD_DEFINES_RECURSIVE_MUTEX_NATIVE_HANDLE not defined for this platform as not supported"
#endif #endif
return boost::report_errors(); return boost::report_errors();

View File

@@ -8,9 +8,12 @@
#if ! defined BOOST_NO_CXX11_DECLTYPE #if ! defined BOOST_NO_CXX11_DECLTYPE
#define BOOST_RESULT_OF_USE_DECLTYPE #define BOOST_RESULT_OF_USE_DECLTYPE
#endif #endif
#define BOOST_THREAD_PROVIDES_EXECUTORS
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/static_assert.hpp> #include <boost/static_assert.hpp>
#include <cassert>
#include <boost/thread/executors/basic_thread_pool.hpp>
struct TestCallback struct TestCallback
@@ -19,12 +22,14 @@ struct TestCallback
result_type operator()(boost::future<void> future) const result_type operator()(boost::future<void> future) const
{ {
assert(future.is_ready());
future.get(); future.get();
return boost::make_ready_future(); return boost::make_ready_future();
} }
result_type operator()(boost::future<boost::future<void> > future) const result_type operator()(boost::future<boost::future<void> > future) const
{ {
assert(future.is_ready());
future.get(); future.get();
return boost::make_ready_future(); return boost::make_ready_future();
} }
@@ -33,12 +38,24 @@ struct TestCallback
int main() int main()
{ {
#if ! defined BOOST_NO_CXX11_DECLTYPE && ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS #if ! defined BOOST_NO_CXX11_DECLTYPE && ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS
{
boost::promise<void> test_promise; boost::promise<void> test_promise;
boost::future<void> test_future(test_promise.get_future()); boost::future<void> test_future(test_promise.get_future());
auto f1 = test_future.then(TestCallback()); auto f1 = test_future.then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
auto f2 = f1.then(TestCallback()); auto f2 = f1.then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<boost::future<void> > >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<boost::future<void> > >::value);
}
{
boost::basic_thread_pool executor;
boost::promise<void> test_promise;
boost::future<void> test_future(test_promise.get_future());
auto f1 = test_future.then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
auto f2 = f1.then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<boost::future<void> > >::value);
}
#endif #endif
return 0; return 0;
} }

View File

@@ -8,9 +8,12 @@
#if ! defined BOOST_NO_CXX11_DECLTYPE #if ! defined BOOST_NO_CXX11_DECLTYPE
#define BOOST_RESULT_OF_USE_DECLTYPE #define BOOST_RESULT_OF_USE_DECLTYPE
#endif #endif
#define BOOST_THREAD_PROVIDES_EXECUTORS
#include <boost/thread/future.hpp> #include <boost/thread/future.hpp>
#include <boost/static_assert.hpp> #include <boost/static_assert.hpp>
#include <cassert>
#include <boost/thread/executors/basic_thread_pool.hpp>
struct TestCallback struct TestCallback
{ {
@@ -18,13 +21,23 @@ struct TestCallback
result_type operator()(boost::future<void> future) const result_type operator()(boost::future<void> future) const
{ {
future.get(); std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
assert(future.is_ready());
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
future.wait();
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
return boost::make_ready_future(); return boost::make_ready_future();
} }
result_type operator()(boost::future<boost::future<void> > future) const result_type operator()(boost::future<boost::future<void> > future) const
{ {
future.get(); std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
assert(future.is_ready());
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
assert(future.get().is_ready());
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
//boost::future<void> ff = future.get();
return boost::make_ready_future(); return boost::make_ready_future();
} }
}; };
@@ -35,6 +48,8 @@ void p1()
int main() int main()
{ {
const int number_of_tests = 2;
#if ! defined BOOST_NO_CXX11_DECLTYPE && ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS #if ! defined BOOST_NO_CXX11_DECLTYPE && ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
{ {
@@ -43,34 +58,104 @@ int main()
f1.wait(); f1.wait();
} }
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{ {
auto f1 = boost::make_ready_future().then(TestCallback()); auto f1 = boost::make_ready_future().then(TestCallback());
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
auto f2 = f1.unwrap(); auto f2 = f1.unwrap();
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value);
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
f2.wait(); f2.wait();
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
} }
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
auto f1 = boost::make_ready_future().then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
boost::future<void> f2 = f1.get();
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
}
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{ {
auto f1 = boost::make_ready_future().then(TestCallback()); auto f1 = boost::make_ready_future().then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
auto f2 = f1.unwrap(); auto f2 = f1.unwrap();
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value);
auto f3 = f2.then(TestCallback()); auto f3 = f2.then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value); BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait(); f3.wait();
} }
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{ {
boost::make_ready_future().then( boost::make_ready_future().then(
TestCallback()).unwrap().then(TestCallback()).get(); TestCallback()).unwrap().then(TestCallback()).get();
} }
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl; std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{ {
boost::future<void> f = boost::async(p1); boost::future<void> f = boost::async(p1);
f.then( f.then(
TestCallback()).unwrap().then(TestCallback()).get(); TestCallback()).unwrap().then(TestCallback()).get();
} }
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{
auto f1 = boost::make_ready_future().then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
auto f3 = f1.then(TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait();
}
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{
boost::basic_thread_pool executor;
auto f1 = boost::make_ready_future().then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
auto f3 = f1.then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait();
}
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{
boost::basic_thread_pool executor(1);
auto f1 = boost::make_ready_future().then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
std::cout << __FILE__ << "[" << __LINE__ << "] " << int(f1.valid()) << std::endl;
auto f2 = f1.unwrap();
std::cout << __FILE__ << "[" << __LINE__ << "] " << int(f2.valid()) << std::endl;
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value);
auto f3 = f2.then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait();
}
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{
boost::basic_thread_pool executor;
auto f1 = boost::make_ready_future().then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
auto f2 = f1.unwrap();
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value);
auto f3 = f2.then(executor, TestCallback());
BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait();
}
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
#endif #endif
return 0; return 0;
} }

42
test/test_11256.cpp Normal file
View File

@@ -0,0 +1,42 @@
// Copyright (C) 2015 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)
#define BOOST_THREAD_VERSION 4
#define BOOST_THREAD_PROVIDES_EXECUTORS
#include <boost/thread.hpp>
#include <boost/thread/thread_pool.hpp>
#include <cassert>
auto createFuture()
{
boost::promise<void> promise;
promise.set_value();
return promise.get_future();
}
auto stepOne(boost::basic_thread_pool &executor)
{
auto sendFuture = createFuture();
auto wrappedFuture = sendFuture.then(executor, [](auto f) mutable {
return createFuture();
});
return wrappedFuture.unwrap();
}
auto stepTwo(boost::basic_thread_pool &executor)
{
auto future = stepOne(executor);
return future.then(executor, [](auto f) {
assert(f.is_ready());
});
}
int main()
{
boost::basic_thread_pool executor{1};
stepTwo(executor).get();
}

29
test/test_11266.cpp Normal file
View File

@@ -0,0 +1,29 @@
// Copyright (C) 2015 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)
#define BOOST_THREAD_VERSION 4
#include <boost/thread/future.hpp>
void func(int) { }
int main()
{
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
{
boost::packaged_task<void(int)> task{func};
}
{
boost::packaged_task<void(int)> task{func};
task(0);
}
{
boost::packaged_task<void(int)> task{func};
int x = 0;
task(x);
}
#endif
}

View File

@@ -24,7 +24,7 @@
using namespace boost::chrono; using namespace boost::chrono;
typedef boost::scheduled_thread_pool scheduled_tp; typedef boost::scheduled_thread_pool<> scheduled_tp;
void fn(int x) void fn(int x)
{ {
@@ -46,19 +46,18 @@ void func2(scheduled_tp* tp, steady_clock::duration d)
void test_timing(const int n) void test_timing(const int n)
{ {
//This function should take n seconds to execute. //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++) for(int i = 1; i <= n; i++)
{ {
se.submit_after(boost::bind(fn,i), milliseconds(i*100)); 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 //dtor is called here so all task will have to be executed before we return
} }
void test_deque_timing() void test_deque_timing()
{ {
boost::scheduled_thread_pool se(4); boost::scheduled_thread_pool<> se(4);
for(int i = 0; i < 10; i++) for(int i = 0; i < 10; i++)
{ {
steady_clock::duration d = milliseconds(i*100); steady_clock::duration d = milliseconds(i*100);
@@ -85,10 +84,10 @@ void test_deque_multi(const int n)
int main() int main()
{ {
steady_clock::time_point start = steady_clock::now(); //steady_clock::time_point start = steady_clock::now();
test_timing(5); test_timing(5);
steady_clock::duration diff = steady_clock::now() - start; //steady_clock::duration diff = steady_clock::now() - start;
BOOST_TEST(diff > milliseconds(500)); //BOOST_TEST(diff > milliseconds(500));
test_deque_timing(); test_deque_timing();
test_deque_multi(4); test_deque_multi(4);
test_deque_multi(8); test_deque_multi(8);

View File

@@ -27,7 +27,6 @@ typedef boost::executors::basic_thread_pool thread_pool;
void fn(int x) void fn(int x)
{ {
//std::cout << "[" << __LINE__ << "] " << steady_clock::now() << std::endl;
std::cout << x << std::endl; std::cout << x << std::endl;
} }
@@ -75,7 +74,7 @@ int main()
test_after(5, sch); test_after(5, sch);
test_at(5, sch); test_at(5, sch);
test_on(5, sch, tp); test_on(5, sch, tp);
boost::this_thread::sleep_for(boost::chrono::seconds(10)); std::cout << "[" << __LINE__ << "] " << std::endl;
return boost::report_errors(); return boost::report_errors();
} }

View File

@@ -28,7 +28,6 @@ typedef boost::executors::basic_thread_pool thread_pool;
void fn(int x) void fn(int x)
{ {
//std::cout << "[" << __LINE__ << "] " << steady_clock::now() << std::endl;
std::cout << x << 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),seconds(i));
sa.submit_after(boost::bind(fn,i), milliseconds(i*100)); sa.submit_after(boost::bind(fn,i), milliseconds(i*100));
} }
boost::this_thread::sleep_for(boost::chrono::seconds(10));
} }
int main() int main()
{ {
steady_clock::time_point start = steady_clock::now();
test_timing(5); test_timing(5);
steady_clock::duration diff = steady_clock::now() - start;
BOOST_TEST(diff > seconds(5));
return boost::report_errors(); return boost::report_errors();
} }

View File

@@ -26,9 +26,9 @@
unsigned throw_one = 0xFFFF; unsigned throw_one = 0xFFFF;
#if defined _GLIBCXX_THROW #if defined _GLIBCXX_THROW
void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc) inline void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc)
#elif defined BOOST_MSVC #elif defined BOOST_MSVC
void* operator new(std::size_t s) inline void* operator new(std::size_t s)
#else #else
void* operator new(std::size_t s) throw (std::bad_alloc) void* operator new(std::size_t s) throw (std::bad_alloc)
#endif #endif
@@ -40,9 +40,9 @@ void* operator new(std::size_t s) throw (std::bad_alloc)
} }
#if defined BOOST_MSVC #if defined BOOST_MSVC
void operator delete(void* p) inline void operator delete(void* p)
#else #else
void operator delete(void* p) throw () inline void operator delete(void* p) throw ()
#endif #endif
{ {
std::cout << __FILE__ << ":" << __LINE__ << std::endl; std::cout << __FILE__ << ":" << __LINE__ << std::endl;

View File

@@ -28,9 +28,9 @@
unsigned throw_one = 0xFFFF; unsigned throw_one = 0xFFFF;
#if defined _GLIBCXX_THROW #if defined _GLIBCXX_THROW
void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc) inline void* operator new(std::size_t s) _GLIBCXX_THROW (std::bad_alloc)
#elif defined BOOST_MSVC #elif defined BOOST_MSVC
void* operator new(std::size_t s) inline void* operator new(std::size_t s)
#else #else
void* operator new(std::size_t s) throw (std::bad_alloc) void* operator new(std::size_t s) throw (std::bad_alloc)
#endif #endif
@@ -41,9 +41,9 @@ void* operator new(std::size_t s) throw (std::bad_alloc)
} }
#if defined BOOST_MSVC #if defined BOOST_MSVC
void operator delete(void* p) inline void operator delete(void* p)
#else #else
void operator delete(void* p) throw () inline void operator delete(void* p) throw ()
#endif #endif
{ {
std::free(p); std::free(p);