2
0
mirror of https://github.com/boostorg/thread.git synced 2026-02-03 21:52:07 +00:00

Compare commits

..

54 Commits

Author SHA1 Message Date
Vicente J. Botet Escriba
8961e8d593 go on with the future refactoring. 2015-10-14 03:04:26 +02:00
Vicente J. Botet Escriba
faca9512f6 minor fix on clang with executor forward declaration. 2015-10-13 23:55:38 +02:00
Vicente J. Botet Escriba
7df0d13763 minor fix on clang with executor forward declaration. 2015-10-13 19:15:18 +02:00
Vicente J. Botet Escriba
c3cd767ea4 make executor_adaptor copyable/movable. 2015-10-13 19:13:56 +02:00
Vicente J. Botet Escriba
5a57ab857b comment serial_executor_cont test as not supported. 2015-10-13 03:08:59 +02:00
Vicente J. Botet Escriba
b80130759a make executor_adaptor copy constructible. 2015-10-13 03:08:09 +02:00
Vicente J. Botet Escriba
ff9a1da3b2 Merge from develop. 2015-10-12 01:44:31 +02:00
Vicente J. Botet Escriba
51f68f4717 fix c++98 move emmulation. 2015-09-30 23:51:32 +02:00
Vicente J. Botet Escriba
50cca7429a fix c++98 move emmulation. 2015-09-30 23:07:59 +02:00
Vicente J. Botet Escriba
6d533763a0 replace >> by > >. 2015-09-30 22:37:13 +02:00
Vicente J. Botet Escriba
f4c223662b use init for unwraped future also. 2015-09-30 16:39:51 +02:00
Vicente J. Botet Escriba
011f9c80c0 merge from develop. 2015-09-30 07:12:50 +02:00
Vicente J. Botet Escriba
4fca45e58c Merge branch 'develop' into feature/make_executors_copyable 2015-09-29 02:47:42 +02:00
Vicente J. Botet Escriba
02fab84a04 abstract common behavior on continuation_shared_state. 2015-09-29 02:11:27 +02:00
Vicente J. Botet Escriba
aebef44e64 Move post-construction initialization to init memberfunction. 2015-09-29 01:12:06 +02:00
Vicente J. Botet Escriba
5c1482a636 fix bad usage of share_from_this before the instance is ahared :(. 2015-09-28 23:48:20 +02:00
Vicente J. Botet Escriba
06179eda96 Merge branch 'develop' into feature/make_executors_copyable 2015-09-27 10:37:47 +02:00
Vicente J. Botet Escriba
5fccbf5dd1 merge from develop 2015-09-11 18:52:16 +02:00
Vicente J. Botet Escriba
7232c1c0ac cleanup aftermerge fromdevelop. 2015-09-03 07:49:35 +02:00
Vicente J. Botet Escriba
7dee098bea merge from develop. 2015-09-03 07:28:54 +02:00
Vicente J. Botet Escriba
6b6df268f5 merge make_executors_copyable. 2015-05-29 08:01:06 +02:00
Vicente J. Botet Escriba
bde5a1ef55 merge from develop. 2015-05-28 19:59:13 +02:00
Vicente J. Botet Escriba
a741bd1bba Merge branch 'develop' into fix/make_executors_copyable 2015-04-18 07:11:12 +02:00
Vicente J. Botet Escriba
caaa7b4cc2 store executor by value. 2015-03-08 23:30:41 +01:00
Vicente J. Botet Escriba
9a05211faa fix issue with c++03 compilers. Pass Executors by const& instead of by &. 2015-03-04 07:59:27 +01:00
Vicente J. Botet Escriba
7ffcec448c uncomment more tests. 2015-03-03 19:29:50 +01:00
Vicente J. Botet Escriba
62bffed368 More fixes to make executor copyable. 2015-03-03 08:27:17 +01:00
Vicente J. Botet Escriba
5a1de7a722 ensure that generic executors are copyable. 2015-03-03 00:50:48 +01:00
Vicente J. Botet Escriba
33ee3445af refactor basic_thread_pool. It doesn't works yet for at_thread_exit. Needs to replace function<void(basic_thread_pool)>. 2015-03-01 18:00:58 +01:00
Vicente J. Botet Escriba
8511771816 Merge branch 'develop' into fix/make_executors_copyable 2015-03-01 01:40:23 +01:00
Vicente J. Botet Escriba
7dbd04197d Make scheduled_thread_pool copyable. 2015-02-28 19:01:45 +01:00
Vicente J. Botet Escriba
a53f31fb99 Store the Executor in scheduling_adaptor. This class must be finished as it doesn't make use of the executor :(. 2015-02-28 17:07:57 +01:00
Vicente J. Botet Escriba
b2b8684d0c make scheduled_thread_pool design closer to basic_thread_pool. 2015-02-28 17:04:17 +01:00
Vicente J. Botet Escriba
df14c8ac18 fix the move(w) on scheduler and store copies of the Executors. 2015-02-28 16:29:00 +01:00
Vicente J. Botet Escriba
6e5a46c16f merge from develop. 2015-02-28 15:32:18 +01:00
Vicente J. Botet Escriba
264ed4c308 move the work parameter. 2015-02-28 10:44:44 +01:00
Vicente J. Botet Escriba
65c4693c87 Add missing push(movable&&) and Run some failing tests that work when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined. 2015-02-28 10:41:20 +01:00
Vicente J. Botet Escriba
05d6eca09d Run some failing tests that work when BOOST_NO_CXX11_RVALUE_REFERENCES is not defined. 2015-02-28 10:38:15 +01:00
Vicente J. Botet Escriba
c192777aef fix missing include in caller_context.hpp and let the possibility to dump function at compile time. 2015-02-28 09:53:09 +01:00
Vicente J. Botet Escriba
fdd1db970d cleanup work and store by value scheduler. 2015-02-28 09:06:57 +01:00
Vicente J. Botet Escriba
3bc5fb1725 fix a lot of things for c++11 compilers. There is yet a lot to do :( 2015-02-26 08:16:11 +01:00
Vicente J. Botet Escriba
9481562b5c update executors doc removing the move-only constraint. 2015-02-21 16:51:55 +01:00
Vicente J. Botet Escriba
e44b5309ae rename serial_executors to generic_serial_executors and let serial_executor be the template form. 2015-02-21 16:17:11 +01:00
Vicente J. Botet Escriba
eecf8f6c36 Allow polymorphic executors to be copiable. 2015-02-21 14:29:51 +01:00
Vicente J. Botet Escriba
532d215de9 Make serial_executor_cont copyable, and fix it: reschedule_until and try_executing_one must return false, as a serial executor can not re-enter. 2015-02-21 12:26:40 +01:00
Vicente J. Botet Escriba
71bce54c71 fix serial_exeutor: reschedule_until and try_executing_one must return false, as a serial executor can not re-enter. 2015-02-21 12:25:29 +01:00
Vicente J. Botet Escriba
41bde57707 Make scheduler copyable. 2015-02-21 11:21:20 +01:00
Vicente J. Botet Escriba
ff7e394084 remove last sleep as now the tasks block the executors shared state lifetime as it is copied. 2015-02-21 11:20:42 +01:00
Vicente J. Botet Escriba
81f67eeb54 Change copyright date. 2015-02-21 11:18:08 +01:00
Vicente J. Botet Escriba
a4827a31f3 Change copyright date. 2015-02-21 11:16:19 +01:00
Vicente J. Botet Escriba
cd31e9c34f Make executor_adaptor copyable. 2015-02-21 01:00:12 +01:00
Vicente J. Botet Escriba
9492bcd485 Make serial_executor copyable. Replace generic_executor_ref by generic_executor. 2015-02-20 22:26:12 +01:00
Vicente J. Botet Escriba
ff9457e79c make basic_thread_pool copyable. 2015-02-20 20:47:30 +01:00
Vicente J. Botet Escriba
de580474a3 make inline_executor, loop_executor and thread_executor copyable. 2015-02-20 19:11:08 +01:00
65 changed files with 3433 additions and 1706 deletions

View File

@@ -60,8 +60,7 @@ project boost/thread
<toolset>gcc:<cxxflags>-Wno-long-long
#<toolset>gcc:<cxxflags>-ansi
#<toolset>gcc:<cxxflags>-fpermissive
<toolset>gcc-4:<cxxflags>-Wno-variadic-macros
<toolset>gcc-5:<cxxflags>-Wno-variadic-macros
<toolset>gcc:<cxxflags>-Wno-variadic-macros
#<toolset>gcc:<cxxflags>-Wunused-local-typedefs
<toolset>gcc:<cxxflags>-Wunused-function
<toolset>gcc:<cxxflags>-Wno-unused-parameter
@@ -71,9 +70,7 @@ project boost/thread
#<toolset>darwin:<cxxflags>-ansi
<toolset>darwin:<cxxflags>-fpermissive
<toolset>darwin:<cxxflags>-Wno-long-long
#<toolset>darwin:<cxxflags>-Wno-variadic-macros
<toolset>darwin-4:<cxxflags>-Wno-variadic-macros
<toolset>darwin-5:<cxxflags>-Wno-variadic-macros
<toolset>darwin:<cxxflags>-Wno-variadic-macros
#<toolset>darwin:<cxxflags>-Wunused-local-typedefs
<toolset>darwin:<cxxflags>-Wunused-function
<toolset>darwin:<cxxflags>-Wno-unused-parameter

View File

@@ -112,7 +112,7 @@ A question arises of which of these executors (or others) be included in this li
[////////////////////////]
[section:rationale Design Rationale]
The authors of Boost.Thread have taken a different approach respect to N3785. Instead of basing all the design on an abstract executor class we make executor concepts. We believe that this is the good direction as a static polymorphic executor can be seen as a dynamic polymorphic executor using a simple adaptor. We believe also that it would make the library more usable, and more convenient for users.
The authors of Boost.Thread have taken a different approach respect to N3785. Instead of basing all the design on a abstract executor class we make executor concepts. We believe that this is the good direction as a static polymorphic executor can be seen as a dynamic polymorphic executor using a simple adaptor. We believe also that it would make the library more usable, and more convenient for users.
The major design decisions concern deciding what a unit of work is, how to manage with units of work and time related functions in a polymorphic way.
@@ -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.
@@ -364,8 +364,8 @@ A type `E` meets the `Executor` requirements if the following expressions are we
where
* `e` denotes a value of type `E`,
* `lc` denotes a lvalue reference of type `Closure`,
* `rc` denotes a rvalue reference of type `Closure`
* `lc` denotes a lvalue referece of type `Closure`,
* `rc` denotes a rvalue referece of type `Closure`
* `p` denotes a value of type `Predicate`
[/////////////////////////////////////]
@@ -388,7 +388,7 @@ If invoked closure throws an exception the executor will call std::terminate, as
[endsect]
[/////////////////////////////////////]
[section:submitrc `e.submit(rc);`]
[section:submitrc `e.submit(lc);`]
[variablelist
@@ -417,7 +417,7 @@ If invoked closure throws an exception the executor will call std::terminate, as
[[Return type:] [`void`.]]
[[Throws:] [Whatever exception that can be thrown while ensuring the thread safety.]]
[[Throws:] [Whatever exception that can be throw while ensuring the thread safety.]]
[[Exception safety:] [If an exception is thrown then the executor state is unmodified.]]
@@ -462,7 +462,7 @@ If invoked closure throws an exception the executor will call std::terminate, as
[variablelist
[[Requires:] [This must be called from a scheduled work]]
[[Requires:] [This must be called from an scheduled work]]
[[Effects:] [reschedule works until `p()`.]]
@@ -506,9 +506,6 @@ Executor abstract base class.
public:
typedef boost::work work;
executor(executor const&) = delete;
executor& operator=(executor const&) = delete;
executor();
virtual ~executor() {};
@@ -533,7 +530,7 @@ Executor abstract base class.
[variablelist
[[Effects:] [Constructs an executor. ]]
[[Effects:] [Constructs a executor. ]]
[[Throws:] [Nothing. ]]
@@ -573,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);
@@ -600,7 +594,7 @@ Polymorphic adaptor of a model of Executor to an executor.
[variablelist
[[Effects:] [Constructs an executor_adaptor. ]]
[[Effects:] [Constructs a executor_adaptor. ]]
[[Throws:] [Nothing. ]]
@@ -641,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;
@@ -669,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 `]
@@ -683,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();
@@ -733,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.]]
]
@@ -1339,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;
@@ -1393,7 +1422,7 @@ A serial executor ensuring that there are no two work units that executes concur
[/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`]
generic_executor_ref& underlying_executor() noexcept;
Executor& underlying_executor() noexcept;
[variablelist
@@ -1418,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();
@@ -1433,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);
@@ -1456,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();
@@ -1468,6 +1495,80 @@ A serial executor ensuring that there are no two work units that executes concur
]
[endsect]
[/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`]
generic_executor& underlying_executor() noexcept;
[variablelist
[[Return:] [The underlying executor instance. ]]
]
[endsect]
[endsect]
[//////////////////////////////////////////////////////////]
[section:serial_executor_cont Template Class `serial_executor_cont`]
A serial executor ensuring that there are no two work units that executes concurrently.
#include <boost/thread/serial_executor_cont.hpp>
namespace boost {
template <class Executor>
class serial_executor_cont
{
public:
serial_executor_cont(Executor& ex);
Executor& underlying_executor() noexcept;
void close();
bool closed();
template <typename Closure>
void submit(Closure&& closure);
bool try_executing_one();
template <typename Pred>
bool reschedule_until(Pred const& pred);
};
}
[/////////////////////////////////////]
[section:constructor Constructor `serial_executor_cont(Executor&)`]
template <class Executor>
serial_executor_cont(Executor& ex);
[variablelist
[[Effects:] [Constructs a serial_executor_cont. ]]
[[Throws:] [Nothing. ]]
]
[endsect]
[/////////////////////////////////////]
[section:destructor Destructor `~serial_executor_cont()`]
~serial_executor_cont();
[variablelist
[[Effects:] [Destroys the serial_executor.]]
[[Synchronization:] [The completion of all the closures happen before the completion of the executor destructor.]]
]
[endsect]
[/////////////////////////////////////]
[section:underlying_executor Function member `underlying_executor()`]
@@ -1478,6 +1579,8 @@ A serial executor ensuring that there are no two work units that executes concur
[[Return:] [The underlying executor instance. ]]
[[Throws:] [Nothing.]]
]
@@ -1485,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`]
@@ -1496,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();
@@ -1521,7 +1698,7 @@ A serial executor ensuring that there are no two work units that executes concur
[variablelist
[[Effects:] [Constructs an inline_executor. ]]
[[Effects:] [Constructs a inline_executor. ]]
[[Throws:] [Nothing. ]]
@@ -1560,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>
@@ -1623,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);
@@ -1680,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();
@@ -1709,7 +1877,7 @@ A user scheduled executor.
[variablelist
[[Effects:] [creates an executor that runs closures using one of its closure-executing methods. ]]
[[Effects:] [creates a executor that runs closures using one of its closure-executing methods. ]]
[[Throws:] [Whatever exception is thrown while initializing the needed resources. ]]

View File

@@ -8,34 +8,6 @@
[section:changes History]
[heading Version 4.7.0 - boost 1.61]
[*Know Bugs:]
* [@http://svn.boost.org/trac/boost/ticket/3926 #3926] thread_specific_ptr + dlopen library causes a SIGSEGV.
Please define BOOST_THREAD_PATCH to apply the patch that could unfortunately results is a regression as described in [@http://svn.boost.org/trac/boost/ticket/12049 #12049].
* [@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/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
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 regression test snapshot.
[*New Experimental Features:]
* [@http://svn.boost.org/trac/boost/ticket/11772 #11772] Add a launch::sync policy
[*Fixed Bugs:]
* [@http://svn.boost.org/trac/boost/ticket/11494 #11494] boost::this_thread::yield() is marked as deprecated in the synopsis
* [@http://svn.boost.org/trac/boost/ticket/11562 #11562] (condition_variable_any::wait_until + recursive_mutex + steady_clock) timer expires after computer time is set forward on Ubuntu 64-bit
* [@http://svn.boost.org/trac/boost/ticket/12013 #12013] F_pass and FArgs_pass tests segfault
* [@http://svn.boost.org/trac/boost/ticket/12036 #12036] boost::physical_concurrency always returns 0 if BOOST_USE_WINAPI_VERSION is not defined
[heading Version 4.6.0 - boost 1.60]
[*Know Bugs:]
@@ -51,16 +23,11 @@ Please take a look at [@http://www.boost.org/development/tests/master/developer/
[*New Experimental Features:]
* [@http://svn.boost.org/trac/boost/ticket/11231 #11231] Allow to set continuation future's destructor behavior to non-blocking
* [@http://svn.boost.org/trac/boost/ticket/11424 #11424] Provide shared_timed_mutex as an alternative name for shared_mutex and deprecate the use of shared_mutex as a timed mutex
* [@http://svn.boost.org/trac/boost/ticket/11734 #11734] future::then(Cont) should be able to execute the continuation on undetermined thread
* [@http://svn.boost.org/trac/boost/ticket/11736 #11736] Allow to use launch::executor on future::then(launch::executor, cont)
* [@http://svn.boost.org/trac/boost/ticket/11737 #11737] Add a launch::inherit policy that can be used on ::then() to use the policy of the parent future
[*Fixed Bugs:]
* [@http://svn.boost.org/trac/boost/ticket/3926 #3926] thread_specific_ptr + dlopen library causes a SIGSEGV.
* [@http://svn.boost.org/trac/boost/ticket/6377 #6377] Condition variable blocks when changing time
* [@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/7665 #7665] this_thread::sleep_for no longer uses steady_clock in thread
@@ -68,33 +35,23 @@ Please take a look at [@http://www.boost.org/development/tests/master/developer/
* [@http://svn.boost.org/trac/boost/ticket/9309 #9309] test_latch fails often on clang-darwin-tot11
* [@http://svn.boost.org/trac/boost/ticket/10788 #10788] GetLogicalProcessor isn't available for Windows platform less or equals to 0x0502
* [@http://svn.boost.org/trac/boost/ticket/11090 #11090] ex_future_unwrap- ThreadSanitizer: lock-order-inversion (potential deadlock)
* [@http://svn.boost.org/trac/boost/ticket/11158 #11158] Pthread thread deadlock when faketime used
* [@http://svn.boost.org/trac/boost/ticket/11174 #11174] boost::condition_variable::timed_wait with predicate unexpectedly wakes up while should wait infinite
* [@http://svn.boost.org/trac/boost/ticket/11185 #11185] Incorrect URL redirection
* [@http://svn.boost.org/trac/boost/ticket/11192 #11192] boost::future<>::then() with an executor doesn't compile when the callback returns a future
* [@http://svn.boost.org/trac/boost/ticket/11250 #11250] future made from make_exceptional fails on assertion in destructor
* [@http://svn.boost.org/trac/boost/ticket/11256 #11256] future<>::is_ready() == false in continuation function
* [@http://svn.boost.org/trac/boost/ticket/11158 #11158] Pthread thread deadlock when faketime used
* [@http://svn.boost.org/trac/boost/ticket/11261 #11261] bad use of scoped threads in basic_thread_pool
* [@http://svn.boost.org/trac/boost/ticket/11262 #11262] bad use of direct pointer in shared_state_nullary_task
* [@http://svn.boost.org/trac/boost/ticket/11263 #11263] lock already locked lock
* [@http://svn.boost.org/trac/boost/ticket/11266 #11266] boost::packaged_task has invalid variadic signature
* [@http://svn.boost.org/trac/boost/ticket/11302 #11302] boost thread doesn't build with BOOST_THREAD_PATCH.
* [@http://svn.boost.org/trac/boost/ticket/11322 #11322] sleep_for() nanoseconds overload will always return too early on windows
* [@http://svn.boost.org/trac/boost/ticket/11329 #11329] using declarative for GetProcessHeap, .... fails
* [@http://svn.boost.org/trac/boost/ticket/11368 #11368] boost thread's usage of CreateWaitableTimer wakes PC from sleep (doh)
* [@http://svn.boost.org/trac/boost/ticket/11377 #11377] Boost condition variable always waits for system clock deadline
* [@http://svn.boost.org/trac/boost/ticket/11435 #11435] gcc compiler warning in future.hpp
* [@http://svn.boost.org/trac/boost/ticket/11555 #11555] devector.hpp assumes allocator_traits_type is always present
* [@http://svn.boost.org/trac/boost/ticket/11562 #11562] (condition_variable_any::wait_until + recursive_mutex + steady_clock) timer expires after computer time is set forward on Ubuntu 64-bit
* [@http://svn.boost.org/trac/boost/ticket/11562 #11562] Timer (using steady_clock) expires after computer time is set forward on Ubuntu 64-bit
* [@http://svn.boost.org/trac/boost/ticket/11672 #11672] Thread: Should use unique_ptr, not auto_ptr
* [@http://svn.boost.org/trac/boost/ticket/11688 #11688] thread::try_join_until: Avoid busy wait if system clock changes
* [@http://svn.boost.org/trac/boost/ticket/11672 #11716] ::then(f) should inherit the parent Executor
* [@http://svn.boost.org/trac/boost/ticket/11795 #11795] Incorrect version specification for documentation of thread destructor
* [@http://svn.boost.org/trac/boost/ticket/11796 #11796] Thread move assignment operator, does not detach previous thread data
* [@http://svn.boost.org/trac/boost/ticket/11817 #11817] 'sync_queue_is_closed' was not declared in boost/thread/executors/thread_executor.hpp
* [@http://svn.boost.org/trac/boost/ticket/11818 #11818] future.then will be blocked if promise is set after the invocation of then
* [@http://svn.boost.org/trac/boost/ticket/12049 #12049] Assertion failure from detached threads during shutdown
[heading Version 4.5.0 - boost 1.58]

View File

@@ -81,7 +81,7 @@ When `BOOST_THREAD_VERSION>3` && defined BOOST_THREAD_PLATFORM_PTHREAD define `
[section:move Boost.Atomic]
Boost.Thread uses by default Boost.Atomic in POSIX platforms to implement call_once..
Boost.Thread uses by default an Boost.Atomic in POSIX platforms to implement call_once..
Define `BOOST_THREAD_USES_ATOMIC ` if you want to use Boost.Atomic.
Define `BOOST_THREAD_DONT_USE_ATOMIC ` if you don't want to use Boost.Atomic or if it is not supported in your platform.

View File

@@ -267,7 +267,7 @@ The library provides un implicit conversion to an undefined type that can be use
explicit operator bool() const;
#endif
The user should use the lock.owns_lock() when an explicit conversion is required.
The user should use the lock.owns_lock() when a explicit conversion is required.
[section:bool_conversion `operator `['unspecified-bool-type]`() const`]
@@ -324,7 +324,7 @@ the library declare these types as
}
BOOST_SCOPED_ENUM_DECLARE_END(future_errc)
These macros allows to use 'future_errc' in almost all the cases as a scoped enum.
These macros allows to use 'future_errc' in almost all the cases as an scoped enum.
There are however some limitations:

View File

@@ -29,11 +29,9 @@
enum class launch
{
none = unspecified,
async = unspecified,
deferred = unspecified,
executor = unspecified,
inherit = unspecified,
any = async | deferred
};
@@ -167,27 +165,14 @@
enum class launch
{
none = unspecified,
async = unspecified,
deferred = unspecified,
executor = unspecified,
inherit = unspecified,
any = async | deferred
};
The enum type launch is a bitmask type with launch::async and launch::deferred denoting individual bits.
A future created with `promise<>` or with a `packaged_task<>` or with `make_ready_future`/`make_exceptional_future` (has no associated launch policy), has an implicit a launch policy of `launch::none`.
A future created by `async(launch::async, ...)` or `::then(launch::async, ...)` has associated a launch policy `launch::async`.
A future created by `async(launch::deferred, ...)` or `::then(launch::deferred, ...)` has associated a launch policy `launch::deferred`.
A future created by `async(Executor, ...)` or `::then(Executor, ...)` or `::then(launch::executor, ...)` has associated a launch policy `launch::executor`.
A future created by `async(...)` or `::then(...)` has associated a launch policy `launch::none`.
A future created by `::then(launch::inherit, ...)` has associated a launch policy parent future.
The `executor` and the `inherit` launch policies have a sense only can be user only on `then()`.
[endsect]
[///////////////////////////////////////////////////////////////////////////]
[section:is_error_code_enum Specialization `is_error_code_enum<future_errc>`]
@@ -848,7 +833,7 @@ stored exception, `false` otherwise.]]
[[Effects:] [If `*this` is associated with a shared state, waits until the result is ready. If the result is not ready on
entry, and the result has a ['wait callback] set, that callback is invoked prior to waiting.]]
[[Returns:] [an exception_ptr, storing or not an exception.]]
[[Returns:] [a exception_ptr, storring or not an exception.]]
[[Remarks:] [The result of this function is not stable and the future could lost its exception even if the function returned a valid `exception_ptr` or vice-versa.]]
@@ -913,41 +898,29 @@ There are not too much tests yet, so it is possible that you can find out some t
[variablelist
[[Notes:] [The three functions differ only by input parameters. The first only takes a callable object which accepts a
future object as a parameter. The second function takes an executor as the first parameter and a callable object as
future object as a parameter. The second function takes a executor as the first parameter and a callable object as
the second parameter. The third function takes a launch policy as the first parameter and a callable object as the
second parameter.]]
[[Requires:] [`INVOKE(DECAY_COPY (std::forward<F>(func)), std::move(*this))` shall be a valid expression.]]
[[Effects:] [
All the functions create a shared state that is associated with the returned future object. Additionally,
All the functions create a shared state that is associated with the returned future object. The further behavior of the functions is as follows.
- When the object's shared state is ready, the continuation
`INVOKE(DECAY_COPY(std::forward<F>(func)), std::move(*this))` is called depending on the overload (see below) with the call to DECAY_COPY() being evaluated in the thread that called then.
- The continuation is called when the object's shared state is ready (has a value or exception stored).
- Any value returned from the continuation is stored as the result in the shared state of the resulting `future`.
Any exception propagated from the execution of the continuation is stored as the exceptional result in the shared state of the resulting `future`.
- The continuation launches according to the specified policy or executor.
- When the executor or launch policy is not provided the continuation inherits the
parent's launch policy or executor.
The continuation launches according to the specified policy or executor or noting.
- Any value returned from the continuation is stored as the result in the shared state of the resulting `future`. Any exception propagated from the execution of the continuation is stored as the exceptional result in the shared state of the resulting `future`.
- When the launch policy is `launch::none` the continuation is called on an unspecified thread of execution.
- If the parent was created with `promise<>` or with a `packaged_task<>` (has no associated launch policy), the
continuation behaves the same as the third overload with a policy argument of `launch::async | launch::deferred` and
the same argument for `func`.
- When the launch policy is `launch::async` the continuation is called on a new thread of execution.
- When the launch policy is `launch::deferred` the continuation is called on demand.
- When the launch policy is `launch::executor` the continuation is called on one of the thread of execution of the executor.
- When the launch policy is `launch::inherit` the continuation inherits the parent's launch policy or executor.
- When the executor or launch policy is not provided (first overload) is if as if launch::none was specified.
- When the executor is provided (second overload) the continuation is called on one of the thread of execution of the executor.
- If the parent has a policy of `launch::deferred` and the continuation does not have a specified launch policy
executor, then the parent is filled by immediately calling `.wait()`, and the policy of the antecedent is
- If the parent has a policy of `launch::deferred` and the continuation does not have a specified launch policy or
scheduler, then the parent is filled by immediately calling `.wait()`, and the policy of the antecedent is
`launch::deferred`.
]]
@@ -1357,7 +1330,7 @@ stored exception, `false` otherwise.]]
[[Effects:] [If `*this` is associated with a shared state, waits until the result is ready. If the result is not ready on
entry, and the result has a ['wait callback] set, that callback is invoked prior to waiting.]]
[[Returns:] [an exception_ptr, storing or not an exception.]]
[[Returns:] [a exception_ptr, storring or not an exception.]]
[[Throws:] [Whatever `mutex::lock()/mutex::unlock()` can throw.]]
@@ -1407,38 +1380,22 @@ shared_future object as a parameter. The second function takes an executor as th
the second parameter. The third function takes a launch policy as the first parameter and a callable object as the
second parameter.]]
[[Requires:] [`INVOKE(DECAY_COPY (std::forward<F>(func)), *this)` shall be a valid expression.]]
[[Effects:] [
All the functions create a shared state that is associated with the returned future object. Additionally,
- The continuation is called when the object's shared state is ready (has a value or exception stored).
- When the object's shared state is ready, the continuation
`INVOKE(DECAY_COPY(std::forward<F>(func)), *this)` is called depending on the overload (see below) with the call to DECAY_COPY() being evaluated in the thread that called then.
- The continuation launches according to the specified policy or executor.
- Any value returned from the continuation is stored as the result in the shared state of the resulting `future`.
Any exception propagated from the execution of the continuation is stored as the exceptional result in the shared state of the resulting `future`.
- When the executor or launch policy is not provided the continuation inherits the
parent's launch policy or executor.
- If the parent was created with `promise` or with a `packaged_task` (has no associated launch policy), the
continuation behaves the same as the third overload with a policy argument of `launch::async | launch::deferred` and
the same argument for func.
The continuation launches according to the specified policy or executor or noting.
- When the launch policy is `launch::none` the continuation is called on an unspecified thread of execution.
- When the launch policy is `launch::async` the continuation is called on a new thread of execution.
- When the launch policy is `launch::deferred` the continuation is called on demand.
- When the launch policy is `launch::executor` the continuation is called on one of the thread of execution of the executor.
- When the launch policy is `launch::inherit` the continuation inherits the parent's launch policy or executor.
- When the executor or launch policy is not provided (first overload) is if as if launch::none was specified.
- When the executor is provided (second overload) the continuation is called on one of the thread of execution of the executor.
- If the parent has a policy of `launch::deferred` and the continuation does not have a specified launch policy
- If the parent has a policy of `launch::deferred` and the continuation does not have a specified launch policy or
executor, then the parent is filled by immediately calling `.wait()`, and the policy of the antecedent is
`launch::deferred`.
`launch::deferred`
]]

View File

@@ -326,7 +326,7 @@ Using a `shared_future` solves the issue
[heading share()]
Naming the return type when declaring the `shared_future` is needed; auto is not available within template argument lists.
Namming the return type when declaring the `shared_future` is needed; auto is not available within template argument lists.
Here `share()` could be used to simplify the code
void better_second_use( type arg ) {
@@ -344,7 +344,7 @@ Here `share()` could be used to simplify the code
[heading Writing on get()]
The user can either read or write the future variable.
The user can either read or write the future avariable.
void write_to_get( type arg ) {
@@ -365,7 +365,7 @@ The user can either read or write the future variable.
This works because the `shared_future<>::get()` function returns a non-const reference to the appropriate storage.
Of course the access to this storage must be ensured by the user. The library doesn't ensure the access to the internal storage is thread safe.
There has been some work by the C++ standard committee on an `atomic_future` that behaves as an `atomic` variable, that is thread_safe,
There has been some work by the C++ standard committe on an `atomic_future` that behaves as an `atomic` variable, that is is thread_safe,
and a `shared_future` that can be shared between several threads, but there were not enough consensus and time to get it ready for C++11.
[endsect]
@@ -444,7 +444,7 @@ Input Parameters:
success and one for error handling. However this option has not been retained for the moment.
The lambda function takes a future as its input which carries the exception
through. This makes propagating exceptions straightforward. This approach also simplifies the chaining of continuations.
* Executor: Providing an overload to `.then`, to take an executor reference places great flexibility over the execution
* Executor: Providing an overload to `.then`, to take a executor reference places great flexibility over the execution
of the future in the programmer's hand. As described above, often taking a launch policy is not sufficient for powerful
asynchronous operations. The lifetime of the executor must outlive the continuation.
* Launch policy: if the additional flexibility that the executor provides is not required.

View File

@@ -240,9 +240,9 @@ The following class describes a so-called monitor pattern.
template <
typename Lockable=mutex
>
class basic_monitor : protected basic_lockable_adapter<Lockable> { // behaves like a BasicLockable for the derived classes
class basic_monitor : protected basic_lockable_adapter<Lockable> { // behaves like an BasicLockable for the derived classes
protected:
typedef unspecified synchronizer; // is a strict lock guard
typedef unspecified synchronizer; // is an strict lock guard
};
[/shared_monitor]

View File

@@ -869,7 +869,7 @@ any other threads have shared ownership, blocks until exclusive ownership can be
[variablelist
[[Precondition:] [The calling thread shall hold an upgrade lock on the mutex.]]
[[Precondition:] [The calling thread shall hold a upgrade lock on the mutex.]]
[[Effects:] [The function attempts to atomically convert the ownership from upgrade to exclusive for the calling thread without blocking.
For this conversion to be successful, this thread must be the only thread holding any ownership of the lock.
@@ -893,7 +893,7 @@ If the conversion is not successful, the upgrade ownership of m is retained.]]
[variablelist
[[Precondition:] [The calling thread shall hold an upgrade lock on the mutex.]]
[[Precondition:] [The calling thread shall hold a upgrade lock on the mutex.]]
[[Effects:] [If the tick period of `rel_time` is not exactly convertible to the native tick period, the duration shall be rounded up to the nearest native tick period.
The function attempts to atomically convert the ownership from upgrade to exclusive for the calling thread within the relative timeout specified by `rel_time`.
@@ -919,7 +919,7 @@ If the conversion is not successful, the upgrade ownership of m is retained.]]
[variablelist
[[Precondition:] [The calling thread shall hold an upgrade lock on the mutex.]]
[[Precondition:] [The calling thread shall hold a upgrade lock on the mutex.]]
[[Effects:] [The function attempts to atomically convert the ownership from upgrade to exclusive for the calling thread within the absolute timeout specified by `abs_time`.
If `abs_time` has already passed, the function attempts to obtain exclusive ownership without blocking (as if by calling `__try_unlock_upgrade_and_lock()`).
@@ -2150,7 +2150,7 @@ object passed to the constructor.]]
__nested_strict_lock is a model of __StrictLock.
A nested strict lock is a scoped lock guard ensuring a mutex is locked on its
scope, by taking ownership of a nesting lock, locking the mutex on construction if not already locked
scope, by taking ownership of an nesting lock, locking the mutex on construction if not already locked
and restoring the ownership to the nesting lock on destruction.

View File

@@ -150,7 +150,7 @@ This wrapper can be used to join the thread before destroying it.
[variablelist
[[Effects:] [Construct an internal thread in place.]]
[[Effects:] [Construct a internal thread in place.]]
[[Postconditions:] [`*this.t_` refers to the newly created thread of execution and `this->get_id()!=thread::id()`.]]

View File

@@ -110,10 +110,10 @@ where
* `q` denotes a value of type `Q`,
* `e` denotes a value of type Q::value_type,
* `u` denotes a value of type Q::size_type,
* `lve` denotes an lvalue reference of type Q::value_type,
* `rve` denotes an rvalue reference of type Q::value_type:
* `lve` denotes a lvalue referece of type Q::value_type,
* `rve` denotes a rvalue referece of type Q::value_type:
[/* `spe` denotes a shared_ptr<Q::value_type>]
* `qs` denotes a variable of of type `queue_op_status`,
* `qs` denotes a variable of of type `queus_op_status`,
[/////////////////////////////////////]
@@ -246,8 +246,8 @@ where
* `e` denotes a value of type `Q::value_type`,
* `s` denotes a value of type `queue_status`,
* `u` denotes a value of type `Q::size_type`,
* `lve` denotes an lvalue reference of type Q::value_type,
* `rve` denotes an rvalue reference of type Q::value_type:
* `lve` denotes a lvalue referece of type Q::value_type,
* `rve` denotes a rvalue referece of type Q::value_type:
[/* `spe` denotes a shared_ptr<Q::value_type>]
@@ -357,8 +357,8 @@ where
* `q` denotes a value of type `Q`,
* `e` denotes a value of type Q::value_type,
* `s` denotes a value of type `queue_status`,
* `lve` denotes an lvalue reference of type Q::value_type,
* `rve` denotes an rvalue reference of type Q::value_type:
* `lve` denotes a lvalue referece of type Q::value_type,
* `rve` denotes a rvalue referece of type Q::value_type:
[/* `spe` denotes a shared_ptr<Q::value_type>]
@@ -545,7 +545,7 @@ Closed queues add the following valid expressions
[[Return:] [
- If the queue is closed return `queue_op_status::closed`,
- If the queue is closed retun `queue_op_status::closed`,
- otherwise, return `queue_op_status::success` if no exception is thrown.

View File

@@ -156,7 +156,7 @@ object passed to the constructor.]]
};
}
`externally_locked_stream` cloaks a reference to a stream of type `Stream`, and actually
`externally_locked_stream` cloaks a reference to an stream of type `Stream`, and actually
provides full access to that object through the `get` member functions, provided you
pass a reference to a strict lock object.

View File

@@ -63,7 +63,7 @@ Both forms of pointer dereference return a proxy object rather than a real refer
The pointer-like semantics work very well for simple accesses such as assignment and calls to member functions. However, sometimes you need to perform an operation that requires multiple accesses under protection of the same lock, and that's what the synchronize() method provides.
By calling synchronize() you obtain a strict_lock_ptr object that holds a lock on the mutex protecting the data, and which can be used to access the protected data. The lock is held until the strict_lock_ptr object is destroyed, so you can safely perform multi-part operations. The strict_lock_ptr object also acts as a pointer-to-T, just like synchronized_value does, but this time the lock is already held. For example, the following function adds a trailing slash to a path held in a synchronized_value. The use of the strict_lock_ptr object ensures that the string hasn't changed in between the query and the update.
By calling synchronize() you obtain an strict_lock_ptr object that holds a lock on the mutex protecting the data, and which can be used to access the protected data. The lock is held until the strict_lock_ptr object is destroyed, so you can safely perform multi-part operations. The strict_lock_ptr object also acts as a pointer-to-T, just like synchronized_value does, but this time the lock is already held. For example, the following function adds a trailing slash to a path held in a synchronized_value. The use of the strict_lock_ptr object ensures that the string hasn't changed in between the query and the update.
void addTrailingSlashIfMissing(boost::synchronized_value<std::string> & path)
{

View File

@@ -8,7 +8,7 @@
[library Thread
[quickbook 1.5]
[version 4.7.0]
[version 4.5.0]
[authors [Williams, Anthony] [Botet Escriba, Vicente J.]]
[copyright 2007-11 Anthony Williams]
[copyright 2011-15 Vicente J. Botet Escriba]

View File

@@ -21,7 +21,7 @@
{
thread::id get_id() noexcept;
template<typename TimeDuration>
void yield() noexcept;
void yield() noexcept; // DEPRECATED
template <class Clock, class Duration>
void sleep_until(const chrono::time_point<Clock, Duration>& abs_time);
template <class Rep, class Period>
@@ -255,7 +255,7 @@ does not complete when the specified time has elapsed or reached respectively.
[endsect]
[section:destructor1 Destructor V1-2]
[section:destructor1 Destructor V1]
When the __thread__ object that represents a thread of execution is destroyed the thread becomes ['detached]. Once a thread is
detached, it will continue executing until the invocation of the function or callable object supplied on construction has completed,
@@ -264,7 +264,7 @@ object. In this case, the __thread__ object ceases to represent the now-detached
[endsect]
[section:destructor2 Destructor V3-X]
[section:destructor2 Destructor V2]
When the __thread__ object that represents a thread of execution is destroyed the program terminates if the thread is __joinable__.
@@ -280,7 +280,7 @@ You can use a thread_joiner to ensure that the thread has been joined at the thr
{
boost::thread t(my_func);
boost::thread_joiner g(t);
// do something else
// do someting else
} // here the thread_joiner destructor will join the thread before it is destroyed.
[endsect]
@@ -410,7 +410,7 @@ Of course all the synchronization facilities provided by Boost.Thread are also a
The `boost::this_thread` interrupt related functions behave in a degraded mode when called from a thread created using the native interface, i.e. `boost::this_thread::interruption_enabled()` returns false. As consequence the use of `boost::this_thread::disable_interruption` and `boost::this_thread::restore_interruption` will do nothing and calls to `boost::this_thread::interruption_point()` will be just ignored.
As the single way to interrupt a thread is through a __thread__ instance, `interruption_request()` will return false for the native threads.
As the single way to interrupt a thread is through a __thread__ instance, `interruption_request()` wiil returns false for the native threads.
[heading `pthread_exit` POSIX limitation]
@@ -711,7 +711,7 @@ are copied into internal storage for access by the new thread.]]]
[[Throws:] [Nothing.]]
[[Note:] [The reason to moving to std::terminate is that either implicitly detaching or joining a `joinable()` thread in its destructor could result in difficult to debug correctness (for `detach`) or performance (for `join`) bugs encountered only when an exception is raised. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. Join the thread before destroying or use a scoped thread.]]
[[Note:] [The reason to moving to std::terminate is that either implicitly detaching or joining a `joinable()` thread in its destructor could result in difficult to debug correctness (for `detach`) or performance (for `join`) bugs encountered only when an exception is raised. Thus the programmer must ensure that the destructor is never executed while the thread is still joinable. Join the thread before destroying or use an scoped thread.]]
]
@@ -964,7 +964,7 @@ predefined __interruption_points__ with interruption enabled .]]
[section:hardware_concurrency Static member function `hardware_concurrency()`]
unsigned hardware_concurrency() noexcept;
unsigned hardware_concurrency() noexecpt;
[variablelist
@@ -979,7 +979,7 @@ or 0 if this information is not available.]]
[section:physical_concurrency Static member function `physical_concurrency()`]
unsigned physical_concurrency() noexcept;
unsigned physical_concurrency() noexecpt;
[variablelist
@@ -1290,7 +1290,7 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if `
[variablelist
[[Effects:] [Constructs a thread attributes instance with its default values.]]
[[Effects:] [Constructs a thread atrributes instance with its default values.]]
[[Throws:] [Nothing]]
@@ -1304,7 +1304,7 @@ instances of __thread_id__ `a` and `b` is the same if `a==b`, and different if `
[variablelist
[[Effects:] [Stores the stack size to be used to create a thread. This is a hint that the implementation can choose a better size if to small or too big or not aligned to a page.]]
[[Effects:] [Stores the stack size to be used to create a thread. This is an hint that the implementation can choose a better size if to small or too big or not aligned to a page.]]
[[Postconditions:] [`this-> get_stack_size()` returns the chosen stack size.]]
@@ -1532,7 +1532,7 @@ do not throw exceptions. __thread_interrupted__ if the current thread of executi
[variablelist
[[Effects:] [Suspends the current thread until the duration specified
[[Effects:] [Suspends the current thread until the duration specified by
by `rel_time` has elapsed.]]
[[Throws:] [Nothing if operations of chrono::duration<Rep, Period> do not throw exceptions. __thread_interrupted__ if the current thread of execution is interrupted.]]

View File

@@ -18,7 +18,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>
@@ -73,7 +73,7 @@ void submit_some(boost::executor& tp)
}
void at_th_entry(boost::basic_thread_pool& )
void at_th_entry(boost::basic_thread_pool )
{
}
@@ -84,66 +84,118 @@ int test_executor_adaptor()
{
try
{
// {
// std::cout << BOOST_CONTEXTOF << std::endl;
// boost::basic_thread_pool e1;
// std::cout << BOOST_CONTEXTOF << std::endl;
// boost::basic_thread_pool e2 = e1;
// std::cout << BOOST_CONTEXTOF << std::endl;
// }
{
std::cout << BOOST_CONTEXTOF << std::endl;
boost::executor_adaptor < boost::basic_thread_pool > ea(4);
std::cout << BOOST_CONTEXTOF << std::endl;
submit_some( ea);
std::cout << BOOST_CONTEXTOF << std::endl;
}
#if 1
{
std::cout << BOOST_CONTEXTOF << std::endl;
boost::executor_adaptor < boost::basic_thread_pool > ea(4);
// fixme
// ERROR= tr1::bad_weak_ptr
{
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;
}
// {
// 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;
// }
std::cout << BOOST_CONTEXTOF << std::endl;
submit_some(ea);
std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::basic_thread_pool ea3(1);
std::cout << BOOST_CONTEXTOF << std::endl;
boost::future<int> t1 = boost::async(ea3, &f1);
std::cout << BOOST_CONTEXTOF << std::endl;
boost::future<int> t2 = boost::async(ea3, &f1);
std::cout << BOOST_CONTEXTOF << std::endl;
//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;
}
#endif
// std::cout << BOOST_CONTEXTOF << std::endl;
// {
// boost::basic_thread_pool ea3(1);
// std::cout << BOOST_CONTEXTOF << std::endl;
// boost::future<int> t1 = boost::async(ea3, &f1);
// std::cout << BOOST_CONTEXTOF << std::endl;
// boost::future<int> t2 = boost::async(ea3, &f1);
// std::cout << BOOST_CONTEXTOF << std::endl;
// //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;
// }
std::cout << BOOST_CONTEXTOF << std::endl;
submit_some(ea);
std::cout << BOOST_CONTEXTOF << std::endl;
}
#endif
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;
// 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
// 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);
}
#endif
std::cout << BOOST_CONTEXTOF << std::endl;
{
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);
}
std::cout << BOOST_CONTEXTOF << std::endl;
#if 1
#if 0
// fixme
// ERROR= tr1::bad_weak_ptr
{
@@ -156,7 +208,7 @@ int test_executor_adaptor()
{
boost::async(&f1);
}
#if 1
#if 0
// fixme
// ERROR= tr1::bad_weak_ptr

View File

@@ -41,28 +41,10 @@ int main()
for (int i=0; i< number_of_tests; i++)
try
{
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
{
boost::future<int> inner_future = boost::async(boost::launch::async, &p2);
inner_future.wait();
int ii = inner_future.get();
BOOST_THREAD_LOG << "ii= "<< ii << "" << BOOST_THREAD_END_LOG;
}
#endif
{
boost::future<boost::future<int> > outer_future = boost::async(boost::launch::async, &p2);
boost::future<int> inner_future = outer_future.unwrap();
inner_future.wait();
int ii = inner_future.get();
BOOST_THREAD_LOG << "ii= "<< ii << "" << BOOST_THREAD_END_LOG;
}
{
boost::future<boost::future<int> > outer_future = boost::async(boost::launch::async, &p2);
boost::future<int> inner_future = outer_future.unwrap();
int ii = inner_future.get();
BOOST_THREAD_LOG << "ii= "<< ii << "" << BOOST_THREAD_END_LOG;
}
boost::future<boost::future<int> > outer_future = boost::async(boost::launch::async, &p2);
boost::future<int> inner_future = outer_future.unwrap();
int ii = inner_future.get();
BOOST_THREAD_LOG << "ii= "<< ii << "" << BOOST_THREAD_END_LOG;
}
catch (std::exception& ex)
{

View File

@@ -0,0 +1,184 @@
// Copyright (C) 2014 Vicente Botet
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#if ! defined BOOST_NO_CXX11_DECLTYPE
#define BOOST_RESULT_OF_USE_DECLTYPE
#endif
#define BOOST_THREAD_VERSION 4
#define BOOST_THREAD_PROVIDES_EXECUTORS
//#define BOOST_THREAD_USES_LOG
#define BOOST_THREAD_USES_LOG_THREAD_ID
#define BOOST_THREAD_QUEUE_DEPRECATE_OLD
#include <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/loop_executor.hpp>
#include <boost/thread/executors/generic_serial_executor.hpp>
#include <boost/thread/executors/serial_executor.hpp>
#include <boost/thread/executors/inline_executor.hpp>
#include <boost/thread/executors/thread_executor.hpp>
#include <boost/thread/executors/executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/executors/generic_executor.hpp>
#include <boost/thread/executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/assert.hpp>
#include <string>
#include <iostream>
void p1()
{
// std::cout << BOOST_CONTEXTOF << std::endl;
//boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
}
void p2()
{
// std::cout << BOOST_CONTEXTOF << std::endl;
//boost::this_thread::sleep_for(boost::chrono::seconds(10));
}
int f1()
{
// std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(1));
return 1;
}
int f2(int i)
{
// std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(2));
return i + 1;
}
void submit_some(boost::generic_executor tp)
{
for (int i = 0; i < 3; ++i) {
tp.submit(&p2);
}
for (int i = 0; i < 3; ++i) {
tp.submit(&p1);
}
}
template < class Executor>
void submit_some2(Executor& tp)
{
for (int i = 0; i < 3; ++i) {
tp.submit(&p2);
}
for (int i = 0; i < 3; ++i) {
tp.submit(&p1);
}
}
template <class Executor>
void submit_some3(boost::serial_executor<Executor>& tp)
{
for (int i = 0; i < 3; ++i) {
tp.submit(&p2);
}
for (int i = 0; i < 3; ++i) {
tp.submit(&p1);
}
}
void at_th_entry(boost::basic_thread_pool)
{
}
int test_generic_executor()
{
// std::cout << BOOST_CONTEXTOF << std::endl;
{
try
{
{
boost::basic_thread_pool ea(4);
submit_some( ea);
// {
// boost::future<int> t1 = boost::async(ea, &f1);
// boost::future<int> t2 = boost::async(ea, &f1);
// // std::cout << BOOST_CONTEXTOF << " t1= " << t1.get() << std::endl;
// // std::cout << BOOST_CONTEXTOF << " t2= " << t2.get() << std::endl;
// }
submit_some(ea);
// {
// boost::basic_thread_pool ea3(1);
// boost::future<int> t1 = boost::async(ea3, &f1);
// boost::future<int> t2 = boost::async(ea3, &f1);
// //boost::future<int> t2 = boost::async(ea3, f2, 1); // todo this doesn't compiles yet on C++11
// //boost::future<int> t2 = boost::async(ea3, boost::bind(f2, 1)); // todo this doesn't compiles yet on C++98
// // std::cout << BOOST_CONTEXTOF << " t1= " << t1.get() << std::endl;
// // std::cout << BOOST_CONTEXTOF << " t2= " << t2.get() << std::endl;
// }
submit_some(ea);
}
// std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::loop_executor ea2;
submit_some( ea2);
ea2.run_queued_closures();
}
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
// // std::cout << BOOST_CONTEXTOF << std::endl;
// {
// boost::basic_thread_pool ea1(4);
// boost::generic_serial_executor ea2(ea1);
// submit_some(ea2);
// }
// std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::basic_thread_pool ea1(4);
boost::serial_executor<boost::basic_thread_pool> ea2(ea1);
submit_some3(ea2);
}
#endif
// std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::inline_executor ea1;
submit_some(ea1);
}
// std::cout << BOOST_CONTEXTOF << std::endl;
{
//boost::thread_executor ea1;
//submit_some(ea1);
}
// std::cout << BOOST_CONTEXTOF << std::endl;
// {
// boost::basic_thread_pool ea(4, at_th_entry);
// boost::future<int> t1 = boost::async(ea, &f1);
// // std::cout << BOOST_CONTEXTOF << " t1= " << t1.get() << std::endl;
// }
}
catch (std::exception& ex)
{
std::cout << "ERROR= " << ex.what() << "" << std::endl;
return 1;
}
catch (...)
{
std::cout << " ERROR= exception thrown" << std::endl;
return 2;
}
}
// std::cout << BOOST_CONTEXTOF << std::endl;
return 0;
}
int main()
{
return test_generic_executor();
}

View File

@@ -17,11 +17,12 @@
#include <boost/thread/caller_context.hpp>
#include <boost/thread/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)
{
}
@@ -80,22 +81,22 @@ int test_generic_executor_ref()
{
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;
}
// {
// 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;
@@ -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
@@ -122,18 +123,18 @@ int test_generic_executor_ref()
//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;
}
std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::basic_thread_pool ea(4, at_th_entry);
boost::async(ea, &f1);
std::cout << BOOST_CONTEXTOF << std::endl;
}
// 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;
// }
// std::cout << BOOST_CONTEXTOF << std::endl;
// {
// boost::basic_thread_pool ea(4, at_th_entry);
// boost::async(ea, &f1);
// std::cout << BOOST_CONTEXTOF << std::endl;
// }
std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(200));
std::cout << BOOST_CONTEXTOF << std::endl;

View File

@@ -0,0 +1,117 @@
// Copyright (C) 2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
#include <boost/config.hpp>
#if ! defined BOOST_NO_CXX11_DECLTYPE
#define BOOST_RESULT_OF_USE_DECLTYPE
#endif
#define BOOST_THREAD_VERSION 4
#define BOOST_THREAD_PROVIDES_EXECUTORS
//#define BOOST_THREAD_USES_LOG
#define BOOST_THREAD_USES_LOG_THREAD_ID
#define BOOST_THREAD_QUEUE_DEPRECATE_OLD
#include <boost/thread/caller_context.hpp>
#include <boost/thread/executors/basic_thread_pool.hpp>
#include <boost/thread/executors/generic_serial_executor.hpp>
#include <boost/thread/executors/executor.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/thread/executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/assert.hpp>
#include <string>
#include <iostream>
void p1()
{
std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(30));
std::cout << BOOST_CONTEXTOF << std::endl;
}
void p2()
{
std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
std::cout << BOOST_CONTEXTOF << std::endl;
}
int f1()
{
// std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(1));
return 1;
}
int f2(int i)
{
// std::cout << BOOST_CONTEXTOF << std::endl;
boost::this_thread::sleep_for(boost::chrono::seconds(2));
return i + 1;
}
void submit_some(boost::generic_serial_executor& tp)
{
//std::cout << BOOST_CONTEXTOF << std::endl;
for (int i = 0; i < 3; ++i) {
//std::cout << BOOST_CONTEXTOF << std::endl;
tp.submit(&p2);
}
for (int i = 0; i < 3; ++i) {
//std::cout << BOOST_CONTEXTOF << std::endl;
tp.submit(&p1);
}
//std::cout << BOOST_CONTEXTOF << std::endl;
}
void at_th_entry(boost::basic_thread_pool )
{
}
int test_executor_adaptor()
{
std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::basic_thread_pool tp;
boost::generic_serial_executor e1(tp);
boost::generic_serial_executor e2 = e1;
}
std::cout << BOOST_CONTEXTOF << std::endl;
{
try
{
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
// std::cout << BOOST_CONTEXTOF << std::endl;
{
boost::basic_thread_pool ea1(4);
boost::generic_serial_executor ea2(ea1);
submit_some(ea2);
}
#endif
// std::cout << BOOST_CONTEXTOF << std::endl;
}
catch (std::exception& ex)
{
std::cout << "ERROR= " << ex.what() << "" << std::endl;
return 1;
}
catch (...)
{
std::cout << " ERROR= exception thrown" << std::endl;
return 2;
}
}
// std::cout << BOOST_CONTEXTOF << std::endl;
return 0;
}
int main()
{
return test_executor_adaptor();
}

View File

@@ -0,0 +1,124 @@
// 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;
std::cout << BOOST_CONTEXTOF << std::endl;
boost::generic_serial_executor_cont e1(tp);
std::cout << BOOST_CONTEXTOF << std::endl;
boost::generic_serial_executor_cont e2 = e1;
std::cout << BOOST_CONTEXTOF << std::endl;
}
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);
std::cout << BOOST_CONTEXTOF << std::endl;
boost::generic_serial_executor_cont ea2(ea1);
std::cout << BOOST_CONTEXTOF << std::endl;
submit_some(ea2);
std::cout << BOOST_CONTEXTOF << std::endl;
}
#endif
std::cout << BOOST_CONTEXTOF << std::endl;
}
catch (std::exception& ex)
{
std::cout << "ERROR= " << ex.what() << "" << std::endl;
return 1;
}
catch (...)
{
std::cout << " ERROR= exception thrown" << std::endl;
return 2;
}
}
// std::cout << BOOST_CONTEXTOF << std::endl;
return 0;
}
int main()
{
return test_executor_adaptor();
}

View File

@@ -51,8 +51,8 @@ int f2(int i)
boost::this_thread::sleep_for(boost::chrono::seconds(2));
return i + 1;
}
void submit_some(boost::serial_executor& tp)
template <class Executor>
void submit_some(boost::serial_executor<Executor>& tp)
{
for (int i = 0; i < 3; ++i) {
std::cout << BOOST_CONTEXTOF << std::endl;
@@ -80,9 +80,8 @@ int test_executor_adaptor()
#if ! defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
{
boost::basic_thread_pool ea1(4);
boost::serial_executor ea2(ea1);
boost::serial_executor<boost::basic_thread_pool> ea2(ea1);
submit_some(ea2);
boost::this_thread::sleep_for(boost::chrono::seconds(10));
}
#endif
}

View File

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

View File

@@ -57,5 +57,6 @@ namespace boost
// 20.8.2.4, class template enable_shared_from_this:
// 20.8.2.5, shared_ptr atomic access:
// 20.8.2.6 hash support
#include <boost/thread/csbl/memory/shared_ptr.hpp>
#endif // header

View File

@@ -11,17 +11,30 @@
#include <boost/thread/csbl/memory/config.hpp>
#if defined BOOST_NO_CXX11_SMART_PTR
//#if defined BOOST_NO_CXX11_SMART_PTR
#if 1
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/enable_shared_from_this.hpp>
namespace boost
{
template<class T> bool atomic_compare_exchange_strong( shared_ptr<T> * p, shared_ptr<T> * v, shared_ptr<T> w )
{
return atomic_compare_exchange(p,v,w);
}
namespace csbl
{
using ::boost::shared_ptr;
using ::boost::make_shared;
using ::boost::static_pointer_cast;
using ::boost::dynamic_pointer_cast;
using ::boost::const_pointer_cast;
using ::boost::enable_shared_from_this;
using ::boost::atomic_load;
using ::boost::atomic_compare_exchange_strong;
}
}
@@ -35,6 +48,12 @@ namespace boost
{
using std::shared_ptr;
using std::make_shared;
using std::dynamic_pointer_cast;
using std::static_pointer_cast;
using std::const_pointer_cast;
using std::enable_shared_from_this;
using std::atomic_load;
using std::atomic_compare_exchange_strong;
}
}

View File

@@ -72,21 +72,13 @@ namespace boost
BOOST_SYMBOL_VISIBLE
invoker& operator=(BOOST_THREAD_RV_REF(invoker) f)
{
if (this != &f)
{
f_ = boost::move(BOOST_THREAD_RV(f).f_);
}
return *this;
f_ = boost::move(BOOST_THREAD_RV(f).f_);
}
BOOST_SYMBOL_VISIBLE
invoker& operator=( BOOST_THREAD_COPY_ASSIGN_REF(invoker) f)
{
if (this != &f)
{
f_ = f.f_;
}
return *this;
f_ = f.f_;
}
result_type operator()()
@@ -99,7 +91,7 @@ namespace boost
result_type
execute(tuple_indices<Indices...>)
{
return detail::invoke(boost::move(csbl::get<0>(f_)), boost::move(csbl::get<Indices>(f_))...);
return invoke(boost::move(csbl::get<0>(f_)), boost::move(csbl::get<Indices>(f_))...);
}
};
@@ -136,7 +128,7 @@ namespace boost
result_type
execute(tuple_indices<Indices...>)
{
return detail::invoke<R>(boost::move(csbl::get<0>(f_)), boost::move(csbl::get<Indices>(f_))...);
return invoke<R>(boost::move(csbl::get<0>(f_)), boost::move(csbl::get<Indices>(f_))...);
}
};
//BOOST_THREAD_DCL_MOVABLE_BEG(X) invoker<Fp> BOOST_THREAD_DCL_MOVABLE_END
@@ -190,7 +182,7 @@ namespace boost
{} \
\
result_type operator()() { \
return detail::invoke(boost::move(fp_) \
return invoke(boost::move(fp_) \
BOOST_PP_REPEAT(n, BOOST_THREAD_MOVE_DCL, ~) \
); \
} \
@@ -315,7 +307,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -381,7 +373,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -442,7 +434,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -498,7 +490,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -549,7 +541,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -595,7 +587,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -636,7 +628,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
, boost::move(v2_)
@@ -672,7 +664,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
, boost::move(v1_)
);
@@ -703,7 +695,7 @@ namespace boost
result_type operator()()
{
return detail::invoke(boost::move(fp_)
return invoke(boost::move(fp_)
, boost::move(v0_)
);
}

View File

@@ -72,7 +72,7 @@ namespace boost
void run2(tuple_indices<Indices...>)
{
detail::invoke(std::move(std::get<0>(fp)), std::move(std::get<Indices>(fp))...);
invoke(std::move(std::get<0>(fp)), std::move(std::get<Indices>(fp))...);
}
void run()
{
@@ -354,8 +354,6 @@ namespace boost
#if defined BOOST_THREAD_PROVIDES_THREAD_MOVE_ASSIGN_CALLS_TERMINATE_IF_JOINABLE
if (joinable()) std::terminate();
#else
detach();
#endif
thread_info=BOOST_THREAD_RV(other).thread_info;
BOOST_THREAD_RV(other).thread_info.reset();
@@ -840,7 +838,7 @@ namespace boost
void BOOST_THREAD_DECL add_thread_exit_function(thread_exit_function_base*);
struct shared_state_base;
#if defined(BOOST_THREAD_PLATFORM_WIN32)
inline void make_ready_at_thread_exit(shared_ptr<shared_state_base> as)
inline void make_ready_at_thread_exit(csbl::shared_ptr<shared_state_base> as)
{
detail::thread_data_base* const current_thread_data(detail::get_current_thread_data());
if(current_thread_data)
@@ -849,7 +847,7 @@ namespace boost
}
}
#else
void BOOST_THREAD_DECL make_ready_at_thread_exit(shared_ptr<shared_state_base> as);
void BOOST_THREAD_DECL make_ready_at_thread_exit(csbl::shared_ptr<shared_state_base> as);
#endif
}

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013-2014 Vicente J. Botet Escriba
// Copyright (C) 2013-2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -18,6 +18,11 @@
#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,126 +35,267 @@ namespace executors
/// type-erasure to store the works to do
typedef executors::work work;
private:
typedef thread thread_t;
/// A move aware vector type
typedef csbl::vector<thread_t> thread_vector;
/// A move aware vector
thread_vector threads;
/// the thread safe work queue
concurrent::sync_queue<work > work_queue;
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;
if (work_queue.try_pull(task) == queue_op_status::success)
{
task();
return true;
}
task();
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.
*/
void submit(BOOST_THREAD_RV_REF(work) closure) {
work_queue.push(boost::move(closure));
}
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
//work_queue.push(work(closure));
submit(work(closure));
}
#endif
void submit(void (*closure)())
{
//work_queue.push(work(closure));
submit(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
//work_queue.push(work(boost::forward<Closure>(closure)));
work w((boost::forward<Closure>(closure)));
submit(boost::move(w));
}
/**
* \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)
//basic_thread_pool(basic_thread_pool const&)=default;
//basic_thread_pool(basic_thread_pool &&)=default;
/**
* \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. .
@@ -159,61 +305,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.
*
@@ -221,10 +330,16 @@ namespace executors
*/
~basic_thread_pool()
{
// signal to all the worker threads that there will be no more submissions.
close();
// joins all the threads before destroying the thread pool resources (e.g. the queue).
join();
}
/**
* 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();
}
/**
@@ -232,10 +347,7 @@ namespace executors
*/
void join()
{
for (unsigned i = 0; i < threads.size(); ++i)
{
threads[i].join();
}
pimpl->join();
}
/**
@@ -244,7 +356,7 @@ namespace executors
*/
void close()
{
work_queue.close();
pimpl->close();
}
/**
@@ -252,7 +364,7 @@ namespace executors
*/
bool closed()
{
return work_queue.closed();
return pimpl->closed();
}
/**
@@ -266,28 +378,26 @@ namespace executors
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
void submit(BOOST_THREAD_RV_REF(work) closure) {
work_queue.push(boost::move(closure));
}
// void submit(BOOST_THREAD_RV_REF(work) closure) {
// work_queue.push(boost::move(closure));
// }
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
submit(work(closure));
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
submit(work(closure));
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
//submit(work(boost::forward<Closure>(closure)));
work w((boost::forward<Closure>(closure)));
submit(boost::move(w));
pimpl->submit(boost::forward<Closure>(closure));
}
/**
@@ -298,15 +408,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;

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013,2014 Vicente J. Botet Escriba
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// 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() {}
/**
@@ -129,7 +127,6 @@ namespace boost
bool reschedule_until(Pred const& pred)
{
do {
//schedule_one_or_yield();
if ( ! try_executing_one())
{
return false;

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013,2014 Vicente J. Botet Escriba
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -12,6 +12,7 @@
#include <boost/thread/detail/config.hpp>
#include <boost/thread/executors/executor.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/config/abi_prefix.hpp>
@@ -30,21 +31,52 @@ namespace executors
/// type-erasure to store the works to do
typedef executor::work work;
/// executor is not copyable.
BOOST_THREAD_NO_COPYABLE(executor_adaptor)
/**
* executor_adaptor constructor
*/
//executor_adaptor(executor_adaptor const&) = default;
//executor_adaptor(executor_adaptor &&) = default;
BOOST_THREAD_COPYABLE_AND_MOVABLE(executor_adaptor)
executor_adaptor(executor_adaptor const& x) : ex(x.ex) {}
executor_adaptor(BOOST_THREAD_RV_REF(executor_adaptor) x) : ex(boost::move(x.ex)) {}
executor_adaptor& operator=(BOOST_THREAD_COPY_ASSIGN_REF(executor_adaptor) x)
{
if (this != &ex)
{
ex = x.ex;
}
return *this;
}
executor_adaptor& operator=(BOOST_THREAD_RV_REF(executor_adaptor) x)
{
if (this != &ex)
{
ex = boost::move(x.ex);
}
return *this;
}
executor_adaptor(Executor const& ex) : ex(ex) {}
executor_adaptor(BOOST_THREAD_RV_REF(Executor) ex) : ex(boost::move(ex)) {}
executor_adaptor() : ex() {}
#if ! defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES)
template <typename ...Args>
executor_adaptor(BOOST_THREAD_RV_REF(Args) ... args) : ex(boost::forward<Args>(args)...) {}
template <typename Arg, typename ...Args>
executor_adaptor(BOOST_THREAD_FWD_REF(Arg) arg, BOOST_THREAD_FWD_REF(Args) ... args
, typename disable_if_c<
is_same<typename decay<Arg>::type, executor_adaptor>::value
||
is_same<typename decay<Arg>::type, Executor>::value
, int* >::type=0
)
: ex(boost::forward<Arg>(arg), boost::forward<Args>(args)...) {}
#else
/**
* executor_adaptor constructor
*/
executor_adaptor() : ex() {}
template <typename A1>
executor_adaptor(
BOOST_THREAD_FWD_REF(A1) a1
@@ -129,6 +161,9 @@ namespace executors
};
}
using executors::executor_adaptor;
BOOST_THREAD_DCL_MOVABLE_BEG(T) executor_adaptor<T> BOOST_THREAD_DCL_MOVABLE_END
}
#include <boost/config/abi_suffix.hpp>

View File

@@ -0,0 +1,150 @@
// Copyright (C) 2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
#ifndef BOOST_THREAD_EXECUTORS_GENERIC_EXECUTOR_HPP
#define BOOST_THREAD_EXECUTORS_GENERIC_EXECUTOR_HPP
#include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/thread/executors/executor_adaptor.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/type_traits/decay.hpp>
#include <boost/config/abi_prefix.hpp>
namespace boost
{
namespace executors
{
class generic_executor
{
shared_ptr<executor> ex;
public:
/// type-erasure to store the works to do
typedef executors::work work;
//generic_executor(generic_executor const&) = default;
//generic_executor(generic_executor &&) = default;
template<typename Executor>
generic_executor(Executor const& ex
, typename boost::disable_if<is_same<Executor, generic_executor>,
int* >::type = (int*)0
)
//: ex(make_shared<executor_adaptor<Executor> >(ex)) // todo check why this doesn't work with C++03
: ex( new executor_adaptor<Executor>(ex) )
{
}
//generic_executor(generic_executor const& other) noexcept {}
//generic_executor& operator=(generic_executor const& other) noexcept {}
/**
* \par Effects
* Close the \c executor for submissions.
* The worker threads will work until there is no more closures to run.
*/
void close() { ex->close(); }
/**
* \par Returns
* Whether the pool is closed for submissions.
*/
bool closed() { return ex->closed(); }
void submit(BOOST_THREAD_RV_REF(work) closure)
{
ex->submit(boost::forward<work>(closure));
}
/**
* \par Requires
* \c Closure is a model of Callable(void()) and a model of CopyConstructible/MoveConstructible.
*
* \par Effects
* The specified closure will be scheduled for execution at some point in the future.
* If invoked closure throws an exception the thread pool will call std::terminate, as is the case with threads.
*
* \par Synchronization
* Completion of closure on a particular thread happens before destruction of thread's thread local variables.
*
* \par Throws
* \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
work w ((closure));
submit(boost::move(w));
}
#endif
void submit(void (*closure)())
{
work w ((closure));
submit(boost::move(w));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
work w = boost::move(closure);
submit(boost::move(w));
}
// size_t num_pending_closures() const
// {
// return ex->num_pending_closures();
// }
/**
* \par Effects
* Try to execute one task.
*
* \par Returns
* Whether a task has been executed.
*
* \par Throws
* Whatever the current task constructor throws or the task() throws.
*/
bool try_executing_one() { return ex->try_executing_one(); }
/**
* \par Requires
* This must be called from an scheduled task.
*
* \par Effects
* reschedule functions until pred()
*/
template <typename Pred>
bool reschedule_until(Pred const& pred)
{
do {
//schedule_one_or_yield();
if ( ! try_executing_one())
{
return false;
}
} while (! pred());
return true;
}
};
}
using executors::generic_executor;
}
#include <boost/config/abi_suffix.hpp>
#endif

View File

@@ -13,7 +13,8 @@
#include <boost/thread/detail/move.hpp>
#include <boost/thread/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) )
{
}

View File

@@ -0,0 +1,295 @@
// 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 thread thread_t;
//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();
join();
}
/**
* \b Effects: join all the threads.
*/
void join()
{
if (this_thread::get_id() == thr.get_id()) return;
thr.join();
}
/**
* \b Effects: close the \c generic_serial_executor for submissions.
* The loop will work until there is no more closures to run.
*/
void close()
{
work_queue.close();
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
return work_queue.closed();
}
/**
* \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
*
* \b Effects: The specified \c closure will be scheduled for execution at some point in the future.
* If invoked closure throws an exception the \c generic_serial_executor will call \c std::terminate, as is the case with threads.
*
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
*
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
work_queue.push(work(closure));
}
#endif
void submit(void (*closure)())
{
work_queue.push(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
work_queue.push(work(boost::forward<Closure>(closure)));
}
};
public:
// generic_serial_executor(generic_serial_executor const&) = default;
// generic_serial_executor(generic_serial_executor &&) = default;
/**
* \b Effects: creates a thread pool that runs closures using one of its closure-executing methods.
*
* \b Throws: Whatever exception is thrown while initializing the needed resources.
*/
template <class Executor>
generic_serial_executor(Executor const& ex
, typename boost::disable_if<is_same<Executor, generic_serial_executor>,
int* >::type = (int*)0)
: pimpl(make_shared<shared_state>(ex))
{
}
/**
* \b Effects: Destroys the thread pool.
*
* \b Synchronization: The completion of all the closures happen before the completion of the \c generic_serial_executor destructor.
*/
~generic_serial_executor()
{
}
/**
* \par Returns
* The underlying executor wrapped on a generic executor reference.
*/
generic_executor& underlying_executor() BOOST_NOEXCEPT
{
return pimpl->underlying_executor();
}
/**
* \b Returns: always false as a serial executor can not re-enter.
* Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks.
*/
bool try_executing_one()
{
return false;
}
/**
* \b Effects: close the \c generic_serial_executor for submissions.
* The loop will work until there is no more closures to run.
*/
void close()
{
pimpl->close();
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
return pimpl->closed();
}
/**
* \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
*
* \b Effects: The specified \c closure will be scheduled for execution at some point in the future.
* If invoked closure throws an exception the \c generic_serial_executor will call \c std::terminate, as is the case with threads.
*
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
*
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
pimpl->submit(boost::forward<Closure>(closure));
}
/**
* \b Returns: always false as a serial executor can not re-enter.
* Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks.
*/
template <typename Pred>
bool reschedule_until(Pred const& pred)
{
return false;
}
private:
shared_ptr<shared_state> pimpl;
};
}
using executors::generic_serial_executor;
}
#include <boost/config/abi_suffix.hpp>
#endif

View File

@@ -0,0 +1,265 @@
// Copyright (C) 2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// 2013/11 Vicente J. Botet Escriba
// first implementation of a simple serial scheduler.
#ifndef BOOST_THREAD_GENERIC_SERIAL_EXECUTOR_CONT_HPP
#define BOOST_THREAD_GENERIC_SERIAL_EXECUTOR_CONT_HPP
#include <boost/thread/detail/config.hpp>
#include <boost/thread/detail/delete.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/thread/executors/work.hpp>
#include <boost/thread/executors/generic_executor.hpp>
#include <boost/thread/future.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/utility/enable_if.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/type_traits/decay.hpp>
#include <boost/config/abi_prefix.hpp>
namespace boost
{
namespace executors
{
class generic_serial_executor_cont
{
public:
/// type-erasure to store the works to do
typedef executors::work work;
private:
struct shared_state {
typedef executors::work work;
generic_executor ex_;
future<void> fut_; // protected by mtx_
bool closed_; // protected by mtx_
mutex mtx_;
struct continuation {
work task;
template <class X>
struct result {
typedef void type;
};
continuation(BOOST_THREAD_RV_REF(work) tsk)
: task(boost::move(tsk)) {}
void operator()(future<void> f)
{
try {
task();
} catch (...) {
std::terminate();
}
}
};
bool closed(lock_guard<mutex>&) const
{
return closed_;
}
public:
/**
* \par Returns
* The underlying executor wrapped on a generic executor reference.
*/
generic_executor& underlying_executor() BOOST_NOEXCEPT { return ex_; }
/// shared_state is not copyable.
BOOST_THREAD_NO_COPYABLE(shared_state)
/**
* \b Effects: creates a serial executor that runs closures in fifo order using one the associated executor.
*
* \b Throws: Whatever exception is thrown while initializing the needed resources.
*
* \b Notes:
* * The lifetime of the associated executor must outlive the serial executor.
* * The current implementation doesn't support submission from synchronous continuation, that is,
* - the executor must execute the continuation asynchronously or
* - the continuation can not submit to this serial executor.
*/
template <class Executor>
shared_state(Executor const& ex)
: ex_(ex), fut_(make_ready_future()), closed_(false)
{
}
/**
* \b Effects: Destroys the thread pool.
*
* \b Synchronization: The completion of all the closures happen before the completion of the \c generic_serial_executor_cont destructor.
*/
~shared_state()
{
// signal to the worker thread that there will be no more submissions.
close();
}
/**
* \b Effects: close the \c generic_serial_executor_cont for submissions.
* The loop will work until there is no more closures to run.
*/
void close()
{
lock_guard<mutex> lk(mtx_);
closed_ = true;;
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
lock_guard<mutex> lk(mtx_);
return closed(lk);
}
/**
* \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
*
* \b Effects: The specified \c closure will be scheduled for execution after the last submitted closure finish.
* If the invoked closure throws an exception the \c generic_serial_executor_cont will call \c std::terminate, as is the case with threads.
*
* \b Throws: \c sync_queue_is_closed if the executor is closed.
* Whatever exception that can be throw while storing the closure.
*
*/
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
lock_guard<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(closure)));
}
#endif
void submit(void (*closure)())
{
lock_guard<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(closure)));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
lock_guard<mutex> lk(mtx_);
if (closed(lk)) BOOST_THROW_EXCEPTION( sync_queue_is_closed() );
fut_ = fut_.then(ex_, continuation(work(boost::forward<Closure>(closure))));
}
};
public:
/**
* \b Effects: creates a thread pool that runs closures using one of its closure-executing methods.
*
* \b Throws: Whatever exception is thrown while initializing the needed resources.
*/
template <class Executor>
generic_serial_executor_cont(Executor const& ex
, typename boost::disable_if<is_same<Executor, generic_serial_executor_cont>,
int* >::type = (int*)0)
: pimpl(make_shared<shared_state>(ex))
{
}
/**
* \b Effects: Destroys the thread pool.
*
* \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor destructor.
*/
~generic_serial_executor_cont()
{
}
/**
* \par Returns
* The underlying executor wrapped on a generic executor reference.
*/
generic_executor& underlying_executor() BOOST_NOEXCEPT
{
return pimpl->underlying_executor();
}
/**
* \b Returns: always false as a serial executor can not re-enter.
* Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks.
*/
bool try_executing_one()
{
return false;
}
/**
* \b Effects: close the \c serial_executor for submissions.
* The loop will work until there is no more closures to run.
*/
void close()
{
pimpl->close();
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
return pimpl->closed();
}
/**
* \b Requires: \c Closure is a model of \c Callable(void()) and a model of \c CopyConstructible/MoveConstructible.
*
* \b Effects: The specified \c closure will be scheduled for execution at some point in the future.
* If invoked closure throws an exception the \c serial_executor will call \c std::terminate, as is the case with threads.
*
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
*
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
pimpl->submit(boost::forward<Closure>(closure));
}
/**
* \b Returns: always false as a serial executor can not re-enter.
* Remark: A serial executor can not execute one of its pending tasks as the tasks depends on the other tasks.
*/
template <typename Pred>
bool reschedule_until(Pred const& pred)
{
return false;
}
private:
shared_ptr<shared_state> pimpl;
};
}
using executors::generic_serial_executor_cont;
}
#include <boost/config/abi_suffix.hpp>
#endif

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2014 Vicente J. Botet Escriba
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// 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;

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013,2014 Vicente J. Botet Escriba
// Copyright (C) 2013-2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// 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,178 @@ 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.
*/
void submit(BOOST_THREAD_RV_REF(work) closure) {
work_queue.push(boost::move(closure));
}
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
//work_queue.push(work(closure));
submit(work(closure));
}
#endif
void submit(void (*closure)())
{
//work_queue.push(work(closure));
submit(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_RV_REF(Closure) closure)
{
//work_queue.push(work(boost::forward<Closure>(closure)));
work w((boost::forward<Closure>(closure)));
submit(boost::move(w));
}
/**
* \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 +214,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 +249,7 @@ namespace executors
*/
void close()
{
work_queue.close();
pimpl->close();
}
/**
@@ -124,7 +257,7 @@ namespace executors
*/
bool closed()
{
return work_queue.closed();
return pimpl->closed();
}
/**
@@ -138,29 +271,27 @@ namespace executors
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
void submit(BOOST_THREAD_RV_REF(work) closure) {
work_queue.push(boost::move(closure));
}
// void submit(BOOST_THREAD_RV_REF(work) closure) {
// work_queue.push(boost::move(closure));
// }
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
submit(work(closure));
}
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
submit(work(closure));
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
//work_queue.push(work(boost::forward<Closure>(closure)));
work w((boost::forward<Closure>(closure)));
submit(boost::move(w));
pimpl->submit(boost::forward<Closure>(closure));
}
/**
@@ -171,13 +302,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);
}
/**
@@ -185,15 +310,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;

View File

@@ -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;

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2014 Vicente J. Botet Escriba
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// 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;

View File

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

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2013 Vicente J. Botet Escriba
// Copyright (C) 2013,2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -14,116 +14,179 @@
#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;
typedef 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();
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()
{
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.
*/
void submit(BOOST_THREAD_RV_REF(work) closure)
{
work_queue.push(boost::move(closure));
}
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
//work_queue.push(work(closure));
submit(work(closure));
}
#endif
void submit(void (*closure)())
{
//work_queue.push(work(closure));
submit(work(closure));
}
template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
//work_queue.push(work(boost::move(closure)));
work w((boost::forward<Closure>(closure)));
submit(boost::move(w));
}
};
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 +196,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 +222,7 @@ namespace executors
*/
void close()
{
work_queue.close();
pimpl->close();
}
/**
@@ -151,7 +230,7 @@ namespace executors
*/
bool closed()
{
return work_queue.closed();
return pimpl->closed();
}
/**
@@ -165,47 +244,40 @@ namespace executors
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*/
void submit(BOOST_THREAD_RV_REF(work) closure)
{
work_queue.push(boost::move(closure));
}
// void submit(BOOST_THREAD_RV_REF(work) closure)
// {
// work_queue.push(boost::move(closure));
// }
#if defined(BOOST_NO_CXX11_RVALUE_REFERENCES)
template <typename Closure>
void submit(Closure & closure)
{
submit(work(closure));
pimpl->submit(closure);
}
#endif
void submit(void (*closure)())
{
submit(work(closure));
pimpl->submit(closure);
}
template <typename Closure>
void submit(BOOST_THREAD_FWD_REF(Closure) closure)
{
work w((boost::forward<Closure>(closure)));
submit(boost::move(w));
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;

View File

@@ -12,18 +12,20 @@
#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_ref.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:
@@ -31,93 +33,156 @@ namespace executors
typedef executors::work work;
private:
generic_executor_ref ex_;
future<void> fut_; // protected by mtx_
bool closed_; // protected by mtx_
mutex mtx_;
struct shared_state {
typedef executors::work work;
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();
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))));
}
};
bool closed(lock_guard<mutex>&) const
{
return closed_;
}
public:
/**
* \par Returns
* The underlying executor wrapped on a generic executor reference.
*/
generic_executor_ref& underlying_executor() BOOST_NOEXCEPT { return ex_; }
/// serial_executor_cont is not copyable.
BOOST_THREAD_NO_COPYABLE(serial_executor_cont)
/**
* \b Effects: creates a serial executor that runs closures in fifo order using one the associated executor.
* \b Effects: creates a thread pool that runs closures using one of its closure-executing methods.
*
* \b Throws: Whatever exception is thrown while initializing the needed resources.
*
* \b Notes:
* * The lifetime of the associated executor must outlive the serial executor.
* * The current implementation doesn't support submission from synchronous continuation, that is,
* - the executor must execute the continuation asynchronously or
* - the continuation can not submit to this serial executor.
*/
template <class Executor>
serial_executor_cont(Executor& ex)
: ex_(ex), fut_(make_ready_future()), closed_(false)
: 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_cont destructor.
* \b Synchronization: The completion of all the closures happen before the completion of the \c serial_executor destructor.
*/
~serial_executor_cont()
{
// signal to the worker thread that there will be no more submissions.
close();
}
/**
* \b Effects: close the \c serial_executor_cont for submissions.
* The loop will work until there is no more closures to run.
* \par Returns
* The underlying executor wrapped on a generic executor reference.
*/
void close()
Executor& underlying_executor() BOOST_NOEXCEPT
{
lock_guard<mutex> lk(mtx_);
closed_ = true;;
return pimpl->underlying_executor();
}
/**
* \b Returns: whether the pool is closed for submissions.
*/
bool closed()
{
lock_guard<mutex> lk(mtx_);
return closed(lk);
}
/**
* Effects: none.
* Returns: always false.
* Throws: No.
* \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()
@@ -125,41 +190,64 @@ namespace executors
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 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 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 Throws: \c sync_queue_is_closed if the executor is closed.
* \b Synchronization: completion of \c closure on a particular thread happens before destruction of thread's thread local variables.
*
* \b Throws: \c sync_queue_is_closed if the thread pool is closed.
* Whatever exception that can be throw while storing the closure.
*
*/
#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)));
pimpl->submit(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)));
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() );
fut_ = fut_.then(ex_, continuation(work(boost::forward<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;

View File

@@ -1,4 +1,4 @@
// Copyright (C) 2014 Vicente J. Botet Escriba
// Copyright (C) 2014-2015 Vicente J. Botet Escriba
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -17,7 +17,9 @@
#include <boost/thread/thread_only.hpp>
#include <boost/thread/scoped_thread.hpp>
#include <boost/thread/csbl/vector.hpp>
#include <boost/thread/concurrent_queues/queue_op_status.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/make_shared.hpp>
#include <boost/config/abi_prefix.hpp>
@@ -30,33 +32,148 @@ 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 thread thread_t;
typedef csbl::vector<thread_t> threads_type;
threads_type threads_;
mutable mutex mtx_;
/// thread_executor::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: 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
join();
}
/**
* \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();
}
}
/**
* 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>())
{
}
/**
@@ -66,9 +183,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();
}
/**
@@ -77,21 +201,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();
}
/**
@@ -110,30 +228,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));
}
/**
@@ -142,12 +248,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;
}

View File

@@ -116,9 +116,9 @@ 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()
{
@@ -153,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
@@ -188,7 +187,7 @@ protected:
bool canceled;
#endif
#if defined BOOST_THREAD_PROVIDES_EXECUTORS
Executor* ex;
Executor ex;
#endif
exception_list exs;
typedef csbl::vector<future<void> > group_type;
@@ -211,13 +210,13 @@ protected:
}
}
#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
@@ -245,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&))
@@ -265,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
@@ -280,7 +280,7 @@ protected:
}
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));
}

File diff suppressed because it is too large Load Diff

View File

@@ -22,8 +22,6 @@ namespace boost
#ifdef BOOST_THREAD_PROVIDES_EXECUTORS
executor = 4,
#endif
inherit = 8,
sync = 16,
any = async | deferred
}
BOOST_SCOPED_ENUM_DECLARE_END(launch)

View File

@@ -68,14 +68,17 @@ namespace boost
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
pthread_mutex_t* the_mutex = &internal_mutex;
guard.activate(m);
do {
res = pthread_cond_wait(&cond,&internal_mutex);
} while (res == EINTR);
#else
//boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
pthread_mutex_t* the_mutex = m.mutex()->native_handle();
#endif
do {
res = pthread_cond_wait(&cond,the_mutex);
} while (res == EINTR);
#endif
}
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
this_thread::interruption_point();
@@ -96,17 +99,18 @@ namespace boost
boost::throw_exception(condition_error(EPERM, "boost::condition_variable::do_wait_until() failed precondition mutex not owned"));
}
#endif
thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
int cond_res;
{
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
thread_cv_detail::lock_on_exit<unique_lock<mutex> > guard;
detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
pthread_mutex_t* the_mutex = &internal_mutex;
guard.activate(m);
cond_res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout);
#else
//boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
pthread_mutex_t* the_mutex = m.mutex()->native_handle();
#endif
cond_res=pthread_cond_timedwait(&cond,the_mutex,&timeout);
#endif
}
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
this_thread::interruption_point();
@@ -174,7 +178,7 @@ namespace boost
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
#else
boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
#endif
guard.activate(m);
res=pthread_cond_wait(&cond,&internal_mutex);
@@ -336,15 +340,14 @@ namespace boost
cv_status::timeout;
}
template <class lock_type>
inline cv_status wait_until(
lock_type& lock,
unique_lock<mutex>& lk,
chrono::time_point<chrono::steady_clock, chrono::nanoseconds> tp)
{
using namespace chrono;
nanoseconds d = tp.time_since_epoch();
timespec ts = boost::detail::to_timespec(d);
if (do_wait_until(lock, ts)) return cv_status::no_timeout;
if (do_wait_until(lk, ts)) return cv_status::no_timeout;
else return cv_status::timeout;
}
@@ -392,7 +395,7 @@ namespace boost
private: // used by boost::thread::try_join_until
template <class lock_type>
bool do_wait_until(
inline bool do_wait_until(
lock_type& m,
struct timespec const &timeout)
{
@@ -402,7 +405,7 @@ namespace boost
#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
detail::interruption_checker check_for_interruption(&internal_mutex,&cond);
#else
boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
boost::pthread::pthread_mutex_scoped_lock check_for_interruption(&internal_mutex);
#endif
guard.activate(m);
res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout);
@@ -420,6 +423,8 @@ namespace boost
}
return true;
}
};
}

View File

@@ -13,7 +13,8 @@
#include <boost/thread/mutex.hpp>
#include <boost/thread/pthread/condition_variable_fwd.hpp>
#include <boost/shared_ptr.hpp>
//#include <boost/shared_ptr.hpp>
#include <boost/thread/csbl/memory/shared_ptr.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/assert.hpp>
#ifdef BOOST_THREAD_USES_CHRONO
@@ -127,7 +128,7 @@ namespace boost
> notify_list_t;
notify_list_t notify;
typedef std::vector<shared_ptr<shared_state_base> > async_states_t;
typedef std::vector<csbl::shared_ptr<shared_state_base> > async_states_t;
async_states_t async_states_;
//#if defined BOOST_THREAD_PROVIDES_INTERRUPTIONS
@@ -162,7 +163,7 @@ namespace boost
notify.push_back(std::pair<condition_variable*, mutex*>(cv, m));
}
void make_ready_at_thread_exit(shared_ptr<shared_state_base> as)
void make_ready_at_thread_exit(csbl::shared_ptr<shared_state_base> as)
{
async_states_.push_back(as);
}

View File

@@ -197,9 +197,6 @@ namespace boost
*/
scoped_thread& operator=(BOOST_RV_REF(scoped_thread) x)
{
CallableThread on_destructor;
on_destructor(t_);
t_ = boost::move(BOOST_THREAD_RV(x).t_);
return *this;
}

View File

@@ -91,19 +91,7 @@ namespace boost
cv.wait_until(lk, t);
}
#if defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC && defined BOOST_CHRONO_HAS_CLOCK_STEADY
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& d)
{
using namespace chrono;
if (d > duration<Rep, Period>::zero())
{
steady_clock::time_point c_timeout = steady_clock::now() + ceil<nanoseconds>(d);
sleep_until(c_timeout);
}
}
#elif defined BOOST_THREAD_SLEEP_FOR_IS_STEADY
#if defined BOOST_THREAD_SLEEP_FOR_IS_STEADY && ! defined BOOST_THREAD_HAS_CONDATTR_SET_CLOCK_MONOTONIC
template <class Rep, class Period>
void sleep_for(const chrono::duration<Rep, Period>& d)
@@ -139,8 +127,7 @@ namespace boost
using namespace chrono;
if (d > duration<Rep, Period>::zero())
{
//system_clock::time_point c_timeout = time_point_cast<system_clock::duration>(system_clock::now() + ceil<nanoseconds>(d));
system_clock::time_point c_timeout = system_clock::now() + ceil<system_clock::duration>(d);
steady_clock::time_point c_timeout = steady_clock::now() + ceil<nanoseconds>(d);
sleep_until(c_timeout);
}
}

View File

@@ -156,7 +156,7 @@ namespace boost
{
struct _SECURITY_ATTRIBUTES;
# ifdef BOOST_NO_ANSI_APIS
# if defined(BOOST_USE_WINAPI_VERSION) && ( BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA )
# if BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA
__declspec(dllimport) void* __stdcall CreateMutexW(_SECURITY_ATTRIBUTES*,int,wchar_t const*);
__declspec(dllimport) void* __stdcall CreateSemaphoreW(_SECURITY_ATTRIBUTES*,long,long,wchar_t const*);
__declspec(dllimport) void* __stdcall CreateEventW(_SECURITY_ATTRIBUTES*,int,int,wchar_t const*);
@@ -339,7 +339,7 @@ namespace boost
{
#if !defined(BOOST_NO_ANSI_APIS)
handle const res = win32::CreateEventA(0, type, state, mutex_name);
#elif defined(BOOST_USE_WINAPI_VERSION) && ( BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA )
#elif BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA
handle const res = win32::CreateEventW(0, type, state, mutex_name);
#else
handle const res = win32::CreateEventExW(
@@ -366,7 +366,7 @@ namespace boost
#if !defined(BOOST_NO_ANSI_APIS)
handle const res=win32::CreateSemaphoreA(0,initial_count,max_count,0);
#else
#if defined(BOOST_USE_WINAPI_VERSION) && ( BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA )
#if BOOST_USE_WINAPI_VERSION < BOOST_WINAPI_VERSION_VISTA
handle const res=win32::CreateSemaphoreEx(0,initial_count,max_count,0,0);
#else
handle const res=win32::CreateSemaphoreExW(0,initial_count,max_count,0,0,semaphore_all_access);

View File

@@ -42,7 +42,6 @@ namespace boost
}
}
#if defined BOOST_THREAD_PATCH
const pthread_once_t pthread_once_init_value=PTHREAD_ONCE_INIT;
struct BOOST_THREAD_DECL delete_epoch_tss_key_on_dlclose_t
{
@@ -53,15 +52,11 @@ namespace boost
{
if(memcmp(&epoch_tss_key_flag, &pthread_once_init_value, sizeof(pthread_once_t)))
{
void* data = (void*)pthread_getspecific(epoch_tss_key);
if (data)
delete_epoch_tss_data(data);
pthread_key_delete(epoch_tss_key);
}
}
};
delete_epoch_tss_key_on_dlclose_t delete_epoch_tss_key_on_dlclose;
#endif
}
uintmax_atomic_t& get_once_per_thread_epoch()

View File

@@ -80,8 +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();
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)
{
@@ -110,12 +110,14 @@ namespace boost
thread_info->tss_data.erase(current);
}
}
thread_info->self.reset();
if (thread_info) // fixme: should we test this?
{
thread_info->self.reset();
}
}
}
}
#if defined BOOST_THREAD_PATCH
struct delete_current_thread_tls_key_on_dlclose_t
{
delete_current_thread_tls_key_on_dlclose_t()
@@ -126,15 +128,12 @@ namespace boost
const boost::once_flag uninitialized = BOOST_ONCE_INIT;
if (memcmp(&current_thread_tls_init_flag, &uninitialized, sizeof(boost::once_flag)))
{
void* data = pthread_getspecific(current_thread_tls_key);
if (data)
tls_destructor(data);
pthread_key_delete(current_thread_tls_key);
}
}
};
delete_current_thread_tls_key_on_dlclose_t delete_current_thread_tls_key_on_dlclose;
#endif
void create_current_thread_tls_key()
{
BOOST_VERIFY(!pthread_key_create(&current_thread_tls_key,&tls_destructor));
@@ -831,7 +830,7 @@ namespace boost
}
namespace detail {
void BOOST_THREAD_DECL make_ready_at_thread_exit(shared_ptr<shared_state_base> as)
void BOOST_THREAD_DECL make_ready_at_thread_exit(csbl::shared_ptr<shared_state_base> as)
{
detail::thread_data_base* const current_thread_data(detail::get_current_thread_data());
if(current_thread_data)

View File

@@ -24,7 +24,6 @@
#include <boost/cstdint.hpp>
#if defined BOOST_THREAD_USES_DATETIME
#include <boost/date_time/posix_time/conversion.hpp>
#include <boost/thread/thread_time.hpp>
#endif
#include <boost/thread/csbl/memory/unique_ptr.hpp>
#include <memory>
@@ -468,7 +467,7 @@ namespace boost
#if defined BOOST_THREAD_USES_DATETIME
bool thread::timed_join(boost::system_time const& wait_until)
{
return do_try_join_until(boost::detail::get_milliseconds_until(wait_until));
return do_try_join_until(get_milliseconds_until(wait_until));
}
#endif
bool thread::do_try_join_until_noexcept(uintmax_t milli, bool& res)
@@ -530,7 +529,7 @@ namespace boost
{
// a bit too strict: Windows XP with SP3 would be sufficient
#if BOOST_PLAT_WINDOWS_RUNTIME \
|| ( defined(BOOST_USE_WINAPI_VERSION) && ( BOOST_USE_WINAPI_VERSION <= BOOST_WINAPI_VERSION_WINXP ) ) \
|| ( BOOST_USE_WINAPI_VERSION <= BOOST_WINAPI_VERSION_WINXP ) \
|| ( ( defined(__MINGW32__) && !defined(__MINGW64__) ) && _WIN32_WINNT < 0x0600)
return 0;
#else

View File

@@ -31,8 +31,7 @@ project
<toolset>gcc:<cxxflags>-Wno-long-long
#<toolset>gcc:<cxxflags>-ansi
#<toolset>gcc:<cxxflags>-fpermissive
<toolset>gcc-4:<cxxflags>-Wno-variadic-macros
<toolset>gcc-5:<cxxflags>-Wno-variadic-macros
<toolset>gcc:<cxxflags>-Wno-variadic-macros
#<toolset>gcc:<cxxflags>-Wunused-local-typedefs
<toolset>gcc:<cxxflags>-Wunused-function
<toolset>gcc:<cxxflags>-Wno-unused-parameter
@@ -811,7 +810,10 @@ 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 ]
@@ -960,11 +962,12 @@ rule thread-compile ( sources : reqs * : name )
test-suite ts_
:
#[ thread-run test_11256.cpp ]
#[ thread-run2 ../example/generic_serial_executor_cont.cpp : ex_generic_serial_executor_cont2 ]
#[ thread-run2 ../example/serial_executor_cont.cpp : ex_serial_executor_cont2 ]
#[ thread-run test_11256.cpp ]
#[ thread-run test_11499.cpp ]
#[ thread-run test_11611.cpp ]
#[ thread-run test_11818.cpp ]
[ thread-run test_11796.cpp ]
#[ thread-run test_11633.cpp ]
;

View File

@@ -38,27 +38,6 @@ struct TestCallback
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
//boost::future<void> ff = future.get();
return boost::make_ready_future();
}
result_type operator()(boost::shared_future<void> future) const
{
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::shared_future<boost::future<void> > future) const
{
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();
}
};
@@ -151,12 +130,10 @@ int main()
BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait();
}
#if 1
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
// fixme
for (int i=0; i< number_of_tests; i++)
{
boost::basic_thread_pool executor(2);
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);
@@ -169,7 +146,6 @@ int main()
BOOST_STATIC_ASSERT(std::is_same<decltype(f3), boost::future<boost::future<void> > >::value);
f3.wait();
}
#endif
std::cout << __FILE__ << "[" << __LINE__ << "]" << std::endl;
for (int i=0; i< number_of_tests; i++)
{

View File

@@ -29,10 +29,10 @@ int main()
});
{
//boost::serial_executor_cont serial(ex);
boost::serial_executor serial(ex);
//boost::serial_executor_cont<boost::loop_executor> serial(ex);
boost::serial_executor<boost::loop_executor> serial(ex);
for (size_t i = 0; i < 100000; i++)
for (size_t i = 0; i < 1000000; i++)
serial.submit([i] {
//std::cout << i << ".";
});

View File

@@ -1,33 +0,0 @@
// 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)
#include <boost/thread.hpp>
#include <boost/chrono.hpp>
#include <iostream>
boost::thread th;
int main()
{
for (auto ti = 0; ti < 1000; ti++)
{
th = boost::thread([ti]()
{
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
std::cout << ti << std::endl;
});
}
std::string st;
std::cin >> st;
// for (int i = 0; i < 10; ++i) {
// std::cout << "." << i << std::endl;
// boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
// }
th.join();
return 0;
}

View File

@@ -1,64 +0,0 @@
// 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)
#define BOOST_THREAD_VERSION 4
#include <boost/config.hpp>
#if ! defined BOOST_NO_CXX11_DECLTYPE
#define BOOST_RESULT_OF_USE_DECLTYPE
#endif
#include <boost/thread/future.hpp>
#include <boost/thread/thread.hpp>
#include <thread>
int main()
{
{
boost::promise<int> promise;
boost::future<int> future = promise.get_future();
boost::future<int> result =
future.then
(
boost::launch::deferred,
[](boost::future<int> && f)
{
std::cout << std::this_thread::get_id() << ": callback" << std::endl;
std::cout << "The value is: " << f.get() << std::endl;
return f.get();
}
);
// We could not reach here.
std::cout << std::this_thread::get_id() << ": function" << std::endl;
promise.set_value(0);
}
{
boost::promise<int> promise;
boost::shared_future<int> future = promise.get_future().share();
boost::future<int> result =
future.then
(
boost::launch::deferred,
[](boost::shared_future<int> && f)
{
std::cout << std::this_thread::get_id() << ": callback" << std::endl;
std::cout << "The value is: " << f.get() << std::endl;
return f.get();
}
);
// We could not reach here.
std::cout << std::this_thread::get_id() << ": function" << std::endl;
promise.set_value(0);
}
return 0;
}

View File

@@ -5,13 +5,12 @@
#define BOOST_THREAD_VERSION 2
#define BOOST_THREAD_PROVIDES_INTERRUPTIONS
//#define BOOST_TEST_MODULE Boost.Threads: 2309
//#include <boost/test/unit_test.hpp>
#define BOOST_TEST_MODULE Boost.Threads: 2309
#include <boost/test/unit_test.hpp>
#include <iostream>
#include <boost/thread.hpp>
#include <boost/detail/lightweight_test.hpp>
using namespace std;
@@ -41,7 +40,7 @@
}
}
void ticket_2309_test()
BOOST_AUTO_TEST_CASE(test)
{
try
{
@@ -58,13 +57,9 @@
}
catch (...)
{
BOOST_TEST(false && "exception raised");
BOOST_CHECK(false && "exception raised");
}
}
int main()
{
ticket_2309_test();
}

View File

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

View File

@@ -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();
}

View File

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

View File

@@ -33,7 +33,7 @@ inline void* operator new(std::size_t s)
void* operator new(std::size_t s) throw (std::bad_alloc)
#endif
{
//std::cout << __FILE__ << ":" << __LINE__ << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
if (throw_one == 0) throw std::bad_alloc();
--throw_one;
return std::malloc(s);
@@ -45,7 +45,7 @@ inline void operator delete(void* p)
inline void operator delete(void* p) throw ()
#endif
{
//std::cout << __FILE__ << ":" << __LINE__ << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
std::free(p);
}

View File

@@ -33,7 +33,7 @@ void* operator new(std::size_t s)
void* operator new(std::size_t s) throw (std::bad_alloc)
#endif
{
//std::cout << __FILE__ << ":" << __LINE__ << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
if (throw_one == 0) throw std::bad_alloc();
--throw_one;
return std::malloc(s);
@@ -45,7 +45,7 @@ void operator delete(void* p)
void operator delete(void* p) throw ()
#endif
{
//std::cout << __FILE__ << ":" << __LINE__ << std::endl;
std::cout << __FILE__ << ":" << __LINE__ << std::endl;
std::free(p);
}