mirror of
https://github.com/boostorg/thread.git
synced 2026-02-05 22:22:11 +00:00
Compare commits
164 Commits
fix/10967_
...
fix/make_e
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bde5a1ef55 | ||
|
|
4d45da6c06 | ||
|
|
e39703ff18 | ||
|
|
aafd5ca726 | ||
|
|
9b69912801 | ||
|
|
259c08473d | ||
|
|
6ca3a99cbc | ||
|
|
3dcd875944 | ||
|
|
58e8addad6 | ||
|
|
87615d54dd | ||
|
|
d322aba077 | ||
|
|
c0afe2d873 | ||
|
|
5488482a44 | ||
|
|
71d9a0a120 | ||
|
|
b7b2a463cf | ||
|
|
1b2736012c | ||
|
|
5205fa71bf | ||
|
|
ba2988a8f7 | ||
|
|
e598796eaf | ||
|
|
bce7eabba2 | ||
|
|
0218136ed7 | ||
|
|
461bf803fc | ||
|
|
1bd78bbeea | ||
|
|
348da6b7e4 | ||
|
|
e850218c49 | ||
|
|
385eefd3b3 | ||
|
|
2ddf7aad0b | ||
|
|
c9433c2a5b | ||
|
|
8853a4cbdf | ||
|
|
6f53279b50 | ||
|
|
a741bd1bba | ||
|
|
5c442e068c | ||
|
|
0bed674233 | ||
|
|
66193b0d38 | ||
|
|
45c9a1d7fd | ||
|
|
0d8ddfe378 | ||
|
|
74f479d5c9 | ||
|
|
dbf793e7eb | ||
|
|
b5c6f760c5 | ||
|
|
a5c34e7be2 | ||
|
|
ba2f814342 | ||
|
|
5169a5414b | ||
|
|
82b9a4e28c | ||
|
|
e989d2f626 | ||
|
|
331f0b9325 | ||
|
|
ec6a1fcf80 | ||
|
|
dcebe26a11 | ||
|
|
d7721940f3 | ||
|
|
b678edb0d8 | ||
|
|
caaa7b4cc2 | ||
|
|
1371728e02 | ||
|
|
17d802db81 | ||
|
|
9fc06a8741 | ||
|
|
946fac633e | ||
|
|
9a05211faa | ||
|
|
7ffcec448c | ||
|
|
af1c7d0c26 | ||
|
|
62bffed368 | ||
|
|
5a1de7a722 | ||
|
|
0653efff3d | ||
|
|
566199e49b | ||
|
|
33ee3445af | ||
|
|
8511771816 | ||
|
|
c7bb6fa318 | ||
|
|
c678b3794b | ||
|
|
7dbd04197d | ||
|
|
a53f31fb99 | ||
|
|
b2b8684d0c | ||
|
|
df14c8ac18 | ||
|
|
6e5a46c16f | ||
|
|
e0ce8af1d9 | ||
|
|
c54dbe30a2 | ||
|
|
b8763b05ad | ||
|
|
3755af2063 | ||
|
|
41172e4814 | ||
|
|
264ed4c308 | ||
|
|
65c4693c87 | ||
|
|
05d6eca09d | ||
|
|
c192777aef | ||
|
|
fdd1db970d | ||
|
|
3bc5fb1725 | ||
|
|
25310eaf7c | ||
|
|
9481562b5c | ||
|
|
e44b5309ae | ||
|
|
eecf8f6c36 | ||
|
|
532d215de9 | ||
|
|
71bce54c71 | ||
|
|
41bde57707 | ||
|
|
ff7e394084 | ||
|
|
81f67eeb54 | ||
|
|
a4827a31f3 | ||
|
|
cd31e9c34f | ||
|
|
9492bcd485 | ||
|
|
ff9457e79c | ||
|
|
de580474a3 | ||
|
|
55f9563f60 | ||
|
|
ec770eeb6a | ||
|
|
b9ab307912 | ||
|
|
83ced837fa | ||
|
|
ce2b2801d6 | ||
|
|
1ae96b3820 | ||
|
|
889c178173 | ||
|
|
7ba9591015 | ||
|
|
b07f18facf | ||
|
|
7b1b1b5daa | ||
|
|
63fb28c7d4 | ||
|
|
67e18ff039 | ||
|
|
e715d74a95 | ||
|
|
393d214ac9 | ||
|
|
0fa6038f6a | ||
|
|
cf1182ee99 | ||
|
|
a04497af24 | ||
|
|
821a93ca1a | ||
|
|
e0217424f3 | ||
|
|
cf76bc666c | ||
|
|
d00e39acc7 | ||
|
|
90ba3ae7c6 | ||
|
|
b68702b56d | ||
|
|
3b2322db96 | ||
|
|
6a427ac4b6 | ||
|
|
ffd856073f | ||
|
|
f1fafb117a | ||
|
|
e2cda49b0b | ||
|
|
d1aa2eec56 | ||
|
|
1db0ba4639 | ||
|
|
a4e7eba1d4 | ||
|
|
67778b3278 | ||
|
|
a5e6952a95 | ||
|
|
9cc1357c95 | ||
|
|
b571686e5d | ||
|
|
9c67be287e | ||
|
|
f599783444 | ||
|
|
d628b8d36c | ||
|
|
65c519c22c | ||
|
|
05e492c56c | ||
|
|
3c2321c37a | ||
|
|
6c72a2527f | ||
|
|
1bd09085db | ||
|
|
d6c3f15947 | ||
|
|
5960985355 | ||
|
|
84e2b7869d | ||
|
|
401f69f108 | ||
|
|
4f57e3fb61 | ||
|
|
fdf20f1adc | ||
|
|
990474ff47 | ||
|
|
786d842ad1 | ||
|
|
441c89d304 | ||
|
|
63098e84a2 | ||
|
|
71997bd9aa | ||
|
|
45510facc7 | ||
|
|
cf539064d6 | ||
|
|
3431736c88 | ||
|
|
095b53b2a2 | ||
|
|
7a63c40a01 | ||
|
|
c1148a66cc | ||
|
|
5fa0a5d4c5 | ||
|
|
75a8280701 | ||
|
|
e74fd82060 | ||
|
|
5a141ccf99 | ||
|
|
35d7ae0d2e | ||
|
|
d9c7f95617 | ||
|
|
06b3f6a9d7 | ||
|
|
895b4a0ac5 | ||
|
|
9cb72849ee |
@@ -1,5 +1,5 @@
|
||||
[/
|
||||
/ Copyright (c) 2014 Vicente J. Botet Escriba
|
||||
/ Copyright (c) 2014-2015 Vicente J. Botet Escriba
|
||||
/
|
||||
/ Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
/ file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -174,7 +174,7 @@ This has several advantages:
|
||||
* The scheduled operations are available for all the executors via wrappers.
|
||||
* The template functions could accept any chrono `time_point` and `duration` respectively as we are not working with virtual functions.
|
||||
|
||||
In order to manage with all the clocks, this library propose generic solution. `scheduler<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]
|
||||
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.
|
||||
@@ -319,33 +319,23 @@ The reason is that the user can always use a thread_local variable and reset it
|
||||
}
|
||||
);
|
||||
|
||||
[
|
||||
[heading Default executor]
|
||||
|
||||
The library authors share some of the concerns of the C++ standard committee (introduction of a new single shared resource, a singleton, could make it difficult to make it portable to all the environments) and that this library doesn't need to provide a default executor for the time been.
|
||||
|
||||
The user can always define his default executor himself and use the `at_thread_entry ` member function to set the default constructor.
|
||||
The user can always define his default executor himself.
|
||||
|
||||
thread_local default_executor_state_type default_executor_state;
|
||||
executor* default_executor() { return default_executor_state.default_executor(); }
|
||||
|
||||
// in main
|
||||
MyDefaultExecutor myDefaultExecutor(
|
||||
// at_thread_entry
|
||||
[](MyDefaultExecutor& ex) {
|
||||
default_executor_state.set_default_executor(ex);
|
||||
}
|
||||
);
|
||||
|
||||
basic_thread_pool pool(
|
||||
// at_thread_entry
|
||||
[&myDefaultExecutor](basic_thread_pool& pool) {
|
||||
default_executor_state.set_default_executor(myDefaultExecutor);
|
||||
}
|
||||
);
|
||||
boost::generic_executor_ref default_executor()
|
||||
{
|
||||
static boost::basic_thread_pool tp(4);
|
||||
return generic_executor_ref(tp);
|
||||
}
|
||||
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
[/////////////////////]
|
||||
[section:ref Reference]
|
||||
|
||||
@@ -516,9 +506,6 @@ Executor abstract base class.
|
||||
public:
|
||||
typedef boost::work work;
|
||||
|
||||
executor(executor const&) = delete;
|
||||
executor& operator=(executor const&) = delete;
|
||||
|
||||
executor();
|
||||
virtual ~executor() {};
|
||||
|
||||
@@ -583,9 +570,6 @@ Polymorphic adaptor of a model of Executor to an executor.
|
||||
public:
|
||||
typedef executor::work work;
|
||||
|
||||
executor_adaptor(executor_adaptor const&) = delete;
|
||||
executor_adaptor& operator=(executor_adaptor const&) = delete;
|
||||
|
||||
template <typename ...Args>
|
||||
executor_adaptor(Args&& ... args);
|
||||
|
||||
@@ -651,20 +635,24 @@ Polymorphic adaptor of a model of Executor to an executor.
|
||||
[/////////////////////////////////]
|
||||
[section:generic_executor_ref Class `generic_executor_ref`]
|
||||
|
||||
Executor abstract base class.
|
||||
Type erased executor class.
|
||||
|
||||
#include <boost/thread/generic_executor_ref.hpp>
|
||||
namespace boost {
|
||||
class generic_executor_ref
|
||||
{
|
||||
public:
|
||||
generic_executor_ref(generic_executor_ref const&);
|
||||
generic_executor_ref& operator=(generic_executor_ref const&);
|
||||
|
||||
template <class Executor>
|
||||
generic_executor_ref(Executor& ex);
|
||||
generic_executor_ref() {};
|
||||
generic_executor_ref() = delete;
|
||||
generic_executor_ref(generic_executor_ref const&) = default;
|
||||
generic_executor_ref(generic_executor_ref &&) = default;
|
||||
generic_executor_ref& operator=(generic_executor_ref const&) = default;
|
||||
generic_executor_ref& operator=(generic_executor_ref &&) = default;
|
||||
|
||||
executor& underlying_executor() noexcept;
|
||||
|
||||
void close() = 0;
|
||||
bool closed() = 0;
|
||||
|
||||
@@ -679,6 +667,44 @@ Executor abstract base class.
|
||||
|
||||
[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 `]
|
||||
|
||||
@@ -693,10 +719,7 @@ Scheduler providing time related functions. Note that `scheduler` is not an Exec
|
||||
public:
|
||||
using work = boost::function<void()> ;
|
||||
using clock = Clock;
|
||||
|
||||
scheduler(scheduler const&) = delete;
|
||||
scheduler& operator=(scheduler const&) = delete;
|
||||
|
||||
|
||||
scheduler();
|
||||
~scheduler();
|
||||
|
||||
@@ -743,7 +766,7 @@ Scheduler providing time related functions. Note that `scheduler` is not an Exec
|
||||
|
||||
[[Effects:] [Destroys the scheduler.]]
|
||||
|
||||
[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
|
||||
[[Synchronization:] [The completion of all the closures happen before the completion of the destructor.]]
|
||||
|
||||
]
|
||||
|
||||
@@ -1349,10 +1372,6 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
class serial_executor
|
||||
{
|
||||
public:
|
||||
serial_executor(serial_executor const&) = delete;
|
||||
serial_executor& operator=(serial_executor const&) = delete;
|
||||
|
||||
template <class Executor>
|
||||
serial_executor(Executor& ex);
|
||||
|
||||
Executor& underlying_executor() noexcept;
|
||||
@@ -1403,7 +1422,7 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
[/////////////////////////////////////]
|
||||
[section:underlying_executor Function member `underlying_executor()`]
|
||||
|
||||
generic_executor_ref& underlying_executor() noexcept;
|
||||
Executor& underlying_executor() noexcept;
|
||||
|
||||
[variablelist
|
||||
|
||||
@@ -1428,13 +1447,10 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
class generic_serial_executor
|
||||
{
|
||||
public:
|
||||
generic_serial_executor(generic_serial_executor const&) = delete;
|
||||
generic_serial_executor& operator=(generic_serial_executor const&) = delete;
|
||||
|
||||
template <class Executor>
|
||||
generic_serial_executor(Executor& ex);
|
||||
|
||||
generic_executor_ref& underlying_executor() noexcept;
|
||||
generic_executor& underlying_executor() noexcept;
|
||||
|
||||
void close();
|
||||
bool closed();
|
||||
@@ -1443,6 +1459,7 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
void submit(Closure&& closure);
|
||||
|
||||
bool try_executing_one();
|
||||
|
||||
template <typename Pred>
|
||||
bool reschedule_until(Pred const& pred);
|
||||
|
||||
@@ -1466,7 +1483,7 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
|
||||
[endsect]
|
||||
[/////////////////////////////////////]
|
||||
[section:destructor Destructor `~serial_executor()`]
|
||||
[section:destructor Destructor `~generic_serial_executor()`]
|
||||
|
||||
~generic_serial_executor();
|
||||
|
||||
@@ -1478,6 +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]
|
||||
[/////////////////////////////////////]
|
||||
[section:underlying_executor Function member `underlying_executor()`]
|
||||
@@ -1488,6 +1579,8 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
|
||||
[[Return:] [The underlying executor instance. ]]
|
||||
|
||||
[[Throws:] [Nothing.]]
|
||||
|
||||
]
|
||||
|
||||
|
||||
@@ -1495,6 +1588,82 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
|
||||
[endsect]
|
||||
|
||||
[//////////////////////////////////////////////////////////]
|
||||
[section:generic_serial_cont_executor Class `generic_serial_cont_executor`]
|
||||
|
||||
A serial executor ensuring that there are no two work units that executes concurrently.
|
||||
|
||||
#include <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`]
|
||||
@@ -1506,10 +1675,8 @@ A serial executor ensuring that there are no two work units that executes concur
|
||||
class inline_executor
|
||||
{
|
||||
public:
|
||||
inline_executor(inline_executor const&) = delete;
|
||||
inline_executor& operator=(inline_executor const&) = delete;
|
||||
|
||||
inline_executor();
|
||||
~inline_executor();
|
||||
|
||||
void close();
|
||||
bool closed();
|
||||
@@ -1570,9 +1737,6 @@ A thread pool with up to a fixed number of threads.
|
||||
class basic_thread_pool
|
||||
{
|
||||
public:
|
||||
|
||||
basic_thread_pool(basic_thread_pool const&) = delete;
|
||||
basic_thread_pool& operator=(basic_thread_pool const&) = delete;
|
||||
|
||||
basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency());
|
||||
template <class AtThreadEntry>
|
||||
@@ -1633,9 +1797,6 @@ A thread_executor with a threads for each task.
|
||||
{
|
||||
public:
|
||||
|
||||
thread_executor(thread_executor const&) = delete;
|
||||
thread_executor& operator=(thread_executor const&) = delete;
|
||||
|
||||
thread_executor();
|
||||
template <class AtThreadEntry>
|
||||
basic_thread_pool( unsigned const thread_count, AtThreadEntry at_thread_entry);
|
||||
@@ -1690,9 +1851,6 @@ A user scheduled executor.
|
||||
class loop_executor
|
||||
{
|
||||
public:
|
||||
|
||||
loop_executor(loop_executor const&) = delete;
|
||||
loop_executor& operator=(loop_executor const&) = delete;
|
||||
|
||||
loop_executor();
|
||||
~loop_executor();
|
||||
|
||||
@@ -12,35 +12,56 @@
|
||||
|
||||
[*Know Bugs:]
|
||||
|
||||
* [@http://svn.boost.org/trac/boost/ticket/2442 #2442] Application statically linked with Boost.Thread crashes when Google Desktop is installed (Windows XP)
|
||||
* [@http://svn.boost.org/trac/boost/ticket/3926 #3926] thread_specific_ptr + dlopen library causes a SIGSEGV.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/4833 #4833] MinGW/test_tss_lib: Support of automatic tss cleanup for native threading API not available
|
||||
* [@http://svn.boost.org/trac/boost/ticket/7319 #7319] Take care of c++std-lib-32966 issue
|
||||
* [@http://svn.boost.org/trac/boost/ticket/8600 #8600] wait_for_any hangs, if called with multiple copies of shared_future referencing same task
|
||||
* [@http://svn.boost.org/trac/boost/ticket/9118 #9118] Seg fault on thread join when llvm and libc++ are used
|
||||
* [@http://svn.boost.org/trac/boost/ticket/9309 #9309] test_latch fails often on clang-darwin-tot11
|
||||
* [@http://svn.boost.org/trac/boost/ticket/9310 #9310] test_4648_lib fails on clang-darwin-asan11
|
||||
* [@http://svn.boost.org/trac/boost/ticket/9311 #9311] ex_lambda_future fails on msvc-11.0
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10537 #10537] Application crash on throw exception
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10651 #10651] boost::thread leaks memory when using the MinGW compiler
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10942 #10942] Boost.Thread fails to build on Cygwin.
|
||||
|
||||
[/
|
||||
* [@http://svn.boost.org/trac/boost/ticket/7319 #7319] Take care of c++std-lib-32966 issue
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10651 #10651] boost::thread leaks memory when using the MinGW compiler.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10685 #10685] mfc_thread_init.hpp does not compile.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10967 #10967] Timed wait points inconsistently convert relative to absolute waits.
|
||||
]
|
||||
|
||||
Please take a look at [@https://svn.boost.org/trac/boost/query?status=assigned&status=new&status=reopened&component=thread&type=!Feature+Requests&col=id&col=summary&order=id thread Know Bugs] to see the current state.
|
||||
|
||||
Please take a look at [@http://www.boost.org/development/tests/master/developer/thread.html thread trunk regression test] to see the last snapshot.
|
||||
Please take a look at [@http://www.boost.org/development/tests/master/developer/thread.html thread trunk regression test] to see the last regression test snapshot.
|
||||
|
||||
[*Sever limitations:]
|
||||
|
||||
There are some severe bugs that prevent the use of the library on concrete contexts, in particular:
|
||||
|
||||
* on thread specific storage that prevent the library to be used with dynamic libraries,
|
||||
* on thread specific storage that prevent the library to be used with dynamic libraries ( [@http://svn.boost.org/trac/boost/ticket/3926 #3926], ),
|
||||
|
||||
[*New Experimental Features:]
|
||||
|
||||
* [@http://svn.boost.org/trac/boost/ticket/9600 #9600] Async: Add task_region
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10611 #10611] Add emplace promise::set_value and emplace make_ready_future
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10826 #10826] Add scheduled executor operations
|
||||
* [@http://svn.boost.org/trac/boost/ticket/11048 #11048] Add a serial_executor based on continuations
|
||||
|
||||
[*Fixed Bugs:]
|
||||
|
||||
* [@http://svn.boost.org/trac/boost/ticket/6787 #6787] boost::thread::sleep() hangs if system time is rolled back
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10734 #10734] Submit method work differently on different executors, some throw exception and some silently ignore error (thread_executor and inline_executor)
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10736 #10736] Task exceptions silently ignored. I think std::terminate solution from N3785 and std::thread is better choice and more consistent.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10737 #10737] In serial_executor we have infinite wait if task throw exception.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10822 #10822] Boost.Thread fails to compile on Android
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10824 #10824] Boost.Thread 1.57 breaks Windows XP compatibility for SP2 and below.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10963 #10963] future<future<T>>::then Has No Implementation
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10964 #10964] future<future<T>>::unwrap().then() Deadlocks
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10968 #10968] The futures returned by async() and future::then() are not blocking.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10971 #10971] shared_future::get()/get_or() must be const
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10972 #10972] shared_future::then() can be called multiple times.
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10979 #10979] Support T& type deduction when the make_ready_future parameter is reference_wrapper<T>
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10996 #10996] Thread physical_concurrency() is failing on Windows
|
||||
* [@http://svn.boost.org/trac/boost/ticket/11035 #11035] BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE not defined for Android
|
||||
* [@http://svn.boost.org/trac/boost/ticket/11053 #11053] The attached code results in a R6025 - pure virtual function call in run_thread_exit_callbacks
|
||||
|
||||
|
||||
[heading Version 4.4.0 - boost 1.57]
|
||||
|
||||
@@ -58,6 +79,7 @@ There are some severe bugs that prevent the use of the library on concrete conte
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10537 #10537] Application crash on throw exception
|
||||
* [@http://svn.boost.org/trac/boost/ticket/10651 #10651] boost::thread leaks memory when using the MinGW compiler
|
||||
|
||||
|
||||
Please take a look at [@https://svn.boost.org/trac/boost/query?status=assigned&status=new&status=reopened&component=thread&type=!Feature+Requests&col=id&col=summary&order=id thread Know Bugs] to see the current state.
|
||||
|
||||
Please take a look at [@http://www.boost.org/development/tests/master/developer/thread.html thread trunk regression test] to see the last snapshot.
|
||||
|
||||
@@ -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.
|
||||
(See accompanying file LICENSE_1_0.txt or copy at
|
||||
http://www.boost.org/LICENSE_1_0.txt).
|
||||
]
|
||||
|
||||
[section:compliance Conformance and Extension]
|
||||
[////////////////////////////////////////////]
|
||||
|
||||
[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++]]]
|
||||
|
||||
@@ -88,6 +90,7 @@
|
||||
]
|
||||
[endsect]
|
||||
[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++]]
|
||||
|
||||
@@ -103,6 +106,7 @@
|
||||
|
||||
|
||||
[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]]
|
||||
|
||||
@@ -126,8 +130,10 @@
|
||||
[endsect]
|
||||
|
||||
[section:cxx1y C++1z TS Concurrency - On going proposals]
|
||||
[///////////////////////////////////////////////////////]
|
||||
|
||||
[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/n3817.html N3817 C++ Latches and Barriers]]
|
||||
@@ -140,6 +146,7 @@
|
||||
|
||||
[endsect]
|
||||
[section:queue 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] [ - ]]
|
||||
]
|
||||
[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]]
|
||||
|
||||
[table Asynchronous Executors
|
||||
[table Executors and Schedulers
|
||||
[[Section] [Description] [Status] [Comments]]
|
||||
[[V.1.1] [Class executor] [Yes] [ - ]]
|
||||
[[V.1.1] [add] [Yes] [ renamed with a function template submit ]]
|
||||
[[V.1.1] [num_of_pendin_closures] [No] [ ]]
|
||||
[[V.1.1] [Class `executor`] [Yes] [ - ]]
|
||||
[[V.1.1] [`add`] [Yes] [ renamed with a function template `submit` ]]
|
||||
[[V.1.1] [`num_of_pendin_closures`] [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_after] [No] [ renamed with a function template submit_after ]]
|
||||
[[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 `scheduler::submit_after` ]]
|
||||
[[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.2] [serial_executor] [yes] [ - ]]
|
||||
[[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.5] [thread_executor] [Yes] [ static version thread_executor, dynamic one execduler_adaptor<thread_executor> ]]
|
||||
[[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.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.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]]
|
||||
@@ -212,8 +220,31 @@
|
||||
[[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]
|
||||
[//////////////////////////////////////////////////////////////
|
||||
[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.
|
||||
|
||||
@@ -241,7 +272,7 @@ While Boost.Thread implementation of stream mutexes differ in the approach, it i
|
||||
|
||||
[endsect]
|
||||
|
||||
|
||||
///////////////////////////////]
|
||||
[endsect]
|
||||
|
||||
[endsect]
|
||||
|
||||
@@ -98,7 +98,7 @@ or inheriting from a class which add these lockable functions.
|
||||
The `basic_lockable_adapter` class helps to define the `BankAccount` class as
|
||||
|
||||
class BankAccount
|
||||
: public basic_lockable_adapter<thread_mutex>
|
||||
: public basic_lockable_adapter<boost::mutex>
|
||||
{
|
||||
int balance_;
|
||||
public:
|
||||
@@ -138,7 +138,7 @@ Notice that now acct is being locked by Withdraw after it has already been locke
|
||||
As `boost::mutex` is not recursive, we need to use its recursive version `boost::recursive_mutex`.
|
||||
|
||||
class BankAccount
|
||||
: public basic_lockable_adapter<recursive_mutex>
|
||||
: public basic_lockable_adapter<boost::recursive_mutex>
|
||||
{
|
||||
|
||||
// ...
|
||||
@@ -147,7 +147,7 @@ As `boost::mutex` is not recursive, we need to use its recursive version `boost:
|
||||
The caller-ensured locking approach is more flexible and the most efficient, but very dangerous. In an implementation using caller-ensured locking, BankAccount still holds a mutex, but its member functions don't manipulate it at all. Deposit and Withdraw are not thread-safe anymore. Instead, the client code is responsible for locking BankAccount properly.
|
||||
|
||||
class BankAccount
|
||||
: public basic_lockable_adapter<boost:mutex> {
|
||||
: public basic_lockable_adapter<boost::mutex> {
|
||||
int balance_;
|
||||
public:
|
||||
void Deposit(int amount) {
|
||||
@@ -271,7 +271,7 @@ Now that we have such a strict `strict_lock`, how do we harness its power in def
|
||||
A little code is worth 1,000 words, a (hacked into) saying goes, so here's the new BankAccount class:
|
||||
|
||||
class BankAccount
|
||||
: public basic_lockable_adapter<boost:recursive_mutex>
|
||||
: public basic_lockable_adapter<boost::mutex>
|
||||
{
|
||||
int balance_;
|
||||
public:
|
||||
@@ -280,7 +280,7 @@ A little code is worth 1,000 words, a (hacked into) saying goes, so here's the n
|
||||
balance_ += amount;
|
||||
}
|
||||
void Deposit(int amount) {
|
||||
strict_lock<boost:mutex> guard(*this); // Internally locked
|
||||
strict_lock<BankAccount> guard(*this); // Internally locked
|
||||
Deposit(amount, guard);
|
||||
}
|
||||
void Withdraw(int amount, strict_lock<BankAccount>&) {
|
||||
@@ -288,7 +288,7 @@ A little code is worth 1,000 words, a (hacked into) saying goes, so here's the n
|
||||
balance_ -= amount;
|
||||
}
|
||||
void Withdraw(int amount) {
|
||||
strict_lock<boost:mutex> guard(*this); // Internally locked
|
||||
strict_lock<BankAccount> guard(*this); // Internally locked
|
||||
Withdraw(amount, guard);
|
||||
}
|
||||
};
|
||||
@@ -327,7 +327,7 @@ The scheme is useful because the likelihood of a programmer forgetting about any
|
||||
Using `strict_lock` permits compile-time checking of the most common source of errors, and runtime checking of the less frequent problem.
|
||||
|
||||
Let's see how to enforce that the appropriate BankAccount object is locked. First, we need to add a member function to the `strict_lock` class template.
|
||||
The `bool strict_lock<T>::owns_lock(Loclable*)` function returns a reference to the locked object.
|
||||
The `bool strict_lock<T>::owns_lock(Lockable*)` function returns a reference to the locked object.
|
||||
|
||||
template <class Lockable> class strict_lock {
|
||||
... as before ...
|
||||
@@ -338,7 +338,7 @@ The `bool strict_lock<T>::owns_lock(Loclable*)` function returns a reference to
|
||||
Second, BankAccount needs to use this function compare the locked object against this:
|
||||
|
||||
class BankAccount {
|
||||
: public basic_lockable_adapter<boost::recursive_mutex>
|
||||
: public basic_lockable_adapter<boost::mutex>
|
||||
int balance_;
|
||||
public:
|
||||
void Deposit(int amount, strict_lock<BankAccount>& guard) {
|
||||
@@ -403,7 +403,7 @@ The solution is to use a little bridge template `externally_locked` that control
|
||||
T& get(strict_lock<Lockable>& lock) {
|
||||
|
||||
#ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
|
||||
if (!lock.owns_lock(&lockable_)) throw lock_error(); run time check throw if not locks the same
|
||||
if (!lock.owns_lock(&lockable_)) throw lock_error(); //run time check throw if not locks the same
|
||||
#endif
|
||||
return obj_;
|
||||
}
|
||||
@@ -421,10 +421,10 @@ The solution is to use a little bridge template `externally_locked` that control
|
||||
Instead of making `checkingAcct_` and `savingsAcct_` of type `BankAccount`, `AccountManager` holds objects of type `externally_locked<BankAccount, AccountManager>`:
|
||||
|
||||
class AccountManager
|
||||
: public basic_lockable_adapter<thread_mutex>
|
||||
: public basic_lockable_adapter<boost::mutex>
|
||||
{
|
||||
public:
|
||||
typedef basic_lockable_adapter<thread_mutex> lockable_base_type;
|
||||
typedef basic_lockable_adapter<boost::mutex> lockable_base_type;
|
||||
AccountManager()
|
||||
: checkingAcct_(*this)
|
||||
, savingsAcct_(*this)
|
||||
@@ -498,7 +498,7 @@ Now we need to state that both classes are `strict_lock`s.
|
||||
|
||||
Well let me show what this `nested_strict_lock` class looks like and the impacts on the `externally_locked` class and the `AccountManager::AMoreComplicatedFunction` function.
|
||||
|
||||
First `nested_strict_lock` class will store on a temporary lock the `Locker`, and transfer the lock ownership on the constructor. On destruction it will restore the ownership. Note the use of `lock_traits` and that the `Locker` needs to have a reference to the mutex otherwise and exception is thrown.
|
||||
First `nested_strict_lock` class will store on a temporary lock the `Locker`, and transfer the lock ownership on the constructor. On destruction it will restore the ownership. Note the use of `lock_traits` and that the `Locker` needs to have a reference to the mutex otherwise an exception is thrown.
|
||||
|
||||
template <typename Locker >
|
||||
class nested_strict_lock
|
||||
@@ -510,7 +510,7 @@ First `nested_strict_lock` class will store on a temporary lock the `Locker`, an
|
||||
|
||||
nested_strict_lock(Locker& lock)
|
||||
: lock_(lock) // Store reference to locker
|
||||
, tmp_lock_(lock.move()) // Move ownership to temporaty locker
|
||||
, tmp_lock_(lock.move()) // Move ownership to temporary locker
|
||||
{
|
||||
#ifdef BOOST_THREAD_THROW_IF_PRECONDITION_NOT_SATISFIED
|
||||
if (tmp_lock_.mutex()==0) {
|
||||
|
||||
@@ -1020,7 +1020,7 @@ There are not too much tests yet, so it is possible that you can find out some t
|
||||
void swap(shared_future& other);
|
||||
|
||||
// retrieving the value
|
||||
see below get();
|
||||
see below get() const;
|
||||
|
||||
exception_ptr get_exception_ptr(); // EXTENSION
|
||||
|
||||
@@ -1066,9 +1066,9 @@ There are not too much tests yet, so it is possible that you can find out some t
|
||||
[///////////////////////////////////]
|
||||
[section:get Member function `get()`]
|
||||
|
||||
const R& get();
|
||||
R& get();
|
||||
void get();
|
||||
const R& get() const;
|
||||
R& get() const;
|
||||
void get() const;
|
||||
|
||||
[variablelist
|
||||
|
||||
@@ -2362,7 +2362,7 @@ All `R` types must be the same. If any of the `future<R>` or `shared_future<R>`
|
||||
[section:make_ready_future Non-member function `make_ready_future()` EXTENSION]
|
||||
|
||||
template <typename T>
|
||||
future<typename decay<T>::type> make_ready_future(T&& value); // EXTENSION
|
||||
future<V> make_ready_future(T&& value); // EXTENSION
|
||||
future<void> make_ready_future(); // EXTENSION
|
||||
template <typename T>
|
||||
future<T> make_ready_future(exception_ptr ex); // DEPRECATED
|
||||
@@ -2371,11 +2371,15 @@ All `R` types must be the same. If any of the `future<R>` or `shared_future<R>`
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Remark:][
|
||||
where `V` is determined as follows: Let `U` be `decay_t<T>`. Then `V` is `X&`
|
||||
if `U` equals `reference_wrapper<X>`, otherwise `V` is `U`.
|
||||
]]
|
||||
[[Effects:] [
|
||||
- value prototype: The value that is passed into the function is moved to the shared state of the returned function if it is an rvalue.
|
||||
Otherwise the value is copied to the shared state of the returned function.
|
||||
- value prototype: The value that is passed into the function is moved to the shared state of the returned future if it is an rvalue.
|
||||
Otherwise the value is copied to the shared state of the returned future.
|
||||
|
||||
- exception: The exception that is passed into the function is copied to the shared state of the returned function.
|
||||
- exception: The exception that is passed into the function is copied to the shared state of the returned future.
|
||||
|
||||
.]]
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ the result is ready, it is returned from __unique_future_get__ by rvalue-referen
|
||||
appropriate for the type.
|
||||
|
||||
On the other hand, many instances of __shared_future__ may reference the same result. Instances can be freely copied and assigned,
|
||||
and __shared_future_get__ returns a non `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
|
||||
and __shared_future_get__ returns a `const` reference so that multiple calls to __shared_future_get__ are safe. You can move an
|
||||
instance of __unique_future__ into an instance of __shared_future__, thus transferring ownership of the associated asynchronous
|
||||
result, but not vice-versa.
|
||||
|
||||
|
||||
@@ -2664,7 +2664,7 @@ Only the specificities respect to __Lockable are described here.
|
||||
|
||||
[endsect]
|
||||
[///////////////////////////////]
|
||||
[section:get2 `get(strict_lock<nested_strict_lock<Lock>>&)`]
|
||||
[section:get2 `get(nested_strict_lock<Lock>&)`]
|
||||
|
||||
template <class Lock>
|
||||
T& get(nested_strict_lock<Lock>& lk);
|
||||
@@ -2684,7 +2684,7 @@ Only the specificities respect to __Lockable are described here.
|
||||
[endsect]
|
||||
|
||||
[///////////////////////////////]
|
||||
[section:get3 `get(strict_lock<nested_strict_lock<Lock>>&)`]
|
||||
[section:get3 `get(Lock&)`]
|
||||
|
||||
template <class Lock>
|
||||
T& get(Lock& lk);
|
||||
@@ -2826,7 +2826,7 @@ Only the specificities respect to __Lockable are described here.
|
||||
|
||||
[endsect]
|
||||
[///////////////////////////////]
|
||||
[section:get2 `get(strict_lock<nested_strict_lock<Lock>>&)`]
|
||||
[section:get2 `get(nested_strict_lock<Lock>&)`]
|
||||
|
||||
template <class Lock>
|
||||
T& get(nested_strict_lock<Lock>& lk);
|
||||
@@ -2846,7 +2846,7 @@ Only the specificities respect to __Lockable are described here.
|
||||
[endsect]
|
||||
|
||||
[///////////////////////////////]
|
||||
[section:get3 `get(strict_lock<nested_strict_lock<Lock>>&)`]
|
||||
[section:get3 `get(Lock&)`]
|
||||
|
||||
template <class Lock>
|
||||
T& get(Lock& lk);
|
||||
|
||||
@@ -21,18 +21,18 @@
|
||||
class scoped_thread;
|
||||
void swap(scoped_thread& lhs,scoped_thread& rhs) noexcept;
|
||||
|
||||
[section:motovation Motivation]
|
||||
[section:motivation Motivation]
|
||||
Based on the scoped_thread class defined in C++ Concurrency in Action Boost.Thread defines a thread wrapper class that instead of calling terminate if the thread is joinable on destruction, call a specific action given as template parameter.
|
||||
|
||||
While the scoped_thread class defined in C++ Concurrency in Action is closer to strict_scoped_thread class that doesn't allows any change in the wrapped thread, Boost.Thread provides a class scoped_thread that provides the same non-deprecated interface than __thread.
|
||||
While the scoped_thread class defined in C++ Concurrency in Action is closer to strict_scoped_thread class that doesn't allows any change in the wrapped thread, Boost.Thread provides a class scoped_thread that provides the same non-deprecated interface as __thread.
|
||||
|
||||
[endsect]
|
||||
|
||||
[section:tutorial Tutorial]
|
||||
|
||||
Scoped Threads are wrappers around a thread that allows the user to state what to do at destruction time. One of the common uses is to join the thread at destruction time so this is the default behavior. This is the single difference respect to a thread. While thread call std::terminate() on the destructor is the thread is joinable, strict_scoped_thread<> or scoped_thread<> join the thread if joinable.
|
||||
Scoped Threads are wrappers around a thread that allows the user to state what to do at destruction time. One of the common uses is to join the thread at destruction time so this is the default behavior. This is the single difference respect to a thread. While thread call std::terminate() on the destructor if the thread is joinable, strict_scoped_thread<> or scoped_thread<> join the thread if joinable.
|
||||
|
||||
The difference between strict_scoped_thread and scoped_thread is that the strict_scoped_thread hides completely the owned thread and so the user can do nothing with the owned thread other than the specific action given as parameter, while scoped_thread provide the same interface than __thread and forwards all the operations.
|
||||
The difference between strict_scoped_thread and scoped_thread is that the strict_scoped_thread hides completely the owned thread and so the user can do nothing with the owned thread other than the specific action given as parameter, while scoped_thread provide the same interface as __thread and forwards all the operations.
|
||||
|
||||
boost::strict_scoped_thread<> t1((boost::thread(f)));
|
||||
//t1.detach(); // compile fails
|
||||
@@ -114,15 +114,15 @@ The difference between strict_scoped_thread and scoped_thread is that the strict
|
||||
};
|
||||
|
||||
|
||||
RAI __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
|
||||
RAII __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
|
||||
|
||||
CallableThread: A callable `void(thread&)`.
|
||||
|
||||
The default is a `join_if_joinable`.
|
||||
|
||||
|
||||
`std/boost::thread` destructor terminates the program if the __thread is not joinable.
|
||||
This wrapper can be used to join the thread before destroying it seems a natural need.
|
||||
Thread destructor terminates the program if the __thread is joinable.
|
||||
This wrapper can be used to join the thread before destroying it.
|
||||
|
||||
[heading Example]
|
||||
|
||||
@@ -233,13 +233,13 @@ This wrapper can be used to join the thread before destroying it seems a natural
|
||||
void swap(scoped_thread& lhs,scoped_thread& rhs) noexcept;
|
||||
|
||||
|
||||
RAI __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
|
||||
RAII __thread wrapper adding a specific destroyer allowing to master what can be done at destruction time.
|
||||
|
||||
CallableThread: A callable void(thread&).
|
||||
The default is join_if_joinable.
|
||||
|
||||
thread std::thread destructor terminates the program if the thread is not joinable.
|
||||
Having a wrapper that can join the thread before destroying it seems a natural need.
|
||||
Thread destructor terminates the program if the thread is joinable.
|
||||
This wrapper can be used to join the thread before destroying it.
|
||||
|
||||
Remark: `scoped_thread` is not a __thread as __thread is not designed to be derived from as a polymorphic type.
|
||||
|
||||
@@ -312,9 +312,9 @@ any) to `*this`.
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Effects:] [move the thread to own `t_`.]]
|
||||
[[Effects:] [Transfers ownership of the thread managed by `other` (if any) to the newly constructed scoped_thread instance.]]
|
||||
|
||||
[[Postconditions:] [`*this.t_` refers to the newly created thread of execution and `this->get_id()!=thread::id()`.]]
|
||||
[[Postconditions:] [other.get_id()==thread::id() and get_id() returns the value of other.get_id() prior to the construction.]]
|
||||
|
||||
[[Throws:] [Nothing]]
|
||||
|
||||
@@ -329,7 +329,7 @@ any) to `*this`.
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Effects:] [Construct a internal thread in place.]]
|
||||
[[Effects:] [Construct an internal thread in place.]]
|
||||
|
||||
[[Postconditions:] [`*this.t_` refers to the newly created thread of execution and `this->get_id()!=thread::id()`.]]
|
||||
|
||||
|
||||
@@ -181,7 +181,7 @@ where
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Requires:] [Q::value_type is no throw copy movable. This is needed to ensure the exception safety.]]
|
||||
[[Requires:] [Q::value_type is no throw move constructible. This is needed to ensure the exception safety.]]
|
||||
|
||||
[[Effects:] [Waits until the queue is not empty and not closed. If the queue is empty and closed throws sync_queue_is_closed. Otherwise pull the element from the queue `q` and moves the pulled element.]]
|
||||
|
||||
@@ -206,7 +206,7 @@ where
|
||||
|
||||
[variablelist
|
||||
|
||||
[/[Requires:] [Q::value_type is no throw copy movable. This is needed to ensure the exception safety. ]]
|
||||
[/[Requires:] [Q::value_type is no throw move assignable. This is needed to ensure the exception safety. ]]
|
||||
|
||||
[[Effects:] [Waits until the queue is not empty and not closed. If the queue is empty and closed throws sync_queue_is_closed. Otherwise pull the element from the queue `q` and moves the pulled element into a shared_ptr.]]
|
||||
|
||||
@@ -314,7 +314,7 @@ where
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Effects:] [Waits until the queue is not empty and then pull the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
|
||||
[[Effects:] [If the queue is not empty pulls the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
|
||||
|
||||
[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]]
|
||||
|
||||
@@ -393,7 +393,7 @@ where
|
||||
|
||||
[endsect]
|
||||
[/////////////////////////////////////]
|
||||
[section:nonblocking_push_back_m `s = q.nonblocking_push_back(rve());`]
|
||||
[section:nonblocking_push_back_m `s = q.nonblocking_push_back(rve);`]
|
||||
|
||||
[variablelist
|
||||
|
||||
@@ -427,7 +427,7 @@ where
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Effects:] [Waits until the queue is not empty and then pull the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
|
||||
[[Effects:] [If the queue is not empty pulls the element from the queue `q` and moves the pulled element into `lve` (this could need an allocation for unbounded queues).]]
|
||||
|
||||
[[Synchronization:] [Prior pull-like operations on the same object synchronizes with this operation.]]
|
||||
|
||||
@@ -636,12 +636,12 @@ Closed queues add the following valid expressions
|
||||
|
||||
namespace boost
|
||||
{
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType=std::size_t>
|
||||
class queue_base
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~queue_base() {};
|
||||
@@ -671,7 +671,7 @@ Closed queues add the following valid expressions
|
||||
|
||||
virtual queue_op_status wait_push_back(const value_type& x) = 0;
|
||||
virtual queue_op_status wait_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
virtual queue_op_status wait_pull_front(ValueType& elem) = 0;
|
||||
virtual queue_op_status wait_pull_front(value_type& elem) = 0;
|
||||
|
||||
};
|
||||
}
|
||||
@@ -685,11 +685,11 @@ Closed queues add the following valid expressions
|
||||
namespace boost
|
||||
{
|
||||
template <typename Queue>
|
||||
class queue_adaptor : public queue_base<typename Queue::value_type>
|
||||
class queue_adaptor : public queue_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
|
||||
@@ -1002,13 +1002,13 @@ Closed queues add the following valid expressions
|
||||
|
||||
namespace boost
|
||||
{
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class Container = csbl::devector<ValueType>>
|
||||
class sync_queue
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef csbl::deque<ValueType> underlying_queue_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef Container underlying_queue_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
|
||||
sync_queue(sync_queue const&) = delete;
|
||||
sync_queue& operator=(sync_queue const&) = delete;
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
[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.
|
||||
|
||||
|
||||
@@ -88,9 +88,9 @@
|
||||
typedef T value_type;
|
||||
typedef Lockable mutex_type;
|
||||
|
||||
synchronized_value() noexept(is_nothrow_default_constructible<T>::value);
|
||||
synchronized_value(T const& other) noexept(is_nothrow_copy_constructible<T>::value);
|
||||
synchronized_value(T&& other) noexept(is_nothrow_move_constructible<T>::value);
|
||||
synchronized_value() noexcept(is_nothrow_default_constructible<T>::value);
|
||||
synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value);
|
||||
synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value);
|
||||
synchronized_value(synchronized_value const& rhs);
|
||||
synchronized_value(synchronized_value&& other);
|
||||
|
||||
@@ -129,7 +129,7 @@
|
||||
|
||||
[section:constructor `synchronized_value()`]
|
||||
|
||||
synchronized_value() noexept(is_nothrow_default_constructible<T>::value);
|
||||
synchronized_value() noexcept(is_nothrow_default_constructible<T>::value);
|
||||
|
||||
[variablelist
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
|
||||
[section:constructor_vt `synchronized_value(T const&)`]
|
||||
|
||||
synchronized_value(T const& other) noexept(is_nothrow_copy_constructible<T>::value);
|
||||
synchronized_value(T const& other) noexcept(is_nothrow_copy_constructible<T>::value);
|
||||
|
||||
[variablelist
|
||||
|
||||
@@ -175,11 +175,11 @@
|
||||
|
||||
[section:move_vt `synchronized_value(T&&)`]
|
||||
|
||||
synchronized_value(T&& other) noexept(is_nothrow_move_constructible<T>::value);
|
||||
synchronized_value(T&& other) noexcept(is_nothrow_move_constructible<T>::value);
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Requires:] [`T` is `CopyMovable `.]]
|
||||
[[Requires:] [`T` is `MoveConstructible `.]]
|
||||
[[Effects:] [Move constructs the cloaked value_type]]
|
||||
|
||||
[[Throws:] [Any exception thrown by `value_type(value_type&&)`.]]
|
||||
@@ -194,7 +194,7 @@
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Requires:] [`T` is `CopyMovable `.]]
|
||||
[[Requires:] [`T` is `MoveConstructible `.]]
|
||||
[[Effects:] [Move constructs the cloaked value_type]]
|
||||
|
||||
[[Throws:] [Any exception thrown by `value_type(value_type&&)` or `mtx_.lock()`.]]
|
||||
@@ -209,7 +209,7 @@
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Requires:] [`T` is `Assignale`.]]
|
||||
[[Requires:] [`T` is `Assignable`.]]
|
||||
[[Effects:] [Copies the underlying value on a scope protected by the two mutexes. The mutex is not copied. The locks are acquired avoiding deadlock. For example, there is no problem if one thread assigns `a = b` and the other assigns `b = a`.]]
|
||||
[[Return:] [`*this`]]
|
||||
|
||||
@@ -224,7 +224,7 @@
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Requires:] [`T` is `Assignale`.]]
|
||||
[[Requires:] [`T` is `Assignable`.]]
|
||||
[[Effects:] [Copies the value on a scope protected by the mutex.]]
|
||||
[[Return:] [`*this`]]
|
||||
|
||||
@@ -273,7 +273,7 @@
|
||||
|
||||
[variablelist
|
||||
|
||||
[[Requires:] [`T` is `Assignale`.]]
|
||||
[[Requires:] [`T` is `Assignable`.]]
|
||||
[[Effects:] [Swaps the data on a scope protected by both mutex. Both mutex are acquired to avoid dead-lock. The mutexes are not swapped.]]
|
||||
|
||||
[[Throws:] [Any exception thrown by `swap(value_, rhs.value)` or `mtx_.lock()` or `rhs_.mtx_.lock()`.]]
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
|
||||
[library Thread
|
||||
[quickbook 1.5]
|
||||
[version 4.4.0]
|
||||
[version 4.5.0]
|
||||
[authors [Williams, Anthony] [Botet Escriba, Vicente J.]]
|
||||
[copyright 2007-11 Anthony Williams]
|
||||
[copyright 2011-14 Vicente J. Botet Escriba]
|
||||
[copyright 2011-15 Vicente J. Botet Escriba]
|
||||
[purpose C++ Library for launching threads and synchronizing data between them]
|
||||
[category text]
|
||||
[license
|
||||
|
||||
@@ -432,12 +432,14 @@ This behavior is incompatible with the current Boost.Thread design, so the use o
|
||||
class attributes; // EXTENSION
|
||||
|
||||
thread() noexcept;
|
||||
~thread();
|
||||
|
||||
thread(const thread&) = delete;
|
||||
thread& operator=(const thread&) = delete;
|
||||
|
||||
// move support
|
||||
thread(thread&&) noexcept;
|
||||
thread& operator=(thread&&) noexcept;
|
||||
~thread();
|
||||
|
||||
template <class F>
|
||||
explicit thread(F f);
|
||||
@@ -456,10 +458,6 @@ This behavior is incompatible with the current Boost.Thread design, so the use o
|
||||
template <class F, class ...Args>
|
||||
explicit thread(attributes& attrs, F&& f, Args&&... args);
|
||||
|
||||
// move support
|
||||
thread(thread && x) noexcept;
|
||||
thread& operator=(thread && x) noexcept;
|
||||
|
||||
void swap(thread& x) noexcept;
|
||||
|
||||
class id;
|
||||
|
||||
61
example/default_executor.cpp
Normal file
61
example/default_executor.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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/generic_executor_ref.hpp>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/thread/caller_context.hpp>
|
||||
|
||||
|
||||
boost::generic_executor_ref default_executor()
|
||||
{
|
||||
static boost::basic_thread_pool tp(4);
|
||||
return boost::generic_executor_ref(tp);
|
||||
}
|
||||
|
||||
void p2()
|
||||
{
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void p1()
|
||||
{
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
|
||||
default_executor().submit(&p2);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(400));
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
|
||||
default_executor().submit(&p1);
|
||||
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(5));
|
||||
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
#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/serial_executor.hpp>
|
||||
#include <boost/thread/executors/generic_serial_executor.hpp>
|
||||
#include <boost/thread/executors/inline_executor.hpp>
|
||||
#include <boost/thread/executors/thread_executor.hpp>
|
||||
#include <boost/thread/executors/executor.hpp>
|
||||
@@ -27,6 +27,12 @@
|
||||
#include <boost/assert.hpp>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <cassert>
|
||||
|
||||
boost::future<void> p(boost::future<void> f) {
|
||||
assert(f.is_ready());
|
||||
return boost::make_ready_future();
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
{
|
||||
boost::basic_thread_pool e1;
|
||||
boost::basic_thread_pool e2 = e1;
|
||||
}
|
||||
{
|
||||
boost::executor_adaptor < boost::basic_thread_pool > ea(4);
|
||||
submit_some( ea);
|
||||
@@ -98,25 +108,65 @@ int test_executor_adaptor()
|
||||
submit_some(ea);
|
||||
}
|
||||
// std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
{
|
||||
boost::loop_executor e1;
|
||||
boost::loop_executor e2 = e1;
|
||||
boost::executor_adaptor < boost::loop_executor > ea2(e2);
|
||||
submit_some( ea2);
|
||||
ea2.underlying_executor().run_queued_closures();
|
||||
}
|
||||
{
|
||||
boost::executor_adaptor < boost::loop_executor > ea2;
|
||||
submit_some( ea2);
|
||||
ea2.underlying_executor().run_queued_closures();
|
||||
}
|
||||
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
// std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
{
|
||||
boost::executor_adaptor < boost::basic_thread_pool > ea1(4);
|
||||
boost::executor_adaptor < boost::serial_executor > ea2(ea1);
|
||||
boost::basic_thread_pool tp;
|
||||
boost::generic_serial_executor e1(tp);
|
||||
boost::generic_serial_executor e2 = e1;
|
||||
}
|
||||
{
|
||||
boost::basic_thread_pool ea1(4);
|
||||
boost::generic_serial_executor ea2(ea1);
|
||||
boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2);
|
||||
submit_some(ea3);
|
||||
}
|
||||
{
|
||||
boost::basic_thread_pool ea1(4);
|
||||
boost::generic_serial_executor ea2(ea1);
|
||||
boost::executor_adaptor < boost::generic_serial_executor > ea3(ea2);
|
||||
submit_some(ea3);
|
||||
}
|
||||
//#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
|
||||
{
|
||||
boost::basic_thread_pool ea1(4);
|
||||
boost::executor_adaptor < boost::generic_serial_executor > ea2(ea1);
|
||||
submit_some(ea2);
|
||||
}
|
||||
#endif
|
||||
//#endif
|
||||
// std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
{
|
||||
boost::inline_executor e1;
|
||||
boost::inline_executor e2 = e1;
|
||||
boost::executor_adaptor < boost::inline_executor > ea2(e2);
|
||||
submit_some(ea2);
|
||||
}
|
||||
{
|
||||
boost::executor_adaptor < boost::inline_executor > ea1;
|
||||
submit_some(ea1);
|
||||
}
|
||||
// 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;
|
||||
submit_some(ea1);
|
||||
@@ -147,4 +197,19 @@ int test_executor_adaptor()
|
||||
int main()
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
@@ -35,31 +35,83 @@ int p1()
|
||||
|
||||
int main()
|
||||
{
|
||||
const int number_of_tests = 100;
|
||||
const int number_of_tests = 200;
|
||||
BOOST_THREAD_LOG << "<MAIN" << BOOST_THREAD_END_LOG;
|
||||
|
||||
{
|
||||
for (int i=0; i< number_of_tests; i++)
|
||||
try
|
||||
{
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
boost::future<int> f1 = boost::async(boost::launch::async, &p1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
f1.wait();
|
||||
BOOST_ASSERT(f1.get()==1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << "ERRORRRRR "<<ex.what() << "" << std::endl;
|
||||
BOOST_THREAD_LOG << "ERRORRRRR "<<ex.what() << "" << BOOST_THREAD_END_LOG;
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << " ERRORRRRR exception thrown" << std::endl;
|
||||
BOOST_THREAD_LOG << " ERRORRRRR exception thrown" << BOOST_THREAD_END_LOG;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (int i=0; i< number_of_tests; i++)
|
||||
try
|
||||
{
|
||||
//boost::future<int> f1 = boost::async(boost::launch::async, &p1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
boost::future<int> f1 = boost::async(&p1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
boost::future<int> f2 = f1.fallback_to(-1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
f2.wait();
|
||||
//std::cout << __FILE__ << "["<< __LINE__<<"] " << std::endl;
|
||||
BOOST_ASSERT(f2.get()==1);
|
||||
//std::cout << __FILE__ << "["<< __LINE__<<"] " << std::endl;
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << "ERRORRRRR "<<ex.what() << "" << std::endl;
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << "ERRORRRRR "<<ex.what() << "" << std::endl;
|
||||
BOOST_THREAD_LOG << "ERRORRRRR "<<ex.what() << "" << BOOST_THREAD_END_LOG;
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << " ERRORRRRR exception thrown" << std::endl;
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << " ERRORRRRR exception thrown" << std::endl;
|
||||
BOOST_THREAD_LOG << " ERRORRRRR exception thrown" << BOOST_THREAD_END_LOG;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
for (int i=0; i< number_of_tests; i++)
|
||||
try
|
||||
{
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
boost::future<int> f1 = boost::async(boost::launch::async, &p1_ex);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
f1.wait();
|
||||
BOOST_ASSERT(f1.get_or(-1)==-1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << "ERRORRRRR "<<ex.what() << "" << std::endl;
|
||||
BOOST_THREAD_LOG << "ERRORRRRR "<<ex.what() << "" << BOOST_THREAD_END_LOG;
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << " ERRORRRRR exception thrown" << std::endl;
|
||||
BOOST_THREAD_LOG << " ERRORRRRR exception thrown" << BOOST_THREAD_END_LOG;
|
||||
return 2;
|
||||
}
|
||||
@@ -75,18 +127,20 @@ int main()
|
||||
boost::future<int> f2 = f1.fallback_to(-1);
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
f2.wait();
|
||||
//std::cout << __FILE__ << "["<< __LINE__<<"] " << std::endl;
|
||||
BOOST_ASSERT(f2.get()==-1);
|
||||
//std::cout << __FILE__ << "["<< __LINE__<<"] " << std::endl;
|
||||
BOOST_THREAD_LOG << "" << BOOST_THREAD_END_LOG;
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << "ERRORRRRR "<<ex.what() << "" << std::endl;
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << "ERRORRRRR "<<ex.what() << "" << std::endl;
|
||||
BOOST_THREAD_LOG << "ERRORRRRR "<<ex.what() << "" << BOOST_THREAD_END_LOG;
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << " ERRORRRRR exception thrown" << std::endl;
|
||||
std::cout << __FILE__ << "["<< __LINE__<<"] " << " ERRORRRRR exception thrown" << std::endl;
|
||||
BOOST_THREAD_LOG << " ERRORRRRR exception thrown" << BOOST_THREAD_END_LOG;
|
||||
return 2;
|
||||
}
|
||||
|
||||
184
example/generic_executor.cpp
Normal file
184
example/generic_executor.cpp
Normal 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();
|
||||
|
||||
|
||||
}
|
||||
@@ -17,11 +17,12 @@
|
||||
#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/serial_executor.hpp>
|
||||
#include <boost/thread/executors/generic_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_ref.hpp>
|
||||
#include <boost/thread/executor.hpp>
|
||||
#include <boost/thread/future.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;
|
||||
{
|
||||
boost::basic_thread_pool ea1(4);
|
||||
boost::serial_executor ea2(ea1);
|
||||
boost::generic_serial_executor ea2(ea1);
|
||||
submit_some(ea2);
|
||||
}
|
||||
#endif
|
||||
|
||||
117
example/generic_serial_executor.cpp
Normal file
117
example/generic_serial_executor.cpp
Normal 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();
|
||||
}
|
||||
118
example/generic_serial_executor_cont.cpp
Normal file
118
example/generic_serial_executor_cont.cpp
Normal 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();
|
||||
}
|
||||
@@ -42,6 +42,14 @@ int main()
|
||||
int result = f2.get();
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -94,6 +94,11 @@ int main()
|
||||
f.get();
|
||||
}
|
||||
#endif
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
boost::future<int> f = compute(-1);
|
||||
f.wait();
|
||||
}
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
boost::future<int> f = compute(0);
|
||||
@@ -104,11 +109,31 @@ int main()
|
||||
boost::future<int&> f = compute_ref(0);
|
||||
std::cout << f.get() << std::endl;
|
||||
}
|
||||
// {
|
||||
// std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
// boost::future<int> f = compute(2);
|
||||
// std::cout << f.get() << std::endl;
|
||||
// }
|
||||
#if __cplusplus > 201103L
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
int i = 0;
|
||||
boost::future<int&> f = boost::make_ready_future(std::ref(i));
|
||||
std::cout << f.get() << std::endl;
|
||||
}
|
||||
#endif
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
int i = 0;
|
||||
boost::future<int&> f = boost::make_ready_future(boost::ref(i));
|
||||
std::cout << f.get() << std::endl;
|
||||
}
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
const int i = 0;
|
||||
boost::future<int const&> f = boost::make_ready_future(boost::cref(i));
|
||||
std::cout << f.get() << std::endl;
|
||||
}
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
boost::future<int> f = compute(2);
|
||||
std::cout << f.get() << std::endl;
|
||||
}
|
||||
{
|
||||
std::cout << __FILE__ << " "<< __LINE__ << std::endl;
|
||||
boost::shared_future<int> f = shared_compute(0);
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#endif
|
||||
#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;
|
||||
try {
|
||||
@@ -32,22 +32,22 @@ void producer(the_ostream &mos, boost::sync_queue<int> & sbq)
|
||||
{
|
||||
sbq.push(i);
|
||||
//sbq << i;
|
||||
mos << "push(" << i << ") "<< sbq.size()<<"\n";
|
||||
//mos << "push(" << i << ") "<< sbq.size()<<"\n";
|
||||
this_thread::sleep_for(chrono::milliseconds(200));
|
||||
}
|
||||
}
|
||||
catch(sync_queue_is_closed&)
|
||||
{
|
||||
mos << "closed !!!\n";
|
||||
//mos << "closed !!!\n";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
mos << "exception !!!\n";
|
||||
//mos << "exception !!!\n";
|
||||
}
|
||||
}
|
||||
|
||||
void consumer(
|
||||
the_ostream &mos,
|
||||
the_ostream & /*mos*/,
|
||||
boost::sync_queue<int> & sbq)
|
||||
{
|
||||
using namespace boost;
|
||||
@@ -57,21 +57,21 @@ void consumer(
|
||||
int r;
|
||||
sbq.pull(r);
|
||||
//sbq >> r;
|
||||
mos << i << " pull(" << r << ") "<< sbq.size()<<"\n";
|
||||
//mos << i << " pull(" << r << ") "<< sbq.size()<<"\n";
|
||||
|
||||
this_thread::sleep_for(chrono::milliseconds(250));
|
||||
}
|
||||
}
|
||||
catch(sync_queue_is_closed&)
|
||||
{
|
||||
mos << "closed !!!\n";
|
||||
//mos << "closed !!!\n";
|
||||
}
|
||||
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;
|
||||
try {
|
||||
@@ -81,17 +81,17 @@ void consumer2(the_ostream &mos, boost::sync_queue<int> & sbq)
|
||||
queue_op_status st = sbq.try_pull(r);
|
||||
if (queue_op_status::closed == st) break;
|
||||
if (queue_op_status::success == st) {
|
||||
mos << i << " pull(" << r << ")\n";
|
||||
//mos << i << " pull(" << r << ")\n";
|
||||
}
|
||||
this_thread::sleep_for(chrono::milliseconds(250));
|
||||
}
|
||||
}
|
||||
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;
|
||||
try {
|
||||
@@ -100,13 +100,13 @@ void consumer3(the_ostream &mos, boost::sync_queue<int> & sbq)
|
||||
int r;
|
||||
queue_op_status res = sbq.wait_pull(r);
|
||||
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));
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
mos << "exception !!!\n";
|
||||
//mos << "exception !!!\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
#include <boost/static_assert.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;
|
||||
try {
|
||||
@@ -35,22 +35,22 @@ void producer(the_ostream &mos, boost::queue_back<int> sbq)
|
||||
{
|
||||
sbq.push(i);
|
||||
//sbq << i;
|
||||
mos << "push(" << i << ") " << sbq.size() <<"\n";
|
||||
//mos << "push(" << i << ") " << sbq.size() <<"\n";
|
||||
this_thread::sleep_for(chrono::milliseconds(200));
|
||||
}
|
||||
}
|
||||
catch(sync_queue_is_closed&)
|
||||
{
|
||||
mos << "closed !!!\n";
|
||||
//mos << "closed !!!\n";
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
mos << "exception !!!\n";
|
||||
//mos << "exception !!!\n";
|
||||
}
|
||||
}
|
||||
|
||||
void consumer(
|
||||
the_ostream &mos,
|
||||
the_ostream &/*mos*/,
|
||||
boost::queue_front<int> sbq)
|
||||
{
|
||||
using namespace boost;
|
||||
@@ -60,21 +60,21 @@ void consumer(
|
||||
int r;
|
||||
sbq.pull(r);
|
||||
//sbq >> r;
|
||||
mos << i << " pull(" << r << ") " << sbq.size() <<"\n";
|
||||
//mos << i << " pull(" << r << ") " << sbq.size() <<"\n";
|
||||
|
||||
this_thread::sleep_for(chrono::milliseconds(250));
|
||||
}
|
||||
}
|
||||
catch(sync_queue_is_closed&)
|
||||
{
|
||||
mos << "closed !!!\n";
|
||||
//mos << "closed !!!\n";
|
||||
}
|
||||
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;
|
||||
try {
|
||||
@@ -84,17 +84,17 @@ void consumer2(the_ostream &mos, boost::queue_front<int> sbq)
|
||||
queue_op_status st = sbq.try_pull(r);
|
||||
if (queue_op_status::closed == st) break;
|
||||
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));
|
||||
}
|
||||
}
|
||||
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;
|
||||
try {
|
||||
@@ -103,13 +103,13 @@ void consumer3(the_ostream &mos, boost::queue_front<int> sbq)
|
||||
int r;
|
||||
queue_op_status res = sbq.wait_pull(r);
|
||||
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));
|
||||
}
|
||||
}
|
||||
catch(...)
|
||||
{
|
||||
mos << "exception !!!\n";
|
||||
//mos << "exception !!!\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ counter c;
|
||||
|
||||
void change_count()
|
||||
{
|
||||
std::cout << "count == " << c.increment() << std::endl;
|
||||
//std::cout << "count == " << c.increment() << std::endl;
|
||||
}
|
||||
|
||||
int main(int, char*[])
|
||||
|
||||
106
example/serial_executor.cpp
Normal file
106
example/serial_executor.cpp
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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/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;
|
||||
}
|
||||
template <class Executor>
|
||||
void submit_some(boost::serial_executor<Executor>& tp)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
void at_th_entry(boost::basic_thread_pool& )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int test_executor_adaptor()
|
||||
{
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
{
|
||||
boost::basic_thread_pool ea1(4);
|
||||
boost::serial_executor<boost::basic_thread_pool> ea2(ea1);
|
||||
submit_some(ea2);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
std::cout << "ERROR= " << ex.what() << "" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::cout << " ERROR= exception thrown" << std::endl;
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int main()
|
||||
{
|
||||
return test_executor_adaptor();
|
||||
}
|
||||
113
example/serial_executor_cont.cpp
Normal file
113
example/serial_executor_cont.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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/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;
|
||||
}
|
||||
|
||||
template < class Executor>
|
||||
void submit_some(boost::serial_executor_cont<Executor>& tp)
|
||||
{
|
||||
//std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
//std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
tp.submit(&p2);
|
||||
}
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
//std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
tp.submit(&p1);
|
||||
}
|
||||
//std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void at_th_entry(boost::basic_thread_pool& )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
int test_executor_adaptor()
|
||||
{
|
||||
// std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
// std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
{
|
||||
boost::basic_thread_pool ea1(4);
|
||||
boost::serial_executor_cont<boost::basic_thread_pool> 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();
|
||||
}
|
||||
86
example/this_executor.cpp
Normal file
86
example/this_executor.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// 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_THREAD_ID
|
||||
|
||||
#include <boost/thread/caller_context.hpp>
|
||||
#include <boost/thread/executors/basic_thread_pool.hpp>
|
||||
#include <boost/thread/executors/generic_executor_ref.hpp>
|
||||
#include <boost/smart_ptr/shared_ptr.hpp>
|
||||
#include <boost/smart_ptr/make_shared.hpp>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include <boost/thread/caller_context.hpp>
|
||||
|
||||
struct current_executor_state_type {
|
||||
boost::shared_ptr<boost::generic_executor_ref> current_executor_ptr;
|
||||
|
||||
template <class Executor>
|
||||
void set_current_executor(Executor& ex)
|
||||
{
|
||||
current_executor_ptr = boost::make_shared<boost::generic_executor_ref>(ex);
|
||||
}
|
||||
boost::generic_executor_ref current_executor()
|
||||
{
|
||||
if (current_executor_ptr)
|
||||
return *current_executor_ptr;
|
||||
else
|
||||
throw "";
|
||||
}
|
||||
};
|
||||
|
||||
thread_local current_executor_state_type current_executor_state;
|
||||
|
||||
boost::generic_executor_ref current_executor()
|
||||
{
|
||||
return current_executor_state.current_executor();
|
||||
}
|
||||
|
||||
void p2()
|
||||
{
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
}
|
||||
|
||||
|
||||
void p1()
|
||||
{
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
|
||||
current_executor().submit(&p2);
|
||||
boost::this_thread::sleep_for(boost::chrono::milliseconds(400));
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
|
||||
boost::basic_thread_pool tp(4,
|
||||
// at_thread_entry
|
||||
[](boost::basic_thread_pool& pool)
|
||||
{
|
||||
current_executor_state.set_current_executor(pool);
|
||||
}
|
||||
);
|
||||
|
||||
tp.submit(&p1);
|
||||
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(5));
|
||||
|
||||
std::cout << BOOST_CONTEXTOF << std::endl;
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
@@ -59,6 +59,7 @@ int main()
|
||||
threads.remove_thread(th);
|
||||
BOOST_TEST(! threads.is_thread_in(th));
|
||||
th->join();
|
||||
delete th;
|
||||
}
|
||||
{
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// (C) Copyright 2013 Vicente J. Botet Escriba
|
||||
// (C) Copyright 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)
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#endif
|
||||
#include <boost/current_function.hpp>
|
||||
#include <boost/io/ios_state.hpp>
|
||||
#include <iomanip>
|
||||
|
||||
#include <boost/config/abi_prefix.hpp>
|
||||
@@ -43,9 +44,11 @@ namespace boost
|
||||
#endif
|
||||
{
|
||||
io::ios_flags_saver ifs(os);
|
||||
os << ctx.filename << "["
|
||||
os << std::setw(50) << ctx.filename << "["
|
||||
<< std::setw(4) << std::right << std::dec<< ctx.lineno << "] ";
|
||||
#if defined BOOST_THREAD_USES_LOG_CURRENT_FUNCTION
|
||||
os << ctx.func << " " ;
|
||||
#endif
|
||||
}
|
||||
return os;
|
||||
}
|
||||
|
||||
@@ -27,12 +27,12 @@ namespace detail
|
||||
|
||||
template <typename Queue>
|
||||
class deque_adaptor_copyable_only :
|
||||
public boost::deque_base<typename Queue::value_type>
|
||||
public boost::deque_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
Queue queue;
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
deque_adaptor_copyable_only() {}
|
||||
@@ -63,12 +63,12 @@ namespace detail
|
||||
};
|
||||
template <typename Queue>
|
||||
class deque_adaptor_movable_only :
|
||||
public boost::deque_base<typename Queue::value_type>
|
||||
public boost::deque_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
Queue queue;
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
|
||||
@@ -102,12 +102,12 @@ namespace detail
|
||||
|
||||
template <typename Queue>
|
||||
class deque_adaptor_copyable_and_movable :
|
||||
public boost::deque_base<typename Queue::value_type>
|
||||
public boost::deque_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
Queue queue;
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace detail
|
||||
{
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~deque_adaptor() {};
|
||||
};
|
||||
|
||||
@@ -27,12 +27,12 @@ namespace concurrent
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType>
|
||||
class deque_base_copyable_only
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~deque_base_copyable_only() {};
|
||||
@@ -58,16 +58,16 @@ namespace detail
|
||||
virtual queue_op_status nonblocking_pull_front(value_type&) = 0;
|
||||
|
||||
virtual queue_op_status wait_push_back(const value_type& x) = 0;
|
||||
virtual queue_op_status wait_pull_front(ValueType& elem) = 0;
|
||||
virtual queue_op_status wait_pull_front(value_type& elem) = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType>
|
||||
class deque_base_movable_only
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~deque_base_movable_only() {};
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace detail
|
||||
|
||||
virtual queue_op_status nonblocking_pull_front(value_type&) = 0;
|
||||
|
||||
virtual queue_op_status wait_pull_front(ValueType& elem) = 0;
|
||||
virtual queue_op_status wait_pull_front(value_type& elem) = 0;
|
||||
|
||||
virtual void push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
virtual queue_op_status try_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
@@ -97,12 +97,12 @@ namespace detail
|
||||
};
|
||||
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType>
|
||||
class deque_base_copyable_and_movable
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~deque_base_copyable_and_movable() {};
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace detail
|
||||
virtual queue_op_status nonblocking_pull_front(value_type&) = 0;
|
||||
|
||||
virtual queue_op_status wait_push_back(const value_type& x) = 0;
|
||||
virtual queue_op_status wait_pull_front(ValueType& elem) = 0;
|
||||
virtual queue_op_status wait_pull_front(value_type& elem) = 0;
|
||||
|
||||
virtual void push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
virtual queue_op_status try_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
@@ -137,7 +137,7 @@ namespace detail
|
||||
virtual queue_op_status wait_push_back(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
};
|
||||
|
||||
template <class T,
|
||||
template <class T, class ST,
|
||||
#if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
#if defined __GNUC__ && ! defined __clang__
|
||||
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) || !defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
@@ -166,28 +166,28 @@ namespace detail
|
||||
>
|
||||
struct deque_base;
|
||||
|
||||
template <class T>
|
||||
struct deque_base<T, true, true> {
|
||||
typedef deque_base_copyable_and_movable<T> type;
|
||||
template <class T, class ST>
|
||||
struct deque_base<T, ST, true, true> {
|
||||
typedef deque_base_copyable_and_movable<T, ST> type;
|
||||
};
|
||||
template <class T>
|
||||
struct deque_base<T, true, false> {
|
||||
typedef deque_base_copyable_only<T> type;
|
||||
template <class T, class ST>
|
||||
struct deque_base<T, ST, true, false> {
|
||||
typedef deque_base_copyable_only<T, ST> type;
|
||||
};
|
||||
template <class T>
|
||||
struct deque_base<T, false, true> {
|
||||
typedef deque_base_movable_only<T> type;
|
||||
template <class T, class ST>
|
||||
struct deque_base<T, ST, false, true> {
|
||||
typedef deque_base_movable_only<T, ST> type;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
template <class ValueType, class SizeType=std::size_t>
|
||||
class deque_base :
|
||||
public detail::deque_base<ValueType>::type
|
||||
public detail::deque_base<ValueType, SizeType>::type
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~deque_base() {};
|
||||
};
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace detail
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef Queue underlying_queue_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
typedef queue_op_status op_status;
|
||||
|
||||
typedef typename chrono::steady_clock clock;
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace detail
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef Queue underlying_queue_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
typedef queue_op_status op_status;
|
||||
|
||||
typedef typename chrono::steady_clock clock;
|
||||
|
||||
@@ -27,12 +27,12 @@ namespace detail
|
||||
|
||||
template <typename Queue>
|
||||
class queue_adaptor_copyable_only :
|
||||
public boost::queue_base<typename Queue::value_type>
|
||||
public boost::queue_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
Queue queue;
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
queue_adaptor_copyable_only() {}
|
||||
@@ -63,12 +63,12 @@ namespace detail
|
||||
};
|
||||
template <typename Queue>
|
||||
class queue_adaptor_movable_only :
|
||||
public boost::queue_base<typename Queue::value_type>
|
||||
public boost::queue_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
Queue queue;
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
|
||||
@@ -102,12 +102,12 @@ namespace detail
|
||||
|
||||
template <typename Queue>
|
||||
class queue_adaptor_copyable_and_movable :
|
||||
public boost::queue_base<typename Queue::value_type>
|
||||
public boost::queue_base<typename Queue::value_type, typename Queue::size_type>
|
||||
{
|
||||
Queue queue;
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
|
||||
@@ -195,7 +195,7 @@ namespace detail
|
||||
{
|
||||
public:
|
||||
typedef typename Queue::value_type value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef typename Queue::size_type size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~queue_adaptor() {};
|
||||
};
|
||||
|
||||
@@ -27,12 +27,12 @@ namespace concurrent
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType>
|
||||
class queue_base_copyable_only
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~queue_base_copyable_only() {};
|
||||
@@ -62,12 +62,12 @@ namespace detail
|
||||
|
||||
};
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType>
|
||||
class queue_base_movable_only
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~queue_base_movable_only() {};
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace detail
|
||||
|
||||
virtual queue_op_status nonblocking_pull(value_type&) = 0;
|
||||
|
||||
virtual queue_op_status wait_pull(ValueType& elem) = 0;
|
||||
virtual queue_op_status wait_pull(value_type& elem) = 0;
|
||||
|
||||
virtual void push(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
virtual queue_op_status try_push(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
@@ -97,12 +97,12 @@ namespace detail
|
||||
};
|
||||
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType>
|
||||
class queue_base_copyable_and_movable
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~queue_base_copyable_and_movable() {};
|
||||
|
||||
@@ -129,7 +129,7 @@ namespace detail
|
||||
virtual queue_op_status nonblocking_pull(value_type&) = 0;
|
||||
|
||||
virtual queue_op_status wait_push(const value_type& x) = 0;
|
||||
virtual queue_op_status wait_pull(ValueType& elem) = 0;
|
||||
virtual queue_op_status wait_pull(value_type& elem) = 0;
|
||||
|
||||
virtual void push(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
virtual queue_op_status try_push(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
@@ -137,7 +137,7 @@ namespace detail
|
||||
virtual queue_op_status wait_push(BOOST_THREAD_RV_REF(value_type) x) = 0;
|
||||
};
|
||||
|
||||
template <class T,
|
||||
template <class T, class ST,
|
||||
#if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
#if defined __GNUC__ && ! defined __clang__
|
||||
#if __GNUC__ < 4 || (__GNUC__ == 4 && __GNUC_MINOR__ < 5) || !defined(__GXX_EXPERIMENTAL_CXX0X__)
|
||||
@@ -166,28 +166,28 @@ namespace detail
|
||||
>
|
||||
struct queue_base;
|
||||
|
||||
template <class T>
|
||||
struct queue_base<T, true, true> {
|
||||
typedef queue_base_copyable_and_movable<T> type;
|
||||
template <class T, class ST>
|
||||
struct queue_base<T, ST, true, true> {
|
||||
typedef queue_base_copyable_and_movable<T, ST> type;
|
||||
};
|
||||
template <class T>
|
||||
struct queue_base<T, true, false> {
|
||||
typedef queue_base_copyable_only<T> type;
|
||||
template <class T, class ST>
|
||||
struct queue_base<T, ST, true, false> {
|
||||
typedef queue_base_copyable_only<T, ST> type;
|
||||
};
|
||||
template <class T>
|
||||
struct queue_base<T, false, true> {
|
||||
typedef queue_base_movable_only<T> type;
|
||||
template <class T, class ST>
|
||||
struct queue_base<T, ST, false, true> {
|
||||
typedef queue_base_movable_only<T, ST> type;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
template <typename ValueType>
|
||||
template <typename ValueType, class SizeType=std::size_t>
|
||||
class queue_base :
|
||||
public detail::queue_base<ValueType>::type
|
||||
public detail::queue_base<ValueType, SizeType>::type
|
||||
{
|
||||
public:
|
||||
typedef ValueType value_type;
|
||||
typedef std::size_t size_type;
|
||||
typedef SizeType size_type;
|
||||
// Constructors/Assignment/Destructors
|
||||
virtual ~queue_base() {};
|
||||
};
|
||||
|
||||
@@ -43,12 +43,13 @@ namespace detail {
|
||||
Compare _compare;
|
||||
public:
|
||||
typedef Type value_type;
|
||||
typedef typename Container::size_type size_type;
|
||||
|
||||
explicit priority_queue(const Compare& compare = Compare())
|
||||
: _elements(), _compare(compare)
|
||||
{ }
|
||||
|
||||
std::size_t size() const
|
||||
size_type size() const
|
||||
{
|
||||
return _elements.size();
|
||||
}
|
||||
|
||||
@@ -106,6 +106,11 @@ namespace detail
|
||||
template <class Rep, class Period>
|
||||
void push(const T& elem, chrono::duration<Rep,Period> const& dura);
|
||||
|
||||
template <class Duration>
|
||||
void push(BOOST_THREAD_RV_REF(T) elem, chrono::time_point<clock,Duration> const& tp);
|
||||
template <class Rep, class Period>
|
||||
void push(BOOST_THREAD_RV_REF(T) elem, chrono::duration<Rep,Period> const& dura);
|
||||
|
||||
template <class Duration>
|
||||
queue_op_status try_push(const T& elem, chrono::time_point<clock,Duration> const& tp);
|
||||
template <class Rep, class Period>
|
||||
@@ -158,6 +163,22 @@ namespace detail
|
||||
push(elem, clock::now() + dura);
|
||||
}
|
||||
|
||||
template <class T, class Clock>
|
||||
template <class Duration>
|
||||
void sync_timed_queue<T, Clock>::push(BOOST_THREAD_RV_REF(T) elem, chrono::time_point<clock,Duration> const& tp)
|
||||
{
|
||||
super::push(stype(boost::move(elem),tp));
|
||||
}
|
||||
|
||||
template <class T, class Clock>
|
||||
template <class Rep, class Period>
|
||||
void sync_timed_queue<T, Clock>::push(BOOST_THREAD_RV_REF(T) elem, chrono::duration<Rep,Period> const& dura)
|
||||
{
|
||||
push(boost::move(elem), clock::now() + dura);
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <class T, class Clock>
|
||||
template <class Duration>
|
||||
queue_op_status sync_timed_queue<T, Clock>::try_push(const T& elem, chrono::time_point<clock,Duration> const& tp)
|
||||
|
||||
@@ -173,7 +173,6 @@ namespace boost
|
||||
private:
|
||||
bool start_thread_noexcept();
|
||||
bool start_thread_noexcept(const attributes& attr);
|
||||
//public:
|
||||
void start_thread()
|
||||
{
|
||||
if (!start_thread_noexcept())
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// (C) Copyright 2013 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_DETAIL_WORK_HPP
|
||||
#define BOOST_THREAD_DETAIL_WORK_HPP
|
||||
|
||||
|
||||
#include <boost/thread/detail/nullary_function.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace thread_detail
|
||||
{
|
||||
|
||||
typedef detail::nullary_function<void()> work;
|
||||
}
|
||||
|
||||
} // namespace boost
|
||||
|
||||
#endif // BOOST_THREAD_DETAIL_MEMORY_HPP
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013-2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -13,11 +13,16 @@
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
#include <boost/thread/detail/delete.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/executors/work.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>
|
||||
|
||||
namespace boost
|
||||
@@ -30,125 +35,256 @@ namespace executors
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
private:
|
||||
/// the kind of stored threads are scoped threads to ensure that the threads are joined.
|
||||
/// A move aware vector type
|
||||
typedef scoped_thread<> thread_t;
|
||||
typedef csbl::vector<thread_t> thread_vector;
|
||||
|
||||
/// the thread safe work queue
|
||||
concurrent::sync_queue<work > work_queue;
|
||||
/// A move aware vector
|
||||
thread_vector threads;
|
||||
struct shared_state : enable_shared_from_this<shared_state> {
|
||||
typedef executors::work work;
|
||||
/// the kind of stored threads are scoped threads to ensure that the threads are joined.
|
||||
/// A move aware vector type
|
||||
//typedef scoped_thread<> thread_t;
|
||||
typedef thread thread_t;
|
||||
typedef csbl::vector<thread_t> thread_vector;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
try
|
||||
{
|
||||
work task;
|
||||
if (work_queue.try_pull(task) == queue_op_status::success)
|
||||
{
|
||||
task();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::terminate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Effects: schedule one task or yields
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
void schedule_one_or_yield()
|
||||
{
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
this_thread::yield();
|
||||
}
|
||||
}
|
||||
private:
|
||||
/// the thread safe work queue
|
||||
concurrent::sync_queue<work > work_queue;
|
||||
/// A move aware vector
|
||||
thread_vector threads;
|
||||
unsigned const thread_count;
|
||||
boost::function<void(basic_thread_pool)> at_thread_entry;
|
||||
friend class basic_thread_pool;
|
||||
|
||||
/**
|
||||
* The main loop of the worker threads
|
||||
*/
|
||||
void worker_thread()
|
||||
{
|
||||
try
|
||||
|
||||
public:
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
for(;;)
|
||||
try
|
||||
{
|
||||
work task;
|
||||
queue_op_status st = work_queue.wait_pull(task);
|
||||
if (st == queue_op_status::closed) return;
|
||||
task();
|
||||
if (work_queue.try_pull(task) == queue_op_status::success)
|
||||
{
|
||||
task();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::terminate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
/**
|
||||
* Effects: schedule one task or yields
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
void schedule_one_or_yield()
|
||||
{
|
||||
std::terminate();
|
||||
return;
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
this_thread::yield();
|
||||
}
|
||||
}
|
||||
}
|
||||
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
template <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&) {}
|
||||
private:
|
||||
|
||||
/**
|
||||
* 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:
|
||||
/// basic_thread_pool is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(basic_thread_pool)
|
||||
|
||||
/**
|
||||
* \b Effects: creates a thread pool that runs closures on \c thread_count threads.
|
||||
*
|
||||
* \b Throws: Whatever exception is thrown while initializing the needed resources.
|
||||
*/
|
||||
basic_thread_pool(unsigned const thread_count = thread::hardware_concurrency()+1)
|
||||
: pimpl(make_shared<shared_state>(thread_count))
|
||||
{
|
||||
try
|
||||
{
|
||||
threads.reserve(thread_count);
|
||||
for (unsigned i = 0; i < thread_count; ++i)
|
||||
{
|
||||
#if 1
|
||||
thread th (&basic_thread_pool::worker_thread, this);
|
||||
threads.push_back(thread_t(boost::move(th)));
|
||||
#else
|
||||
threads.push_back(thread_t(&basic_thread_pool::worker_thread, this)); // do not compile
|
||||
#endif
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
close();
|
||||
throw;
|
||||
}
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Effects: creates a thread pool that runs closures on \c thread_count threads
|
||||
* and executes the at_thread_entry function at the entry of each created thread. .
|
||||
@@ -158,61 +294,24 @@ namespace executors
|
||||
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
template <class AtThreadEntry>
|
||||
basic_thread_pool( unsigned const thread_count, AtThreadEntry& at_thread_entry)
|
||||
: pimpl(make_shared<shared_state>(thread_count, at_thread_entry))
|
||||
{
|
||||
try
|
||||
{
|
||||
threads.reserve(thread_count);
|
||||
for (unsigned i = 0; i < thread_count; ++i)
|
||||
{
|
||||
thread th (&basic_thread_pool::worker_thread1<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;
|
||||
}
|
||||
pimpl->init();
|
||||
}
|
||||
#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
|
||||
{
|
||||
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;
|
||||
}
|
||||
pimpl->init();
|
||||
}
|
||||
template <class AtThreadEntry>
|
||||
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
|
||||
{
|
||||
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;
|
||||
}
|
||||
pimpl->init();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Effects: Destroys the thread pool.
|
||||
*
|
||||
@@ -220,9 +319,16 @@ namespace executors
|
||||
*/
|
||||
~basic_thread_pool()
|
||||
{
|
||||
// signal to all the worker threads that there will be no more submissions.
|
||||
close();
|
||||
// joins all the threads as the threads were scoped_threads
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return pimpl->try_executing_one();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -230,10 +336,7 @@ namespace executors
|
||||
*/
|
||||
void join()
|
||||
{
|
||||
for (unsigned i = 0; i < threads.size(); ++i)
|
||||
{
|
||||
threads[i].join();
|
||||
}
|
||||
pimpl->join();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -242,7 +345,7 @@ namespace executors
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
work_queue.close();
|
||||
pimpl->close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -250,7 +353,7 @@ namespace executors
|
||||
*/
|
||||
bool closed()
|
||||
{
|
||||
return work_queue.closed();
|
||||
return pimpl->closed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -269,18 +372,18 @@ namespace executors
|
||||
template <typename Closure>
|
||||
void submit(Closure & closure)
|
||||
{
|
||||
work_queue.push(work(closure));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
#endif
|
||||
void submit(void (*closure)())
|
||||
{
|
||||
work_queue.push(work(closure));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
|
||||
template <typename 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>
|
||||
bool reschedule_until(Pred const& pred)
|
||||
{
|
||||
do {
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (! pred());
|
||||
return true;
|
||||
return pimpl->reschedule_until(pred);
|
||||
}
|
||||
|
||||
void schedule_one_or_yield()
|
||||
{
|
||||
return pimpl->schedule_one_or_yield();
|
||||
}
|
||||
|
||||
private:
|
||||
shared_ptr<shared_state> pimpl;
|
||||
};
|
||||
}
|
||||
using executors::basic_thread_pool;
|
||||
|
||||
@@ -24,8 +24,7 @@ namespace detail
|
||||
class priority_executor_base
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void()> work;
|
||||
//typedef executors::work work;
|
||||
typedef executors::work_pq work;
|
||||
protected:
|
||||
typedef Queue queue_type;
|
||||
queue_type _workq;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2014 Ian Forbed
|
||||
// Copyright (C) 2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <boost/atomic.hpp>
|
||||
#include <boost/function.hpp>
|
||||
|
||||
#include <boost/config/abi_prefix.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace executors
|
||||
@@ -23,11 +25,10 @@ namespace executors
|
||||
namespace detail
|
||||
{
|
||||
template <class Clock=chrono::steady_clock>
|
||||
class scheduled_executor_base : public priority_executor_base<concurrent::sync_timed_queue<boost::function<void()>, Clock > >
|
||||
class scheduled_executor_base : public priority_executor_base<concurrent::sync_timed_queue<executors::work_pq, Clock > >
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void()> work;
|
||||
//typedef executors::work work;
|
||||
typedef executors::work_pq work;
|
||||
typedef Clock clock;
|
||||
typedef typename clock::duration duration;
|
||||
typedef typename clock::time_point time_point;
|
||||
@@ -46,12 +47,12 @@ namespace detail
|
||||
|
||||
void submit_at(work w, const time_point& tp)
|
||||
{
|
||||
this->_workq.push(w, tp);
|
||||
this->_workq.push(boost::move(w), tp);
|
||||
}
|
||||
|
||||
void submit_after(work w, const duration& dura)
|
||||
{
|
||||
this->_workq.push(w, dura+clock::now());
|
||||
this->_workq.push(boost::move(w), dura+clock::now());
|
||||
}
|
||||
|
||||
}; //end class
|
||||
@@ -59,4 +60,7 @@ namespace detail
|
||||
} //end detail namespace
|
||||
} //end executors namespace
|
||||
} //end boost namespace
|
||||
|
||||
#include <boost/config/abi_suffix.hpp>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013,2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -27,8 +27,6 @@ namespace boost
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
|
||||
/// executor is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(executor)
|
||||
executor() {}
|
||||
|
||||
/**
|
||||
@@ -128,7 +126,6 @@ namespace boost
|
||||
bool reschedule_until(Pred const& pred)
|
||||
{
|
||||
do {
|
||||
//schedule_one_or_yield();
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
return false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013,2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -30,9 +30,8 @@ namespace executors
|
||||
/// type-erasure to store the works to do
|
||||
typedef executor::work work;
|
||||
|
||||
/// executor is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(executor_adaptor)
|
||||
|
||||
// executor_adaptor(executor_adaptor const&) = default;
|
||||
// executor_adaptor(executor_adaptor &&) = default;
|
||||
/**
|
||||
* executor_adaptor constructor
|
||||
*/
|
||||
|
||||
150
include/boost/thread/executors/generic_executor.hpp
Normal file
150
include/boost/thread/executors/generic_executor.hpp
Normal 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
|
||||
@@ -13,7 +13,8 @@
|
||||
#include <boost/thread/detail/move.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>
|
||||
|
||||
@@ -99,8 +100,8 @@ namespace boost
|
||||
|
||||
template<typename Executor>
|
||||
generic_executor_ref(Executor& ex)
|
||||
//: ex(make_shared<executor_ref<Executor> >(ex)) // todo check why this doesn't works with C++03
|
||||
: ex( new executor_ref<Executor>(ex) )
|
||||
//: ex(make_shared<executor_ref<typename decay<Executor>::type> >(ex)) // todo check why this doesn't work with C++03
|
||||
: ex( new executor_ref<typename decay<Executor>::type>(ex) )
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
284
include/boost/thread/executors/generic_serial_executor.hpp
Normal file
284
include/boost/thread/executors/generic_serial_executor.hpp
Normal 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
|
||||
265
include/boost/thread/executors/generic_serial_executor_cont.hpp
Normal file
265
include/boost/thread/executors/generic_serial_executor_cont.hpp
Normal 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
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -14,6 +14,9 @@
|
||||
#include <boost/thread/detail/move.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>
|
||||
|
||||
namespace boost
|
||||
@@ -25,142 +28,227 @@ namespace executors
|
||||
public:
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
bool closed_;
|
||||
mutable mutex mtx_;
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
private:
|
||||
|
||||
struct shared_state {
|
||||
typedef executors::work work;
|
||||
|
||||
bool closed_;
|
||||
mutable mutex mtx_;
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// shared_state is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(shared_state)
|
||||
|
||||
/**
|
||||
* \b Effects: creates a inline executor that runs closures immediately.
|
||||
*
|
||||
* \b Throws: Nothing.
|
||||
*/
|
||||
shared_state()
|
||||
: closed_(false)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* \b Effects: Destroys the inline executor.
|
||||
*
|
||||
* \b Synchronization: The completion of all the closures happen before the completion of the \c inline_executor destructor.
|
||||
*/
|
||||
~shared_state()
|
||||
{
|
||||
// signal to all the worker thread that there will be no more submissions.
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Effects: close the \c inline_executor for submissions.
|
||||
* The loop will work until there is no more closures to run.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
lock_guard<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:
|
||||
/// 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 Throws: Nothing.
|
||||
*/
|
||||
inline_executor()
|
||||
: closed_(false)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* \b Effects: Destroys the inline executor.
|
||||
*
|
||||
* \b Synchronization: The completion of all the closures happen before the completion of the \c inline_executor destructor.
|
||||
*/
|
||||
~inline_executor()
|
||||
{
|
||||
// signal to all the worker thread that there will be no more submissions.
|
||||
close();
|
||||
}
|
||||
/**
|
||||
* \b Effects: close the \c inline_executor for submissions.
|
||||
* The loop will work until there is no more closures to run.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
pimpl->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Effects: close the \c inline_executor for submissions.
|
||||
* The loop will work until there is no more closures to run.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
lock_guard<mutex> lk(mtx_);
|
||||
closed_ = true;
|
||||
}
|
||||
/**
|
||||
* \b Returns: whether the executor is closed for submissions.
|
||||
*/
|
||||
bool closed() const
|
||||
{
|
||||
return pimpl->closed();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Returns: whether the pool is closed for submissions.
|
||||
*/
|
||||
bool closed(lock_guard<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 inline_executor will call \c std::terminate, as is the case with threads.
|
||||
*
|
||||
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
|
||||
*
|
||||
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
|
||||
* Whatever exception that can be throw while storing the closure.
|
||||
*/
|
||||
/**
|
||||
* \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
|
||||
*
|
||||
* \b Effects: The specified \c closure will be scheduled for execution at some point in the future.
|
||||
* If invoked closure throws an exception the \c inline_executor will call \c std::terminate, as is the case with threads.
|
||||
*
|
||||
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
|
||||
*
|
||||
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
|
||||
* Whatever exception that can be throw while storing the closure.
|
||||
*/
|
||||
|
||||
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
template <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;
|
||||
}
|
||||
}
|
||||
template <typename Closure>
|
||||
void submit(Closure & closure)
|
||||
{
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
#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;
|
||||
}
|
||||
}
|
||||
void submit(void (*closure)())
|
||||
{
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
template <typename Closure>
|
||||
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
|
||||
{
|
||||
pimpl->submit(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& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return pimpl->try_executing_one();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Requires: This must be called from an scheduled task.
|
||||
*
|
||||
* \b Effects: reschedule functions until pred()
|
||||
*/
|
||||
template <typename Pred>
|
||||
bool reschedule_until(Pred const& p)
|
||||
{
|
||||
return pimpl->reschedule_until(p);
|
||||
}
|
||||
private:
|
||||
shared_ptr<shared_state> pimpl;
|
||||
};
|
||||
}
|
||||
using executors::inline_executor;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013,2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -17,6 +17,9 @@
|
||||
#include <boost/thread/concurrent_queues/sync_queue.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>
|
||||
|
||||
namespace boost
|
||||
@@ -30,59 +33,170 @@ namespace executors
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
private:
|
||||
/// the thread safe work queue
|
||||
concurrent::sync_queue<work > work_queue;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
work task;
|
||||
try
|
||||
struct shared_state {
|
||||
typedef executors::work work;
|
||||
/// the thread safe work queue
|
||||
concurrent::sync_queue<work > work_queue;
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
if (work_queue.try_pull(task) == queue_op_status::success)
|
||||
work task;
|
||||
try
|
||||
{
|
||||
if (work_queue.try_pull(task) == queue_op_status::success)
|
||||
{
|
||||
task();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::terminate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Effects: schedule one task or yields
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
void schedule_one_or_yield()
|
||||
{
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
/// loop_executor is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(shared_state)
|
||||
|
||||
/**
|
||||
* \b Effects: creates a thread pool that runs closures using one of its closure-executing methods.
|
||||
*
|
||||
* \b Throws: Whatever exception is thrown while initializing the needed resources.
|
||||
*/
|
||||
shared_state()
|
||||
{
|
||||
}
|
||||
/**
|
||||
* \b Effects: Destroys the thread pool.
|
||||
*
|
||||
* \b Synchronization: The completion of all the closures happen before the completion of the \c loop_executor destructor.
|
||||
*/
|
||||
~shared_state()
|
||||
{
|
||||
// signal to all the worker thread that there will be no more submissions.
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* The main loop of the worker thread
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
while (!closed())
|
||||
{
|
||||
schedule_one_or_yield();
|
||||
}
|
||||
while (try_executing_one())
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Effects: close the \c loop_executor for submissions.
|
||||
* The loop will work until there is no more closures to run.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
work_queue.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Returns: whether the pool is closed for submissions.
|
||||
*/
|
||||
bool closed()
|
||||
{
|
||||
return work_queue.closed();
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
|
||||
*
|
||||
* \b Effects: The specified \c closure will be scheduled for execution at some point in the future.
|
||||
* If invoked closure throws an exception the \c loop_executor will call \c std::terminate, as is the case with threads.
|
||||
*
|
||||
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
|
||||
*
|
||||
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
|
||||
* Whatever exception that can be throw while storing the closure.
|
||||
*/
|
||||
|
||||
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
|
||||
template <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();
|
||||
return true;
|
||||
q.pop_front();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
std::terminate();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
private:
|
||||
/**
|
||||
* Effects: schedule one task or yields
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
void schedule_one_or_yield()
|
||||
{
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
this_thread::yield();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
public:
|
||||
/// loop_executor is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(loop_executor)
|
||||
|
||||
/**
|
||||
* \b Effects: creates a thread pool that runs closures using one of its closure-executing methods.
|
||||
*
|
||||
* \b Throws: Whatever exception is thrown while initializing the needed resources.
|
||||
*/
|
||||
loop_executor()
|
||||
: pimpl(make_shared<shared_state>())
|
||||
{
|
||||
}
|
||||
/**
|
||||
@@ -92,22 +206,33 @@ namespace executors
|
||||
*/
|
||||
~loop_executor()
|
||||
{
|
||||
// signal to all the worker thread that there will be no more submissions.
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return pimpl->try_executing_one();
|
||||
}
|
||||
// /**
|
||||
// * Effects: schedule one task or yields
|
||||
// * Throws: whatever the current task constructor throws or the task() throws.
|
||||
// */
|
||||
// void schedule_one_or_yield()
|
||||
// {
|
||||
// return pimpl->schedule_one_or_yield();
|
||||
// }
|
||||
|
||||
|
||||
/**
|
||||
* The main loop of the worker thread
|
||||
*/
|
||||
void loop()
|
||||
{
|
||||
while (!closed())
|
||||
{
|
||||
schedule_one_or_yield();
|
||||
}
|
||||
while (try_executing_one())
|
||||
{
|
||||
}
|
||||
pimpl->loop();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,7 +241,7 @@ namespace executors
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
work_queue.close();
|
||||
pimpl->close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -124,7 +249,7 @@ namespace executors
|
||||
*/
|
||||
bool closed()
|
||||
{
|
||||
return work_queue.closed();
|
||||
return pimpl->closed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -143,18 +268,18 @@ namespace executors
|
||||
template <typename Closure>
|
||||
void submit(Closure & closure)
|
||||
{
|
||||
work_queue.push(work(closure));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
#endif
|
||||
void submit(void (*closure)())
|
||||
{
|
||||
work_queue.push(work(closure));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
|
||||
template <typename 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>
|
||||
bool reschedule_until(Pred const& pred)
|
||||
{
|
||||
do {
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (! pred());
|
||||
return true;
|
||||
return pimpl->reschedule_until(pred);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -179,15 +298,10 @@ namespace executors
|
||||
*/
|
||||
void run_queued_closures()
|
||||
{
|
||||
sync_queue<work>::underlying_queue_type q = work_queue.underlying_queue();
|
||||
while (! q.empty())
|
||||
{
|
||||
work& task = q.front();
|
||||
task();
|
||||
q.pop_front();
|
||||
}
|
||||
pimpl->run_queued_closures();
|
||||
}
|
||||
|
||||
private:
|
||||
shared_ptr<shared_state> pimpl;
|
||||
};
|
||||
}
|
||||
using executors::loop_executor;
|
||||
|
||||
@@ -9,36 +9,123 @@
|
||||
#define BOOST_THREAD_EXECUTORS_SCHEDULED_THREAD_POOL_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 executors
|
||||
{
|
||||
|
||||
class scheduled_thread_pool : public detail::scheduled_executor_base<>
|
||||
template <class Clock = chrono::steady_clock>
|
||||
class scheduled_thread_pool
|
||||
{
|
||||
private:
|
||||
thread_group _workers;
|
||||
public:
|
||||
|
||||
scheduled_thread_pool(size_t num_threads) : super()
|
||||
{
|
||||
for(size_t i = 0; i < num_threads; i++)
|
||||
struct shared_state : public detail::scheduled_executor_base<> {
|
||||
|
||||
/// 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()
|
||||
{
|
||||
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;
|
||||
}; //end class
|
||||
/**
|
||||
* \b Returns: whether the pool is closed for submissions.
|
||||
*/
|
||||
bool closed()
|
||||
{
|
||||
return pimpl->closed();
|
||||
}
|
||||
|
||||
void submit_at(work w, const time_point& tp)
|
||||
{
|
||||
return pimpl->submit_at(boost::move(w), tp);
|
||||
}
|
||||
|
||||
void submit_after(work w, const duration& d)
|
||||
{
|
||||
return pimpl->submit_after(boost::move(w), d);
|
||||
}
|
||||
private:
|
||||
shared_ptr<shared_state> pimpl;
|
||||
};
|
||||
} //end executors namespace
|
||||
|
||||
using executors::scheduled_thread_pool;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -13,6 +13,8 @@
|
||||
#include <boost/chrono/time_point.hpp>
|
||||
#include <boost/chrono/duration.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>
|
||||
|
||||
@@ -36,7 +38,7 @@ namespace boost
|
||||
}
|
||||
|
||||
private:
|
||||
Executor& ex;
|
||||
Executor ex;
|
||||
Function funct;
|
||||
};
|
||||
|
||||
@@ -100,8 +102,8 @@ namespace boost
|
||||
}
|
||||
|
||||
private:
|
||||
Scheduler& sch;
|
||||
Executor& ex;
|
||||
Scheduler sch;
|
||||
Executor ex;
|
||||
typename clock::time_point tp;
|
||||
bool is_closed;
|
||||
};
|
||||
@@ -150,8 +152,8 @@ namespace boost
|
||||
}
|
||||
|
||||
private:
|
||||
Scheduler& sch;
|
||||
Executor& ex;
|
||||
Scheduler sch;
|
||||
Executor ex;
|
||||
}; //end class
|
||||
|
||||
/// Wraps a reference to a @c Scheduler providing an @c Executor that
|
||||
@@ -208,7 +210,7 @@ namespace boost
|
||||
}
|
||||
|
||||
private:
|
||||
Scheduler& sch;
|
||||
Scheduler sch;
|
||||
time_point tp;
|
||||
bool is_closed;
|
||||
}; //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
|
||||
/// that submit the work at/after a specific time/duration respectively.
|
||||
template <class Clock = chrono::steady_clock>
|
||||
class scheduler : public detail::scheduled_executor_base<Clock>
|
||||
class scheduler
|
||||
{
|
||||
public:
|
||||
typedef typename detail::scheduled_executor_base<Clock>::work work;
|
||||
private:
|
||||
|
||||
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 typename clock::duration duration;
|
||||
typedef typename clock::time_point time_point;
|
||||
|
||||
scheduler()
|
||||
: super(),
|
||||
thr(&super::loop, this) {}
|
||||
: pimpl(make_shared<shared_state>())
|
||||
{}
|
||||
|
||||
~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>
|
||||
scheduler_executor_wrapper<scheduler, Ex> on(Ex& ex)
|
||||
{
|
||||
@@ -250,13 +301,10 @@ namespace boost
|
||||
{
|
||||
return at_executor<scheduler>(*this, tp);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef detail::scheduled_executor_base<Clock> super;
|
||||
thread thr;
|
||||
shared_ptr<shared_state> pimpl;
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
using executors::resubmitter;
|
||||
using executors::resubmit;
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace executors
|
||||
class scheduling_adpator : public detail::scheduled_executor_base<>
|
||||
{
|
||||
private:
|
||||
Executor& _exec;
|
||||
Executor _exec;
|
||||
thread _scheduler;
|
||||
public:
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -14,116 +14,168 @@
|
||||
#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_ref.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/config/abi_prefix.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
template <class Executor>
|
||||
class serial_executor
|
||||
{
|
||||
public:
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
private:
|
||||
typedef scoped_thread<> thread_t;
|
||||
|
||||
/// the thread safe work queue
|
||||
concurrent::sync_queue<work > work_queue;
|
||||
generic_executor_ref ex;
|
||||
thread_t thr;
|
||||
struct shared_state {
|
||||
typedef executors::work work;
|
||||
typedef scoped_thread<> thread_t;
|
||||
|
||||
struct try_executing_one_task {
|
||||
work& task;
|
||||
boost::promise<void> &p;
|
||||
try_executing_one_task(work& task, boost::promise<void> &p)
|
||||
: task(task), p(p) {}
|
||||
void operator()() {
|
||||
try {
|
||||
task();
|
||||
p.set_value();
|
||||
} catch (...)
|
||||
/// the thread safe work queue
|
||||
concurrent::sync_queue<work > work_queue;
|
||||
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.
|
||||
*/
|
||||
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:
|
||||
/// 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 Throws: Whatever exception is thrown while initializing the needed resources.
|
||||
*/
|
||||
template <class Executor>
|
||||
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()
|
||||
{
|
||||
// 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()
|
||||
{
|
||||
work_queue.close();
|
||||
pimpl->close();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -151,7 +219,7 @@ namespace executors
|
||||
*/
|
||||
bool closed()
|
||||
{
|
||||
return work_queue.closed();
|
||||
return pimpl->closed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,37 +238,31 @@ namespace executors
|
||||
template <typename Closure>
|
||||
void submit(Closure & closure)
|
||||
{
|
||||
work_queue.push(work(closure));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
#endif
|
||||
void submit(void (*closure)())
|
||||
{
|
||||
work_queue.push(work(closure));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
|
||||
template <typename 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 Effects: reschedule functions until pred()
|
||||
* \b Returns: always false as a serial executor can not re-enter.
|
||||
* Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks.
|
||||
*/
|
||||
template <typename Pred>
|
||||
bool reschedule_until(Pred const& pred)
|
||||
{
|
||||
do {
|
||||
if ( ! try_executing_one())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
} while (! pred());
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
shared_ptr<shared_state> pimpl;
|
||||
};
|
||||
}
|
||||
using executors::serial_executor;
|
||||
|
||||
258
include/boost/thread/executors/serial_executor_cont.hpp
Normal file
258
include/boost/thread/executors/serial_executor_cont.hpp
Normal file
@@ -0,0 +1,258 @@
|
||||
// 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_SERIAL_EXECUTOR_CONT_HPP
|
||||
#define BOOST_THREAD_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/future.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>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
template <class Executor>
|
||||
class serial_executor_cont
|
||||
{
|
||||
public:
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
private:
|
||||
|
||||
struct shared_state {
|
||||
typedef executors::work work;
|
||||
|
||||
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.
|
||||
*/
|
||||
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))));
|
||||
}
|
||||
};
|
||||
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.
|
||||
*/
|
||||
serial_executor_cont(Executor& ex)
|
||||
: 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.
|
||||
*/
|
||||
~serial_executor_cont()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* \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;
|
||||
}
|
||||
|
||||
/**
|
||||
* \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::serial_executor_cont;
|
||||
}
|
||||
|
||||
#include <boost/config/abi_suffix.hpp>
|
||||
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -18,6 +18,9 @@
|
||||
#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>
|
||||
|
||||
#include <boost/config/abi_prefix.hpp>
|
||||
|
||||
namespace boost
|
||||
@@ -29,33 +32,134 @@ namespace executors
|
||||
public:
|
||||
/// type-erasure to store the works to do
|
||||
typedef executors::work work;
|
||||
bool closed_;
|
||||
typedef scoped_thread<> thread_t;
|
||||
typedef csbl::vector<thread_t> threads_type;
|
||||
threads_type threads_;
|
||||
mutable mutex mtx_;
|
||||
private:
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
struct shared_state {
|
||||
typedef executors::work work;
|
||||
bool closed_;
|
||||
typedef scoped_thread<> thread_t;
|
||||
typedef csbl::vector<thread_t> threads_type;
|
||||
threads_type threads_;
|
||||
mutable mutex mtx_;
|
||||
|
||||
/// thread_executor is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(shared_state)
|
||||
|
||||
/**
|
||||
* \b Effects: creates a inline executor that runs closures immediately.
|
||||
*
|
||||
* \b Throws: Nothing.
|
||||
*/
|
||||
shared_state()
|
||||
: closed_(false)
|
||||
{
|
||||
}
|
||||
/**
|
||||
* \b Effects: Waits for closures (if any) to complete, then joins and destroys the threads.
|
||||
*
|
||||
* \b Synchronization: The completion of all the closures happen before the completion of the \c thread_executor destructor.
|
||||
*/
|
||||
~shared_state()
|
||||
{
|
||||
// signal to all the worker thread that there will be no more submissions.
|
||||
close();
|
||||
// all the scoped threads will join before destroying
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* \b Effects: close the \c thread_executor for submissions.
|
||||
* The loop will work until there is no more closures to run.
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
lock_guard<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:
|
||||
/// thread_executor is not copyable.
|
||||
BOOST_THREAD_NO_COPYABLE(thread_executor)
|
||||
|
||||
/**
|
||||
* \b Effects: creates a inline executor that runs closures immediately.
|
||||
*
|
||||
* \b Throws: Nothing.
|
||||
*/
|
||||
thread_executor()
|
||||
: closed_(false)
|
||||
: pimpl(make_shared<shared_state>())
|
||||
{
|
||||
}
|
||||
/**
|
||||
@@ -65,9 +169,16 @@ namespace executors
|
||||
*/
|
||||
~thread_executor()
|
||||
{
|
||||
// signal to all the worker thread that there will be no more submissions.
|
||||
close();
|
||||
// all the scoped threads will join before destroying
|
||||
}
|
||||
|
||||
/**
|
||||
* Effects: try to execute one task.
|
||||
* Returns: whether a task has been executed.
|
||||
* Throws: whatever the current task constructor throws or the task() throws.
|
||||
*/
|
||||
bool try_executing_one()
|
||||
{
|
||||
return pimpl->try_executing_one();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,21 +187,15 @@ namespace executors
|
||||
*/
|
||||
void close()
|
||||
{
|
||||
lock_guard<mutex> lk(mtx_);
|
||||
closed_ = true;
|
||||
pimpl->close();
|
||||
}
|
||||
|
||||
/**
|
||||
* \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);
|
||||
return pimpl->closed();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -109,30 +214,18 @@ namespace executors
|
||||
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)));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
#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)));
|
||||
pimpl->submit(closure);
|
||||
}
|
||||
|
||||
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)));
|
||||
pimpl->submit(boost::forward<Closure>(closure));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,12 +234,14 @@ namespace executors
|
||||
* \b Effects: reschedule functions until 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;
|
||||
}
|
||||
|
||||
@@ -8,36 +8,23 @@
|
||||
#define BOOST_THREAD_EXECUTORS_WORK_HPP
|
||||
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#if ! defined BOOST_THREAD_EXECUTORS_WORK_ACCEPTS_MOVABLE \
|
||||
&& ! defined BOOST_THREAD_EXECUTORS_WORK_DONT_ACCEPT_MOVABLE
|
||||
#define BOOST_THREAD_EXECUTORS_WORK_ACCEPTS_MOVABLE
|
||||
//#define BOOST_THREAD_EXECUTORS_WORK_DONT_ACCEPT_MOVABLE
|
||||
#endif
|
||||
|
||||
#if defined BOOST_THREAD_EXECUTORS_WORK_ACCEPTS_MOVABLE
|
||||
|
||||
#include <boost/thread/detail/nullary_function.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
typedef detail::nullary_function<void()> work;
|
||||
}
|
||||
} // namespace boost
|
||||
|
||||
#else
|
||||
#include <boost/thread/csbl/functional.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace executors
|
||||
{
|
||||
typedef csbl::function<void()> work;
|
||||
typedef detail::nullary_function<void()> work;
|
||||
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
typedef detail::nullary_function<void()> work_pq;
|
||||
//typedef csbl::function<void()> work_pq;
|
||||
#else
|
||||
typedef csbl::function<void()> work_pq;
|
||||
#endif
|
||||
}
|
||||
} // namespace boost
|
||||
|
||||
#endif
|
||||
|
||||
#endif // BOOST_THREAD_EXECUTORS_WORK_HPP
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// (C) Copyright Vicente J. Botet Escriba 2014. Distributed under the Boost
|
||||
// (C) Copyright Vicente J. Botet Escriba 2014-2015. 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)
|
||||
//
|
||||
@@ -50,12 +50,13 @@ BOOST_THREAD_INLINE_NAMESPACE(v2)
|
||||
void handle_task_region_exceptions(exception_list& errors)
|
||||
{
|
||||
try {
|
||||
boost::rethrow_exception(boost::current_exception());
|
||||
//throw boost::current_exception();
|
||||
throw;
|
||||
}
|
||||
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED
|
||||
catch (task_canceled_exception& ex)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
catch (exception_list const& el)
|
||||
{
|
||||
for (exception_list::const_iterator it = el.begin(); it != el.end(); ++it)
|
||||
@@ -94,7 +95,7 @@ BOOST_THREAD_INLINE_NAMESPACE(v2)
|
||||
{
|
||||
lock_guard<mutex> lk(tr.mtx);
|
||||
tr.canceled = true;
|
||||
handle_task_region_exceptions(tr.exs);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -115,16 +116,14 @@ BOOST_THREAD_INLINE_NAMESPACE(v2)
|
||||
template<typename F>
|
||||
friend void task_region_final(BOOST_THREAD_FWD_REF(F) 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>
|
||||
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()
|
||||
{
|
||||
wait_for_all(group.begin(), group.end());
|
||||
|
||||
#if ! defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED
|
||||
|
||||
for (group_type::iterator it = group.begin(); it != group.end(); ++it)
|
||||
{
|
||||
future<void>& f = *it;
|
||||
@@ -140,11 +139,9 @@ BOOST_THREAD_INLINE_NAMESPACE(v2)
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (exs.size() != 0)
|
||||
{
|
||||
boost::throw_exception(exs);
|
||||
//throw exs;
|
||||
}
|
||||
}
|
||||
protected:
|
||||
@@ -156,21 +153,20 @@ protected:
|
||||
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS
|
||||
task_region_handle_gen()
|
||||
: canceled(false)
|
||||
, ex(0)
|
||||
{}
|
||||
task_region_handle_gen(Executor& ex)
|
||||
task_region_handle_gen(Executor const& ex)
|
||||
: canceled(false)
|
||||
, ex(&ex)
|
||||
, ex(ex)
|
||||
{}
|
||||
|
||||
#endif
|
||||
|
||||
#if ! defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED && defined BOOST_THREAD_PROVIDES_EXECUTORS
|
||||
task_region_handle_gen()
|
||||
: ex(0)
|
||||
//: ex(0)
|
||||
{}
|
||||
task_region_handle_gen(Executor& ex)
|
||||
: ex(&ex)
|
||||
task_region_handle_gen(Executor const& ex)
|
||||
: ex(ex)
|
||||
{}
|
||||
#endif
|
||||
|
||||
@@ -187,16 +183,15 @@ protected:
|
||||
}
|
||||
|
||||
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED
|
||||
mutable mutex mtx;
|
||||
bool canceled;
|
||||
#endif
|
||||
#if defined BOOST_THREAD_PROVIDES_EXECUTORS
|
||||
Executor* ex;
|
||||
Executor ex;
|
||||
#endif
|
||||
exception_list exs;
|
||||
typedef csbl::vector<future<void> > group_type;
|
||||
group_type group;
|
||||
mutable mutex mtx;
|
||||
|
||||
|
||||
public:
|
||||
BOOST_DELETED_FUNCTION(task_region_handle_gen(const task_region_handle_gen&))
|
||||
@@ -207,20 +202,21 @@ protected:
|
||||
template<typename F>
|
||||
void run(BOOST_THREAD_FWD_REF(F) f)
|
||||
{
|
||||
lock_guard<mutex> lk(mtx);
|
||||
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED
|
||||
if (canceled) {
|
||||
boost::throw_exception(task_canceled_exception());
|
||||
//throw task_canceled_exception();
|
||||
{
|
||||
lock_guard<mutex> lk(mtx);
|
||||
if (canceled) {
|
||||
boost::throw_exception(task_canceled_exception());
|
||||
}
|
||||
}
|
||||
#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
|
||||
group.push_back(async(detail::wrapped<task_region_handle_gen<Executor>, F>(*this, forward<F>(f))));
|
||||
#endif
|
||||
#else
|
||||
#if defined BOOST_THREAD_PROVIDES_EXECUTORS
|
||||
group.push_back(async(*ex, forward<F>(f)));
|
||||
group.push_back(async(ex, forward<F>(f)));
|
||||
#else
|
||||
group.push_back(async(forward<F>(f)));
|
||||
#endif
|
||||
@@ -229,11 +225,12 @@ protected:
|
||||
|
||||
void wait()
|
||||
{
|
||||
lock_guard<mutex> lk(mtx);
|
||||
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED
|
||||
if (canceled) {
|
||||
boost::throw_exception(task_canceled_exception());
|
||||
//throw task_canceled_exception{};
|
||||
{
|
||||
lock_guard<mutex> lk(mtx);
|
||||
if (canceled) {
|
||||
boost::throw_exception(task_canceled_exception());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
wait_all();
|
||||
@@ -247,17 +244,18 @@ protected:
|
||||
class task_region_handle :
|
||||
public task_region_handle_gen<default_executor>
|
||||
{
|
||||
default_executor tp;
|
||||
//default_executor tp;
|
||||
template <typename F>
|
||||
friend void task_region(BOOST_THREAD_FWD_REF(F) f);
|
||||
template<typename F>
|
||||
friend void task_region_final(BOOST_THREAD_FWD_REF(F) f);
|
||||
|
||||
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
|
||||
ex = &tp;
|
||||
//ex = &tp;
|
||||
#endif
|
||||
}
|
||||
BOOST_DELETED_FUNCTION(task_region_handle(const task_region_handle&))
|
||||
@@ -267,7 +265,7 @@ protected:
|
||||
};
|
||||
|
||||
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);
|
||||
try
|
||||
@@ -276,14 +274,13 @@ protected:
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
lock_guard<mutex> lk(tr.mtx);
|
||||
detail::handle_task_region_exceptions(tr.exs);
|
||||
}
|
||||
tr.wait_all();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
@@ -298,7 +295,6 @@ protected:
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
lock_guard<mutex> lk(tr.mtx);
|
||||
detail::handle_task_region_exceptions(tr.exs);
|
||||
}
|
||||
tr.wait_all();
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -81,7 +81,7 @@ namespace boost
|
||||
};
|
||||
|
||||
boost::condition_variable_any cv;
|
||||
std::vector<registered_waiter> waiters;
|
||||
std::vector<registered_waiter> waiters_;
|
||||
count_type future_count;
|
||||
|
||||
public:
|
||||
@@ -98,7 +98,7 @@ namespace boost
|
||||
registered_waiter waiter(f, f.notify_when_ready(cv), future_count);
|
||||
try
|
||||
{
|
||||
waiters.push_back(waiter);
|
||||
waiters_.push_back(waiter);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
@@ -120,14 +120,14 @@ namespace boost
|
||||
|
||||
count_type wait()
|
||||
{
|
||||
all_futures_lock lk(waiters);
|
||||
all_futures_lock lk(waiters_);
|
||||
for (;;)
|
||||
{
|
||||
for (count_type i = 0; i < waiters.size(); ++i)
|
||||
for (count_type i = 0; i < waiters_.size(); ++i)
|
||||
{
|
||||
if (waiters[i].future_->is_ready(lk.locks[i]))
|
||||
if (waiters_[i].future_->is_ready(lk.locks[i]))
|
||||
{
|
||||
return waiters[i].index;
|
||||
return waiters_[i].index;
|
||||
}
|
||||
}
|
||||
cv.wait(lk);
|
||||
@@ -136,9 +136,9 @@ namespace boost
|
||||
|
||||
~waiter_for_any_in_seq()
|
||||
{
|
||||
for (count_type i = 0; i < waiters.size(); ++i)
|
||||
for (count_type i = 0; i < waiters_.size(); ++i)
|
||||
{
|
||||
waiters[i].future_->unnotify_when_ready(waiters[i].handle);
|
||||
waiters_[i].future_->unnotify_when_ready(waiters_[i].handle);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -120,6 +120,15 @@ namespace boost
|
||||
unique_lock<mutex>& m,
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -149,6 +158,18 @@ namespace boost
|
||||
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 timed_wait(m,get_system_time()+wait_duration,pred);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#ifndef BOOST_THREAD_PTHREAD_MUTEX_HPP
|
||||
#define BOOST_THREAD_PTHREAD_MUTEX_HPP
|
||||
// (C) Copyright 2007-8 Anthony Williams
|
||||
// (C) Copyright 2011-2012 Vicente J. Botet Escriba
|
||||
// (C) Copyright 2011,2012,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/thread/detail/config.hpp>
|
||||
#include <boost/assert.hpp>
|
||||
#include <pthread.h>
|
||||
#include <boost/throw_exception.hpp>
|
||||
#include <boost/core/ignore_unused.hpp>
|
||||
@@ -26,13 +27,12 @@
|
||||
#endif
|
||||
#include <boost/thread/detail/delete.hpp>
|
||||
|
||||
#ifdef _POSIX_TIMEOUTS
|
||||
#if _POSIX_TIMEOUTS >= 0 && _POSIX_TIMEOUTS>=200112L
|
||||
#if (defined(_POSIX_TIMEOUTS) && (_POSIX_TIMEOUTS-0)>=200112L) \
|
||||
|| (defined(__ANDROID__) && defined(__ANDROID_API__) && __ANDROID_API__ >= 21)
|
||||
#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK
|
||||
#define BOOST_PTHREAD_HAS_TIMEDLOCK
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
#include <boost/config/abi_prefix.hpp>
|
||||
@@ -123,10 +123,12 @@ namespace boost
|
||||
void unlock()
|
||||
{
|
||||
int res = posix::pthread_mutex_unlock(&m);
|
||||
if (res)
|
||||
{
|
||||
boost::throw_exception(lock_error(res,"boost: mutex unlock failed in pthread_mutex_unlock"));
|
||||
}
|
||||
(void)res;
|
||||
BOOST_ASSERT(res == 0);
|
||||
// if (res)
|
||||
// {
|
||||
// boost::throw_exception(lock_error(res,"boost: mutex unlock failed in pthread_mutex_unlock"));
|
||||
// }
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
@@ -219,10 +221,12 @@ namespace boost
|
||||
void unlock()
|
||||
{
|
||||
int res = posix::pthread_mutex_unlock(&m);
|
||||
if (res)
|
||||
{
|
||||
boost::throw_exception(lock_error(res,"boost: mutex unlock failed in pthread_mutex_unlock"));
|
||||
}
|
||||
(void)res;
|
||||
BOOST_ASSERT(res == 0);
|
||||
// if (res)
|
||||
// {
|
||||
// boost::throw_exception(lock_error(res,"boost: mutex unlock failed in pthread_mutex_unlock"));
|
||||
// }
|
||||
}
|
||||
|
||||
bool try_lock()
|
||||
|
||||
@@ -27,16 +27,19 @@
|
||||
#endif
|
||||
#include <boost/thread/detail/delete.hpp>
|
||||
|
||||
#ifdef _POSIX_TIMEOUTS
|
||||
#if _POSIX_TIMEOUTS >= 0 && _POSIX_TIMEOUTS>=200112L
|
||||
#if (defined _POSIX_TIMEOUTS && (_POSIX_TIMEOUTS-0)>=200112L) \
|
||||
|| (defined __ANDROID__ && defined __ANDROID_API__ && __ANDROID_API__ >= 21)
|
||||
#ifndef BOOST_PTHREAD_HAS_TIMEDLOCK
|
||||
#define BOOST_PTHREAD_HAS_TIMEDLOCK
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE \
|
||||
|| defined __ANDROID__
|
||||
#define BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) && defined(BOOST_PTHREAD_HAS_TIMEDLOCK)
|
||||
#if defined BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE && defined BOOST_PTHREAD_HAS_TIMEDLOCK
|
||||
#define BOOST_USE_PTHREAD_RECURSIVE_TIMEDLOCK
|
||||
#endif
|
||||
|
||||
@@ -48,7 +51,7 @@ namespace boost
|
||||
{
|
||||
private:
|
||||
pthread_mutex_t m;
|
||||
#ifndef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
#ifndef BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
pthread_cond_t cond;
|
||||
bool is_locked;
|
||||
pthread_t owner;
|
||||
@@ -58,7 +61,7 @@ namespace boost
|
||||
BOOST_THREAD_NO_COPYABLE(recursive_mutex)
|
||||
recursive_mutex()
|
||||
{
|
||||
#ifdef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
#ifdef BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
pthread_mutexattr_t attr;
|
||||
|
||||
int const init_attr_res=pthread_mutexattr_init(&attr);
|
||||
@@ -99,12 +102,12 @@ namespace boost
|
||||
~recursive_mutex()
|
||||
{
|
||||
BOOST_VERIFY(!pthread_mutex_destroy(&m));
|
||||
#ifndef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
#ifndef BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
BOOST_VERIFY(!pthread_cond_destroy(&cond));
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
#ifdef BOOST_THREAD_HAS_PTHREAD_MUTEXATTR_SETTYPE
|
||||
void lock()
|
||||
{
|
||||
BOOST_VERIFY(!pthread_mutex_lock(&m));
|
||||
|
||||
@@ -115,8 +115,13 @@ namespace boost
|
||||
boost::detail::thread_exit_callback_node* thread_exit_callbacks;
|
||||
std::map<void const*,boost::detail::tss_data_node> tss_data;
|
||||
|
||||
//#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
|
||||
// These data must be at the end so that the access to the other fields doesn't change
|
||||
// when BOOST_THREAD_PROVIDES_INTERRUPTIONS is defined.
|
||||
// Another option is to have them always
|
||||
pthread_mutex_t* cond_mutex;
|
||||
pthread_cond_t* current_cond;
|
||||
//#endif
|
||||
typedef std::vector<std::pair<condition_variable*, mutex*>
|
||||
//, hidden_allocator<std::pair<condition_variable*, mutex*> >
|
||||
> notify_list_t;
|
||||
@@ -136,8 +141,10 @@ namespace boost
|
||||
thread_handle(0),
|
||||
done(false),join_started(false),joined(false),
|
||||
thread_exit_callbacks(0),
|
||||
//#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
|
||||
cond_mutex(0),
|
||||
current_cond(0),
|
||||
//#endif
|
||||
notify(),
|
||||
async_states_()
|
||||
//#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
|
||||
@@ -252,7 +259,7 @@ namespace boost
|
||||
inline
|
||||
void BOOST_SYMBOL_VISIBLE sleep_for(const chrono::nanoseconds& ns)
|
||||
{
|
||||
return boost::this_thread::hiden::sleep_for(boost::detail::to_timespec(ns));
|
||||
return boost::this_thread::no_interruption_point::hiden::sleep_for(boost::detail::to_timespec(ns));
|
||||
}
|
||||
#endif
|
||||
#endif // BOOST_THREAD_USES_CHRONO
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
#include <boost/thread/detail/delete.hpp>
|
||||
#include <boost/thread/detail/move.hpp>
|
||||
#include <boost/thread/concurrent_queues/sync_queue.hpp>
|
||||
#include <boost/thread/detail/work.hpp>
|
||||
#include <boost/thread/executors/work.hpp>
|
||||
|
||||
#include <boost/config/abi_prefix.hpp>
|
||||
|
||||
|
||||
@@ -339,8 +339,8 @@ namespace boost
|
||||
{
|
||||
if (wait_duration.is_pos_infinity())
|
||||
{
|
||||
wait(m); // or do_wait(m,detail::timeout::sentinel());
|
||||
return true;
|
||||
wait(m); // or do_wait(m,detail::timeout::sentinel());
|
||||
return true;
|
||||
}
|
||||
if (wait_duration.is_special())
|
||||
{
|
||||
@@ -362,6 +362,18 @@ namespace boost
|
||||
template<typename duration_type,typename predicate_type>
|
||||
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);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -35,6 +35,7 @@ namespace boost
|
||||
typedef HANDLE handle;
|
||||
typedef SYSTEM_INFO system_info;
|
||||
typedef unsigned __int64 ticks_type;
|
||||
typedef FARPROC farproc_t;
|
||||
unsigned const infinite=INFINITE;
|
||||
unsigned const timeout=WAIT_TIMEOUT;
|
||||
handle const invalid_handle_value=INVALID_HANDLE_VALUE;
|
||||
@@ -58,17 +59,20 @@ namespace boost
|
||||
using ::CreateSemaphoreExW;
|
||||
# endif
|
||||
using ::OpenEventW;
|
||||
using ::GetModuleGandleW;
|
||||
# else
|
||||
using ::CreateMutexA;
|
||||
using ::CreateEventA;
|
||||
using ::OpenEventA;
|
||||
using ::CreateSemaphoreA;
|
||||
using ::GetModuleHandleA;
|
||||
# endif
|
||||
#if BOOST_PLAT_WINDOWS_RUNTIME
|
||||
using ::GetNativeSystemInfo;
|
||||
using ::GetTickCount64;
|
||||
#else
|
||||
using ::GetSystemInfo;
|
||||
using ::GetTickCount;
|
||||
#endif
|
||||
using ::CloseHandle;
|
||||
using ::ReleaseMutex;
|
||||
@@ -86,6 +90,7 @@ namespace boost
|
||||
using ::SleepEx;
|
||||
using ::Sleep;
|
||||
using ::QueueUserAPC;
|
||||
using ::GetProcAddress;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@@ -135,6 +140,7 @@ namespace boost
|
||||
typedef void* handle;
|
||||
typedef _SYSTEM_INFO system_info;
|
||||
typedef unsigned __int64 ticks_type;
|
||||
typedef int (__stdcall *farproc_t)();
|
||||
unsigned const infinite=~0U;
|
||||
unsigned const timeout=258U;
|
||||
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);
|
||||
# endif
|
||||
__declspec(dllimport) void* __stdcall OpenEventW(unsigned long,int,wchar_t const*);
|
||||
__declspec(dllimport) void* __stdcall GetModuleHandleW(wchar_t const*);
|
||||
# else
|
||||
__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 CreateEventA(_SECURITY_ATTRIBUTES*,int,int,char const*);
|
||||
__declspec(dllimport) void* __stdcall OpenEventA(unsigned long,int,char const*);
|
||||
__declspec(dllimport) void* __stdcall GetModuleHandleA(char const*);
|
||||
# endif
|
||||
#if BOOST_PLAT_WINDOWS_RUNTIME
|
||||
__declspec(dllimport) void __stdcall GetNativeSystemInfo(_SYSTEM_INFO*);
|
||||
__declspec(dllimport) ticks_type __stdcall GetTickCount64();
|
||||
#else
|
||||
__declspec(dllimport) void __stdcall GetSystemInfo(_SYSTEM_INFO*);
|
||||
__declspec(dllimport) unsigned long __stdcall GetTickCount();
|
||||
#endif
|
||||
__declspec(dllimport) int __stdcall CloseHandle(void*);
|
||||
__declspec(dllimport) int __stdcall ReleaseMutex(void*);
|
||||
@@ -183,6 +192,7 @@ namespace boost
|
||||
__declspec(dllimport) void __stdcall Sleep(unsigned long);
|
||||
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) farproc_t __stdcall GetProcAddress(void *, const char *);
|
||||
#endif
|
||||
|
||||
# ifndef UNDER_CE
|
||||
@@ -216,17 +226,10 @@ namespace boost
|
||||
{
|
||||
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
|
||||
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
|
||||
long _InterlockedCompareExchange(long volatile *, long, long);
|
||||
#pragma intrinsic(_InterlockedCompareExchange)
|
||||
@@ -298,7 +301,7 @@ namespace boost
|
||||
#if BOOST_PLAT_WINDOWS_RUNTIME
|
||||
gettickcount64impl = &GetTickCount64;
|
||||
#else
|
||||
detail::farproc_t addr=GetProcAddress(
|
||||
farproc_t addr=GetProcAddress(
|
||||
#if !defined(BOOST_NO_ANSI_APIS)
|
||||
GetModuleHandleA("KERNEL32.DLL"),
|
||||
#else
|
||||
|
||||
@@ -15,7 +15,9 @@
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <memory>
|
||||
|
||||
#if defined BOOST_THREAD_PATCH
|
||||
#include <string.h> // memcmp.
|
||||
#endif
|
||||
namespace boost
|
||||
{
|
||||
namespace thread_detail
|
||||
|
||||
@@ -80,6 +80,8 @@ namespace boost
|
||||
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_ptr thread_info = static_cast<boost::detail::thread_data_base*>(data)->shared_from_this();
|
||||
|
||||
if(thread_info)
|
||||
{
|
||||
while(!thread_info->tss_data.empty() || thread_info->thread_exit_callbacks)
|
||||
@@ -96,13 +98,10 @@ namespace boost
|
||||
}
|
||||
delete current_node;
|
||||
}
|
||||
for(std::map<void const*,tss_data_node>::iterator next=thread_info->tss_data.begin(),
|
||||
current,
|
||||
end=thread_info->tss_data.end();
|
||||
next!=end;)
|
||||
while (!thread_info->tss_data.empty())
|
||||
{
|
||||
current=next;
|
||||
++next;
|
||||
std::map<void const*,detail::tss_data_node>::iterator current
|
||||
= thread_info->tss_data.begin();
|
||||
if(current->second.func && (current->second.value!=0))
|
||||
{
|
||||
(*current->second.func)(current->second.value);
|
||||
@@ -127,7 +126,8 @@ namespace boost
|
||||
}
|
||||
~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(¤t_thread_tls_init_flag, &uninitialized, sizeof(boost::once_flag)))
|
||||
{
|
||||
pthread_key_delete(current_thread_tls_key);
|
||||
}
|
||||
@@ -161,7 +161,8 @@ namespace boost
|
||||
{
|
||||
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;
|
||||
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());
|
||||
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
|
||||
@@ -255,7 +256,6 @@ namespace boost
|
||||
{
|
||||
thread_info->self.reset();
|
||||
return false;
|
||||
// boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_create"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -269,7 +269,6 @@ namespace boost
|
||||
{
|
||||
thread_info->self.reset();
|
||||
return false;
|
||||
// boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_create"));
|
||||
}
|
||||
int detached_state;
|
||||
res = pthread_attr_getdetachstate(h, &detached_state);
|
||||
@@ -277,7 +276,6 @@ namespace boost
|
||||
{
|
||||
thread_info->self.reset();
|
||||
return false;
|
||||
// boost::throw_exception(thread_resource_error(res, "boost thread: failed in pthread_attr_getdetachstate"));
|
||||
}
|
||||
if (PTHREAD_CREATE_DETACHED==detached_state)
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
#include <wrl\ftm.h>
|
||||
#include <windows.system.threading.h>
|
||||
#pragma comment(lib, "runtimeobject.lib")
|
||||
#endif
|
||||
#endif
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@@ -198,7 +198,7 @@ namespace boost
|
||||
namespace detail
|
||||
{
|
||||
std::atomic_uint threadCount;
|
||||
|
||||
|
||||
bool win32::scoped_winrt_thread::start(thread_func address, void *parameter, unsigned int *thrdId)
|
||||
{
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> threadPoolFactory;
|
||||
@@ -220,7 +220,7 @@ namespace boost
|
||||
m_completionHandle = completionHandle;
|
||||
|
||||
// Create new work item.
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::System::Threading::IWorkItemHandler> workItem =
|
||||
Microsoft::WRL::ComPtr<ABI::Windows::System::Threading::IWorkItemHandler> workItem =
|
||||
Microsoft::WRL::Callback<Microsoft::WRL::Implements<Microsoft::WRL::RuntimeClassFlags<Microsoft::WRL::ClassicCom>, ABI::Windows::System::Threading::IWorkItemHandler, Microsoft::WRL::FtmBase>>
|
||||
([address, parameter, completionHandle](ABI::Windows::Foundation::IAsyncAction *)
|
||||
{
|
||||
@@ -274,13 +274,10 @@ namespace boost
|
||||
}
|
||||
boost::detail::heap_delete(current_node);
|
||||
}
|
||||
for(std::map<void const*,detail::tss_data_node>::iterator next=current_thread_data->tss_data.begin(),
|
||||
current,
|
||||
end=current_thread_data->tss_data.end();
|
||||
next!=end;)
|
||||
while (!current_thread_data->tss_data.empty())
|
||||
{
|
||||
current=next;
|
||||
++next;
|
||||
std::map<void const*,detail::tss_data_node>::iterator current
|
||||
= current_thread_data->tss_data.begin();
|
||||
if(current->second.func && (current->second.value!=0))
|
||||
{
|
||||
(*current->second.func)(current->second.value);
|
||||
@@ -346,7 +343,7 @@ namespace boost
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
bool thread::start_thread_noexcept(const attributes& attr)
|
||||
{
|
||||
#if BOOST_PLAT_WINDOWS_RUNTIME
|
||||
@@ -367,7 +364,7 @@ namespace boost
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
thread::thread(detail::thread_data_ptr data):
|
||||
thread_info(data)
|
||||
{}
|
||||
@@ -529,11 +526,8 @@ namespace boost
|
||||
|
||||
unsigned thread::physical_concurrency() BOOST_NOEXCEPT
|
||||
{
|
||||
// a bit too strict: Windows XP with SP3 would be sufficient
|
||||
#if BOOST_PLAT_WINDOWS_RUNTIME \
|
||||
|| ( BOOST_USE_WINAPI_VERSION <= BOOST_WINAPI_VERSION_WINXP ) \
|
||||
|| ( defined(__MINGW32__) && !defined(__MINGW64__) )
|
||||
return 0;
|
||||
#if BOOST_PLAT_WINDOWS_RUNTIME || (defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR))
|
||||
return hardware_concurrency();
|
||||
#else
|
||||
unsigned cores = 0;
|
||||
DWORD size = 0;
|
||||
@@ -634,7 +628,57 @@ namespace boost
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#ifndef UNDER_CE
|
||||
#if !BOOST_PLAT_WINDOWS_RUNTIME
|
||||
namespace detail_
|
||||
{
|
||||
typedef struct _REASON_CONTEXT {
|
||||
ULONG Version;
|
||||
DWORD Flags;
|
||||
union {
|
||||
LPWSTR SimpleReasonString;
|
||||
struct {
|
||||
HMODULE LocalizedReasonModule;
|
||||
ULONG LocalizedReasonId;
|
||||
ULONG ReasonStringCount;
|
||||
LPWSTR *ReasonStrings;
|
||||
} Detailed;
|
||||
} Reason;
|
||||
} REASON_CONTEXT, *PREASON_CONTEXT;
|
||||
static REASON_CONTEXT default_reason_context={0/*POWER_REQUEST_CONTEXT_VERSION*/, 0x00000001/*POWER_REQUEST_CONTEXT_SIMPLE_STRING*/, (LPWSTR)L"generic"};
|
||||
typedef BOOL (WINAPI *setwaitabletimerex_t)(HANDLE, const LARGE_INTEGER *, LONG, PTIMERAPCROUTINE, LPVOID, PREASON_CONTEXT, ULONG);
|
||||
static inline BOOL WINAPI SetWaitableTimerEx_emulation(HANDLE hTimer, const LARGE_INTEGER *lpDueTime, LONG lPeriod, PTIMERAPCROUTINE pfnCompletionRoutine, LPVOID lpArgToCompletionRoutine, PREASON_CONTEXT WakeContext, ULONG TolerableDelay)
|
||||
{
|
||||
return SetWaitableTimer(hTimer, lpDueTime, lPeriod, pfnCompletionRoutine, lpArgToCompletionRoutine, FALSE);
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 6387) // MSVC sanitiser warns that GetModuleHandleA() might fail
|
||||
#endif
|
||||
static inline setwaitabletimerex_t SetWaitableTimerEx()
|
||||
{
|
||||
static setwaitabletimerex_t setwaitabletimerex_impl;
|
||||
if(setwaitabletimerex_impl)
|
||||
return setwaitabletimerex_impl;
|
||||
void (*addr)()=(void (*)()) GetProcAddress(
|
||||
#if !defined(BOOST_NO_ANSI_APIS)
|
||||
GetModuleHandleA("KERNEL32.DLL"),
|
||||
#else
|
||||
GetModuleHandleW(L"KERNEL32.DLL"),
|
||||
#endif
|
||||
"SetWaitableTimerEx");
|
||||
if(addr)
|
||||
setwaitabletimerex_impl=(setwaitabletimerex_t) addr;
|
||||
else
|
||||
setwaitabletimerex_impl=&SetWaitableTimerEx_emulation;
|
||||
return setwaitabletimerex_impl;
|
||||
}
|
||||
#ifdef _MSC_VER
|
||||
#pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
bool interruptible_wait(detail::win32::handle handle_to_wait_for,detail::timeout target_time)
|
||||
{
|
||||
detail::win32::handle handles[4]={0};
|
||||
@@ -660,32 +704,24 @@ namespace boost
|
||||
|
||||
#ifndef UNDER_CE
|
||||
#if !BOOST_PLAT_WINDOWS_RUNTIME
|
||||
unsigned const min_timer_wait_period=20;
|
||||
|
||||
// Preferentially use coalescing timers for better power consumption and timer accuracy
|
||||
if(!target_time.is_sentinel())
|
||||
{
|
||||
detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds();
|
||||
if(time_left.milliseconds > min_timer_wait_period)
|
||||
timer_handle=CreateWaitableTimer(NULL,false,NULL);
|
||||
if(timer_handle!=0)
|
||||
{
|
||||
// for a long-enough timeout, use a waitable timer (which tracks clock changes)
|
||||
timer_handle=CreateWaitableTimer(NULL,false,NULL);
|
||||
if(timer_handle!=0)
|
||||
ULONG tolerable=32; // Empirical testing shows Windows ignores this when <= 26
|
||||
if(time_left.milliseconds/20>tolerable) // 5%
|
||||
tolerable=time_left.milliseconds/20;
|
||||
LARGE_INTEGER due_time=get_due_time(target_time);
|
||||
bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,&detail_::default_reason_context,tolerable)!=0;
|
||||
if(set_time_succeeded)
|
||||
{
|
||||
LARGE_INTEGER due_time=get_due_time(target_time);
|
||||
|
||||
bool const set_time_succeeded=SetWaitableTimer(timer_handle,&due_time,0,0,0,false)!=0;
|
||||
if(set_time_succeeded)
|
||||
{
|
||||
timeout_index=handle_count;
|
||||
handles[handle_count++]=timer_handle;
|
||||
}
|
||||
timeout_index=handle_count;
|
||||
handles[handle_count++]=timer_handle;
|
||||
}
|
||||
}
|
||||
else if(!target_time.relative)
|
||||
{
|
||||
// convert short absolute-time timeouts into relative ones, so we don't race against clock changes
|
||||
target_time=detail::timeout(time_left.milliseconds);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -752,32 +788,24 @@ namespace boost
|
||||
|
||||
#ifndef UNDER_CE
|
||||
#if !BOOST_PLAT_WINDOWS_RUNTIME
|
||||
unsigned const min_timer_wait_period=20;
|
||||
|
||||
// Preferentially use coalescing timers for better power consumption and timer accuracy
|
||||
if(!target_time.is_sentinel())
|
||||
{
|
||||
detail::timeout::remaining_time const time_left=target_time.remaining_milliseconds();
|
||||
if(time_left.milliseconds > min_timer_wait_period)
|
||||
timer_handle=CreateWaitableTimer(NULL,false,NULL);
|
||||
if(timer_handle!=0)
|
||||
{
|
||||
// for a long-enough timeout, use a waitable timer (which tracks clock changes)
|
||||
timer_handle=CreateWaitableTimer(NULL,false,NULL);
|
||||
if(timer_handle!=0)
|
||||
ULONG tolerable=32; // Empirical testing shows Windows ignores this when <= 26
|
||||
if(time_left.milliseconds/20>tolerable) // 5%
|
||||
tolerable=time_left.milliseconds/20;
|
||||
LARGE_INTEGER due_time=get_due_time(target_time);
|
||||
bool const set_time_succeeded=detail_::SetWaitableTimerEx()(timer_handle,&due_time,0,0,0,&detail_::default_reason_context,tolerable)!=0;
|
||||
if(set_time_succeeded)
|
||||
{
|
||||
LARGE_INTEGER due_time=get_due_time(target_time);
|
||||
|
||||
bool const set_time_succeeded=SetWaitableTimer(timer_handle,&due_time,0,0,0,false)!=0;
|
||||
if(set_time_succeeded)
|
||||
{
|
||||
timeout_index=handle_count;
|
||||
handles[handle_count++]=timer_handle;
|
||||
}
|
||||
timeout_index=handle_count;
|
||||
handles[handle_count++]=timer_handle;
|
||||
}
|
||||
}
|
||||
else if(!target_time.relative)
|
||||
{
|
||||
// convert short absolute-time timeouts into relative ones, so we don't race against clock changes
|
||||
target_time=detail::timeout(time_left.milliseconds);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -293,10 +293,12 @@ rule thread-compile ( sources : reqs * : name )
|
||||
[ thread-run test_7328.cpp ]
|
||||
[ thread-run test_7571.cpp ]
|
||||
[ thread-run test_9319.cpp ]
|
||||
#[ thread-run test_9711.cpp ] this test is invalid and should not work :(
|
||||
#[ thread-run test_9711.cpp ] This is an invalid use of ::then deferred.
|
||||
[ thread-run test_9856.cpp ]
|
||||
[ thread-compile test_10963.cpp : : test_10963_c ]
|
||||
[ thread-run test_10964.cpp ]
|
||||
[ thread-test test_11053.cpp ]
|
||||
[ thread-run test_11266.cpp ]
|
||||
;
|
||||
|
||||
|
||||
@@ -808,6 +810,11 @@ rule thread-compile ( sources : reqs * : name )
|
||||
[ thread-run2 ../example/user_scheduler.cpp : ex_user_scheduler ]
|
||||
[ thread-run2 ../example/executor.cpp : ex_executor ]
|
||||
[ thread-run2 ../example/generic_executor_ref.cpp : ex_generic_executor_ref ]
|
||||
[ thread-run2 ../example/generic_executor.cpp : ex_generic_executor ]
|
||||
[ thread-run2 ../example/generic_serial_executor.cpp : ex_generic_serial_executor ]
|
||||
[ thread-run2 ../example/serial_executor.cpp : ex_serial_executor ]
|
||||
[ thread-run2 ../example/generic_serial_executor_cont.cpp : ex_generic_serial_executor_cont ]
|
||||
[ thread-run2 ../example/serial_executor_cont.cpp : ex_serial_executor_cont ]
|
||||
[ thread-run2 ../example/future_when_all.cpp : ex_future_when_all ]
|
||||
[ thread-run2 ../example/parallel_accumulate.cpp : ex_parallel_accumulate ]
|
||||
[ thread-run2 ../example/parallel_quick_sort.cpp : ex_parallel_quick_sort ]
|
||||
@@ -944,9 +951,18 @@ rule thread-compile ( sources : reqs * : name )
|
||||
[ thread-run2-noit ./experimental/parallel/v2/task_region_pass.cpp : task_region_p ]
|
||||
;
|
||||
|
||||
explicit ts_other ;
|
||||
test-suite ts_other
|
||||
:
|
||||
[ thread-run2 ../example/this_executor.cpp : ex_this_executor ]
|
||||
[ thread-run2 ../example/default_executor.cpp : ex_default_executor ]
|
||||
;
|
||||
|
||||
explicit ts_ ;
|
||||
test-suite ts_
|
||||
:
|
||||
#[ thread-run test_11256.cpp ]
|
||||
;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -132,7 +132,9 @@ void run_exception_1()
|
||||
{
|
||||
std::cout << "task3" << std::endl;
|
||||
});
|
||||
#if defined BOOST_THREAD_TASK_REGION_HAS_SHARED_CANCELED
|
||||
BOOST_TEST(false);
|
||||
#endif
|
||||
});
|
||||
BOOST_TEST(false);
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
int operator()()
|
||||
int operator()() const
|
||||
{
|
||||
boost::this_thread::sleep_for(ms(200));
|
||||
return 3;
|
||||
@@ -167,6 +167,48 @@ struct check_timer {
|
||||
|
||||
int main()
|
||||
{
|
||||
{
|
||||
try {
|
||||
boost::async(f0);
|
||||
} catch (std::exception& ex)
|
||||
{
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << ex.what() << std::endl;
|
||||
BOOST_TEST(false && "exception thrown");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_TEST(false && "exception thrown");
|
||||
}
|
||||
}
|
||||
{
|
||||
try {
|
||||
boost::async(boost::launch::async, f0);
|
||||
} catch (std::exception& ex)
|
||||
{
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << ex.what() << std::endl;
|
||||
BOOST_TEST(false && "exception thrown");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_TEST(false && "exception thrown");
|
||||
}
|
||||
}
|
||||
#if defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
{
|
||||
try {
|
||||
boost::async(boost::launch::deferred, f0);
|
||||
} catch (std::exception& ex)
|
||||
{
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << ex.what() << std::endl;
|
||||
BOOST_TEST(false && "exception thrown");
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_TEST(false && "exception thrown");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
{
|
||||
try
|
||||
|
||||
@@ -51,6 +51,29 @@ void p3(boost::future<int> f)
|
||||
|
||||
int main()
|
||||
{
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
BOOST_TEST(f1.valid());
|
||||
{
|
||||
boost::future<int> f2 = f1.then(&p2);
|
||||
BOOST_TEST(f2.valid());
|
||||
}
|
||||
BOOST_TEST(! f1.valid());
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
BOOST_THREAD_LOG << "ERRORRRRR "<<ex.what() << "" << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_THREAD_LOG << " ERRORRRRR exception thrown" << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(false);
|
||||
}
|
||||
}
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
{
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <boost/thread/future.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/core/ref.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
|
||||
#if defined BOOST_THREAD_USES_CHRONO
|
||||
@@ -128,49 +129,49 @@ int main()
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
}
|
||||
}
|
||||
// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
// {
|
||||
// typedef int& T;
|
||||
// {
|
||||
// boost::promise<T> p;
|
||||
// boost::future<T> f = p.get_future();
|
||||
//#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
|
||||
// boost::thread(func3, boost::move(p)).detach();
|
||||
//#else
|
||||
// int j=5;
|
||||
// p.set_value(j);
|
||||
//#endif
|
||||
// BOOST_TEST(f.valid());
|
||||
// int k=4;
|
||||
// BOOST_TEST(f.get_or(k) == 5);
|
||||
//#ifdef BOOST_THREAD_PROVIDES_FUTURE_INVALID_AFTER_GET
|
||||
// BOOST_TEST(!f.valid());
|
||||
//#endif
|
||||
// }
|
||||
// BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
// {
|
||||
// boost::promise<T> p;
|
||||
// boost::future<T> f = p.get_future();
|
||||
//#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
|
||||
// boost::thread(func4, boost::move(p)).detach();
|
||||
//#else
|
||||
// p.set_exception(boost::make_exception_ptr(3.5));
|
||||
//#endif
|
||||
// try
|
||||
// {
|
||||
// BOOST_TEST(f.valid());
|
||||
// int j=4;
|
||||
// BOOST_TEST(f.get_or(j) == 4);
|
||||
// }
|
||||
// catch (...)
|
||||
// {
|
||||
// BOOST_TEST(false);
|
||||
// }
|
||||
//#ifdef BOOST_THREAD_PROVIDES_FUTURE_INVALID_AFTER_GET
|
||||
// BOOST_TEST(!f.valid());
|
||||
//#endif
|
||||
// }
|
||||
// }
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
{
|
||||
typedef int& T;
|
||||
{
|
||||
boost::promise<T> p;
|
||||
boost::future<T> f = p.get_future();
|
||||
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
|
||||
boost::thread(func3, boost::move(p)).detach();
|
||||
#else
|
||||
int j=5;
|
||||
p.set_value(j);
|
||||
#endif
|
||||
BOOST_TEST(f.valid());
|
||||
int k=4;
|
||||
BOOST_TEST(f.get_or(boost::ref(k)) == 5);
|
||||
#ifdef BOOST_THREAD_PROVIDES_FUTURE_INVALID_AFTER_GET
|
||||
BOOST_TEST(!f.valid());
|
||||
#endif
|
||||
}
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
{
|
||||
boost::promise<T> p;
|
||||
boost::future<T> f = p.get_future();
|
||||
#if defined BOOST_THREAD_PROVIDES_SIGNATURE_PACKAGED_TASK && defined(BOOST_THREAD_PROVIDES_VARIADIC_THREAD)
|
||||
boost::thread(func4, boost::move(p)).detach();
|
||||
#else
|
||||
p.set_exception(boost::make_exception_ptr(3.5));
|
||||
#endif
|
||||
try
|
||||
{
|
||||
BOOST_TEST(f.valid());
|
||||
int j=4;
|
||||
BOOST_TEST(f.get_or(boost::ref(j)) == 4);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_TEST(false);
|
||||
}
|
||||
#ifdef BOOST_THREAD_PROVIDES_FUTURE_INVALID_AFTER_GET
|
||||
BOOST_TEST(!f.valid());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <boost/thread/future.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <cassert>
|
||||
|
||||
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
||||
|
||||
@@ -31,6 +32,8 @@ int p1()
|
||||
|
||||
int p2(boost::future<int> f)
|
||||
{
|
||||
assert(f.is_ready());
|
||||
|
||||
BOOST_THREAD_LOG << "p2 <" << &f << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(f.valid());
|
||||
int i = f.get();
|
||||
@@ -41,6 +44,7 @@ int p2(boost::future<int> f)
|
||||
|
||||
void p3(boost::future<int> f)
|
||||
{
|
||||
assert(f.is_ready());
|
||||
BOOST_THREAD_LOG << "p3 <" << &f << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(f.valid());
|
||||
int i = f.get();
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <boost/thread/executors/basic_thread_pool.hpp>
|
||||
#include <boost/thread/executor.hpp>
|
||||
#include <boost/detail/lightweight_test.hpp>
|
||||
#include <cassert>
|
||||
|
||||
#if defined BOOST_THREAD_PROVIDES_FUTURE_CONTINUATION
|
||||
|
||||
@@ -34,6 +35,7 @@ int p1()
|
||||
|
||||
int p2(boost::future<int> f)
|
||||
{
|
||||
assert(f.is_ready());
|
||||
BOOST_THREAD_LOG << "p2 <" << &f << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(f.valid());
|
||||
int i = f.get();
|
||||
@@ -44,6 +46,7 @@ int p2(boost::future<int> f)
|
||||
|
||||
void p3(boost::future<int> f)
|
||||
{
|
||||
assert(f.is_ready());
|
||||
BOOST_THREAD_LOG << "p3 <" << &f << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(f.valid());
|
||||
int i = f.get();
|
||||
|
||||
@@ -72,6 +72,30 @@ int main()
|
||||
}
|
||||
}
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
{
|
||||
boost::shared_future<int> f1 = boost::async(boost::launch::async, &p1).share();
|
||||
BOOST_TEST(f1.valid());
|
||||
boost::future<int> f2 = f1.then(&p2);
|
||||
boost::future<int> f3 = f1.then(&p2);
|
||||
BOOST_TEST(f2.valid());
|
||||
BOOST_TEST(f3.valid());
|
||||
try
|
||||
{
|
||||
BOOST_TEST(f2.get()==2);
|
||||
BOOST_TEST(f3.get()==2);
|
||||
}
|
||||
catch (std::exception& ex)
|
||||
{
|
||||
BOOST_THREAD_LOG << "ERRORRRRR "<<ex.what() << "" << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(false);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
BOOST_THREAD_LOG << " ERRORRRRR exception thrown" << BOOST_THREAD_END_LOG;
|
||||
BOOST_TEST(false);
|
||||
}
|
||||
}
|
||||
BOOST_THREAD_LOG << BOOST_THREAD_END_LOG;
|
||||
{
|
||||
boost::shared_future<int> f1 = boost::async(boost::launch::async, &p1).share();
|
||||
BOOST_TEST(f1.valid());
|
||||
|
||||
@@ -281,7 +281,7 @@ int main()
|
||||
BOOST_TEST(res[1].is_ready());
|
||||
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
|
||||
{ // deferred future copy-constructible
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -152,7 +152,7 @@ int main()
|
||||
BOOST_TEST(boost::csbl::get<0>(res).is_ready());
|
||||
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
|
||||
{ // deferred future copy-constructible
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -236,7 +236,7 @@ int main()
|
||||
BOOST_TEST(boost::csbl::get<1>(res).is_ready());
|
||||
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
|
||||
{ // deferred future copy-constructible
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -283,7 +283,7 @@ int main()
|
||||
BOOST_TEST(res[1].is_ready());
|
||||
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
|
||||
{ // deferred future copy-constructible
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -125,7 +125,7 @@ int main()
|
||||
BOOST_TEST(boost::csbl::get<0>(res).is_ready());
|
||||
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
|
||||
{ // deferred future copy-constructible
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -225,7 +225,7 @@ int main()
|
||||
BOOST_TEST(boost::csbl::get<0>(res).get() == 123);
|
||||
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
|
||||
{ // deferred future copy-constructible
|
||||
boost::future<int> f1 = boost::async(boost::launch::deferred, &p1);
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
class non_copyable
|
||||
{
|
||||
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
|
||||
int val;
|
||||
public:
|
||||
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
|
||||
non_copyable(int v) : val(v){}
|
||||
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; }
|
||||
|
||||
@@ -21,9 +21,9 @@
|
||||
|
||||
class non_copyable
|
||||
{
|
||||
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
|
||||
int val;
|
||||
public:
|
||||
BOOST_THREAD_MOVABLE_ONLY(non_copyable)
|
||||
non_copyable(int v) : val(v){}
|
||||
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; }
|
||||
|
||||
@@ -29,7 +29,7 @@ int main()
|
||||
boost::recursive_mutex::native_handle_type h = m.native_handle();
|
||||
BOOST_TEST(h);
|
||||
#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
|
||||
|
||||
return boost::report_errors();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -76,7 +76,7 @@ int main()
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
{
|
||||
// empty queue push rvalue/non_copyable succeeds
|
||||
boost::sync_deque<non_copyable> q;
|
||||
@@ -137,11 +137,11 @@ int main()
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
{
|
||||
// empty queue try_push rvalue/non-copyable succeeds
|
||||
boost::sync_deque<non_copyable> q;
|
||||
BOOST_TEST(boost::queue_op_status::success ==q.try_push_back(non_copyable()));
|
||||
BOOST_TEST(boost::queue_op_status::success ==q.try_push_back(non_copyable(1)));
|
||||
BOOST_TEST(! q.empty());
|
||||
BOOST_TEST(! q.full());
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
@@ -179,8 +179,7 @@ int main()
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
{
|
||||
// empty queue nonblocking_push_back rvalue/non-copyable succeeds
|
||||
boost::sync_deque<non_copyable> q;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// Copyright (C) 2014 Ian Forbed
|
||||
// Copyright (C) 2014 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2014,2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -149,17 +149,15 @@ int main()
|
||||
//test_pull_until_when_not_empty();
|
||||
|
||||
#if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
#if 0
|
||||
{
|
||||
// empty queue try_push rvalue/non-copyable succeeds
|
||||
boost::concurrent::sync_priority_queue<non_copyable> q;
|
||||
BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable()));
|
||||
BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable(1)));
|
||||
BOOST_TEST(! q.empty());
|
||||
BOOST_TEST(! q.full());
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
#endif
|
||||
{
|
||||
//fixme
|
||||
// empty queue try_push rvalue/non-copyable succeeds
|
||||
@@ -183,17 +181,16 @@ int main()
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
// {
|
||||
// // empty queue try_push rvalue succeeds
|
||||
// boost::concurrent::sync_priority_queue<int> q;
|
||||
// BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(1));
|
||||
// BOOST_TEST(! q.empty());
|
||||
// BOOST_TEST(! q.full());
|
||||
// BOOST_TEST_EQ(q.size(), 1u);
|
||||
// BOOST_TEST(! q.closed());
|
||||
// }
|
||||
#if ! defined BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
#if 0
|
||||
{
|
||||
// empty queue try_push rvalue succeeds
|
||||
boost::concurrent::sync_priority_queue<int> q;
|
||||
BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(1));
|
||||
BOOST_TEST(! q.empty());
|
||||
BOOST_TEST(! q.full());
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
{
|
||||
// empty queue nonblocking_push rvalue/non-copyable succeeds
|
||||
boost::concurrent::sync_priority_queue<non_copyable> q;
|
||||
@@ -203,17 +200,16 @@ int main()
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
#endif
|
||||
// {
|
||||
// // empty queue nonblocking_push rvalue/non-copyable succeeds
|
||||
// boost::concurrent::sync_priority_queue<non_copyable> q;
|
||||
// non_copyable nc(1);
|
||||
// BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(boost::move(nc)));
|
||||
// BOOST_TEST(! q.empty());
|
||||
// BOOST_TEST(! q.full());
|
||||
// BOOST_TEST_EQ(q.size(), 1u);
|
||||
// BOOST_TEST(! q.closed());
|
||||
// }
|
||||
{
|
||||
// empty queue nonblocking_push rvalue/non-copyable succeeds
|
||||
boost::concurrent::sync_priority_queue<non_copyable> q;
|
||||
non_copyable nc(1);
|
||||
BOOST_TEST(boost::queue_op_status::success == q.nonblocking_push(boost::move(nc)));
|
||||
BOOST_TEST(! q.empty());
|
||||
BOOST_TEST(! q.full());
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
#endif
|
||||
|
||||
{
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2013 Vicente J. Botet Escriba
|
||||
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See accompanying
|
||||
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
@@ -47,7 +47,7 @@ int main()
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
{
|
||||
// empty queue push rvalue/non_copyable succeeds
|
||||
boost::sync_queue<non_copyable> q;
|
||||
@@ -108,11 +108,11 @@ int main()
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
{
|
||||
// empty queue try_push rvalue/non-copyable succeeds
|
||||
boost::sync_queue<non_copyable> q;
|
||||
BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable()));
|
||||
BOOST_TEST(boost::queue_op_status::success ==q.try_push(non_copyable(1)));
|
||||
BOOST_TEST(! q.empty());
|
||||
BOOST_TEST(! q.full());
|
||||
BOOST_TEST_EQ(q.size(), 1u);
|
||||
@@ -150,7 +150,7 @@ int main()
|
||||
BOOST_TEST(! q.closed());
|
||||
}
|
||||
|
||||
#if 0
|
||||
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
|
||||
{
|
||||
// empty queue nonblocking_push rvalue/non-copyable succeeds
|
||||
boost::sync_queue<non_copyable> q;
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
#if ! defined BOOST_NO_CXX11_DECLTYPE
|
||||
#define BOOST_RESULT_OF_USE_DECLTYPE
|
||||
#endif
|
||||
#define BOOST_THREAD_PROVIDES_EXECUTORS
|
||||
|
||||
#include <boost/thread/future.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <cassert>
|
||||
#include <boost/thread/executors/basic_thread_pool.hpp>
|
||||
|
||||
|
||||
struct TestCallback
|
||||
@@ -19,12 +22,14 @@ struct TestCallback
|
||||
|
||||
result_type operator()(boost::future<void> future) const
|
||||
{
|
||||
assert(future.is_ready());
|
||||
future.get();
|
||||
return boost::make_ready_future();
|
||||
}
|
||||
|
||||
result_type operator()(boost::future<boost::future<void> > future) const
|
||||
{
|
||||
assert(future.is_ready());
|
||||
future.get();
|
||||
return boost::make_ready_future();
|
||||
}
|
||||
@@ -33,12 +38,24 @@ struct TestCallback
|
||||
int main()
|
||||
{
|
||||
#if ! defined BOOST_NO_CXX11_DECLTYPE && ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS
|
||||
{
|
||||
boost::promise<void> test_promise;
|
||||
boost::future<void> test_future(test_promise.get_future());
|
||||
auto f1 = test_future.then(TestCallback());
|
||||
BOOST_STATIC_ASSERT(std::is_same<decltype(f1), boost::future<boost::future<void> > >::value);
|
||||
auto f2 = f1.then(TestCallback());
|
||||
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
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8,9 +8,12 @@
|
||||
#if ! defined BOOST_NO_CXX11_DECLTYPE
|
||||
#define BOOST_RESULT_OF_USE_DECLTYPE
|
||||
#endif
|
||||
#define BOOST_THREAD_PROVIDES_EXECUTORS
|
||||
|
||||
#include <boost/thread/future.hpp>
|
||||
#include <boost/static_assert.hpp>
|
||||
#include <cassert>
|
||||
#include <boost/thread/executors/basic_thread_pool.hpp>
|
||||
|
||||
struct TestCallback
|
||||
{
|
||||
@@ -18,19 +21,35 @@ struct TestCallback
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
void p1()
|
||||
{
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int number_of_tests = 2;
|
||||
|
||||
#if ! defined BOOST_NO_CXX11_DECLTYPE && ! defined BOOST_NO_CXX11_AUTO_DECLARATIONS
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
{
|
||||
@@ -39,29 +58,104 @@ int main()
|
||||
f1.wait();
|
||||
}
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
for (int i=0; i< number_of_tests; i++)
|
||||
{
|
||||
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);
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
auto f2 = f1.unwrap();
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
BOOST_STATIC_ASSERT(std::is_same<decltype(f2), boost::future<void> >::value);
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
f2.wait();
|
||||
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());
|
||||
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(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();
|
||||
}
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
for (int i=0; i< number_of_tests; i++)
|
||||
{
|
||||
boost::make_ready_future().then(
|
||||
TestCallback()).unwrap().then(TestCallback()).get();
|
||||
}
|
||||
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
|
||||
for (int i=0; i< number_of_tests; i++)
|
||||
{
|
||||
boost::future<void> f = boost::async(p1);
|
||||
f.then(
|
||||
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
|
||||
return 0;
|
||||
}
|
||||
|
||||
70
test/test_11053.cpp
Normal file
70
test/test_11053.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// 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.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
#include <boost/shared_ptr.hpp>
|
||||
#include <iostream>
|
||||
|
||||
struct A
|
||||
{
|
||||
void DoWork()
|
||||
{
|
||||
std::cout << "A: doing work\n";
|
||||
if (!m_ptr.get())
|
||||
m_ptr.reset(new WorkSpace());
|
||||
// do not very much
|
||||
for (size_t i = 0; i < 10; ++i)
|
||||
m_ptr->a += 10;
|
||||
}
|
||||
|
||||
private:
|
||||
struct WorkSpace
|
||||
{
|
||||
int a;
|
||||
WorkSpace() : a(0) {}
|
||||
};
|
||||
boost::thread_specific_ptr<WorkSpace> m_ptr;
|
||||
};
|
||||
|
||||
struct B
|
||||
{
|
||||
void DoWork()
|
||||
{
|
||||
std::cout << "B: doing work\n";
|
||||
if (!m_ptr.get())
|
||||
m_ptr.reset(new A());
|
||||
m_ptr->DoWork();
|
||||
}
|
||||
private:
|
||||
boost::thread_specific_ptr<A> m_ptr;
|
||||
};
|
||||
|
||||
struct C
|
||||
{
|
||||
void DoWork()
|
||||
{
|
||||
std::cout << "C: doing work\n";
|
||||
if (!m_ptr.get())
|
||||
m_ptr.reset(new B());
|
||||
m_ptr->DoWork();
|
||||
}
|
||||
private:
|
||||
boost::thread_specific_ptr<B> m_ptr;
|
||||
};
|
||||
|
||||
int main(int ac, char** av)
|
||||
{
|
||||
std::cout << "test starting\n";
|
||||
boost::shared_ptr<C> p_C(new C);
|
||||
boost::thread cWorker(&C::DoWork, p_C);
|
||||
cWorker.join();
|
||||
std::cout << "test stopping\n";
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
42
test/test_11256.cpp
Normal file
42
test/test_11256.cpp
Normal 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
29
test/test_11266.cpp
Normal 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
|
||||
}
|
||||
@@ -24,18 +24,37 @@ void foo(IntPromise p)
|
||||
|
||||
void bar(boost::future<int> fooResult)
|
||||
{
|
||||
try {
|
||||
std::cout << "bar" << std::endl;
|
||||
int i = fooResult.get(); // Code hangs on this line (Due to future already being locked by the set_value call)
|
||||
std::cout << "i: " << i << std::endl;
|
||||
} catch(...) {
|
||||
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
|
||||
try {
|
||||
IntPromise p(new boost::promise<int>());
|
||||
boost::thread t(boost::bind(foo, p));
|
||||
boost::future<int> f1 = p->get_future();
|
||||
//f1.then(launch::deferred, boost::bind(bar, _1));
|
||||
f1.then(boost::launch::deferred, &bar);
|
||||
t.join();
|
||||
} catch(...) {
|
||||
return 1;
|
||||
}
|
||||
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
|
||||
try {
|
||||
IntPromise p(new boost::promise<int>());
|
||||
boost::thread t(boost::bind(foo, p));
|
||||
boost::future<int> f1 = p->get_future();
|
||||
f1.then(boost::launch::async, &bar);
|
||||
t.join();
|
||||
} catch(...) {
|
||||
return 2;
|
||||
}
|
||||
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,11 @@ int main() {
|
||||
}
|
||||
}
|
||||
if(failures)
|
||||
std::cerr << "There were " << failures << " failures out of " << total << " timed waits." << std::endl;
|
||||
return failures!=0;
|
||||
std::cout << "There were " << failures << " failures out of " << total << " timed waits." << std::endl;
|
||||
if((100*failures)/total>10)
|
||||
{
|
||||
std::cerr << "This exceeds 10%, so failing the test." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
using namespace boost::chrono;
|
||||
|
||||
typedef boost::scheduled_thread_pool scheduled_tp;
|
||||
typedef boost::scheduled_thread_pool<> scheduled_tp;
|
||||
|
||||
void fn(int x)
|
||||
{
|
||||
@@ -35,23 +35,29 @@ void func(steady_clock::time_point pushed, steady_clock::duration dur)
|
||||
{
|
||||
BOOST_TEST(pushed + dur < steady_clock::now());
|
||||
}
|
||||
void func2(scheduled_tp* tp, steady_clock::duration d)
|
||||
{
|
||||
boost::function<void()> fn = boost::bind(func,steady_clock::now(),d);
|
||||
tp->submit_after(fn, d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void test_timing(const int n)
|
||||
{
|
||||
//This function should take n seconds to execute.
|
||||
boost::scheduled_thread_pool se(4);
|
||||
boost::scheduled_thread_pool<> se(4);
|
||||
|
||||
for(int i = 1; i <= n; i++)
|
||||
{
|
||||
se.submit_after(boost::bind(fn,i), milliseconds(i*100));
|
||||
}
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(10));
|
||||
//dtor is called here so all task will have to be executed before we return
|
||||
}
|
||||
|
||||
void test_deque_timing()
|
||||
{
|
||||
boost::scheduled_thread_pool se(4);
|
||||
boost::scheduled_thread_pool<> se(4);
|
||||
for(int i = 0; i < 10; i++)
|
||||
{
|
||||
steady_clock::duration d = milliseconds(i*100);
|
||||
@@ -67,8 +73,9 @@ void test_deque_multi(const int n)
|
||||
for(int i = 0; i < n; i++)
|
||||
{
|
||||
steady_clock::duration d = milliseconds(i*100);
|
||||
boost::function<void()> fn = boost::bind(func,steady_clock::now(),d);
|
||||
tg.create_thread(boost::bind(boost::mem_fn(&scheduled_tp::submit_after), &se, fn, d));
|
||||
//boost::function<void()> fn = boost::bind(func,steady_clock::now(),d);
|
||||
//tg.create_thread(boost::bind(boost::mem_fn(&scheduled_tp::submit_after), &se, fn, d));
|
||||
tg.create_thread(boost::bind(func2, &se, d));
|
||||
}
|
||||
tg.join_all();
|
||||
//dtor is called here so execution will block untill all the closures
|
||||
@@ -77,10 +84,10 @@ void test_deque_multi(const int n)
|
||||
|
||||
int main()
|
||||
{
|
||||
steady_clock::time_point start = steady_clock::now();
|
||||
//steady_clock::time_point start = steady_clock::now();
|
||||
test_timing(5);
|
||||
steady_clock::duration diff = steady_clock::now() - start;
|
||||
BOOST_TEST(diff > milliseconds(500));
|
||||
//steady_clock::duration diff = steady_clock::now() - start;
|
||||
//BOOST_TEST(diff > milliseconds(500));
|
||||
test_deque_timing();
|
||||
test_deque_multi(4);
|
||||
test_deque_multi(8);
|
||||
|
||||
@@ -27,7 +27,6 @@ typedef boost::executors::basic_thread_pool thread_pool;
|
||||
|
||||
void fn(int x)
|
||||
{
|
||||
//std::cout << "[" << __LINE__ << "] " << steady_clock::now() << std::endl;
|
||||
std::cout << x << std::endl;
|
||||
}
|
||||
|
||||
@@ -75,7 +74,7 @@ int main()
|
||||
test_after(5, sch);
|
||||
test_at(5, sch);
|
||||
test_on(5, sch, tp);
|
||||
boost::this_thread::sleep_for(boost::chrono::seconds(10));
|
||||
std::cout << "[" << __LINE__ << "] " << std::endl;
|
||||
|
||||
return boost::report_errors();
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user