mirror of
https://github.com/boostorg/thread.git
synced 2026-02-03 09:42:16 +00:00
Compare commits
24 Commits
boost-1.29
...
svn-tags/t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
57ed9fd6e7 | ||
|
|
125193dcfa | ||
|
|
77efa9810d | ||
|
|
7f03d1917b | ||
|
|
55b4ca9350 | ||
|
|
7196a0f9d2 | ||
|
|
2caabde5ca | ||
|
|
137d7663c1 | ||
|
|
508b71a921 | ||
|
|
5d90820005 | ||
|
|
84727e90b1 | ||
|
|
9a1e3d3320 | ||
|
|
d33e0c8ee1 | ||
|
|
3332649480 | ||
|
|
c918b66199 | ||
|
|
dbbf56e17a | ||
|
|
c77500c15a | ||
|
|
75084aaa96 | ||
|
|
35714c8f1c | ||
|
|
3699cc97a6 | ||
|
|
5a7377acda | ||
|
|
6aaee629b5 | ||
|
|
b465fe569c | ||
|
|
5e6f72a688 |
@@ -1,17 +1,11 @@
|
||||
# (C) Copyright William E. Kempf 2001. Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears
|
||||
# in all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
# (C) Copyright William E. Kempf 2001. Permission to copy, use, modify,
|
||||
# sell and distribute this software is granted provided this copyright
|
||||
# notice appears in all copies. This software is provided "as is" without
|
||||
# express or implied warranty, and with no claim as to its suitability for
|
||||
# any purpose.
|
||||
#
|
||||
# Boost.Threads build Jamfile
|
||||
#
|
||||
# Declares the following targets:
|
||||
# 1. libboost_thread, a static link library.
|
||||
# 1a. On Win32 (when PTW32 is not defined), a dynamic link library
|
||||
# boost_threadmon, which must be used in conjunction with
|
||||
# libboost_thread. Note that this DLL *must* be used through static
|
||||
# linking to the import library. Dynamic loading will cause undefined
|
||||
# behavior.
|
||||
# Additional configuration variables used:
|
||||
# 1. PTW32 may be used on Win32 platforms to specify that the pthreads-win32
|
||||
# library should be used instead of "native" threads. This feature is
|
||||
@@ -31,37 +25,29 @@ subproject libs/thread/build ;
|
||||
SEARCH on <module@>threads.jam = $(SUBDIR) ;
|
||||
include <module@>threads.jam ;
|
||||
|
||||
template thread_libs
|
||||
## sources ##
|
||||
: <template>thread_base
|
||||
## requirements ##
|
||||
:
|
||||
## default build ##
|
||||
: debug release <runtime-link>static/dynamic
|
||||
;
|
||||
|
||||
#######################
|
||||
# Conditionally declare the Boost.Threads dynamic link library boost_threadmon.
|
||||
|
||||
if $(NT) && ! $(PTW32)
|
||||
{
|
||||
dll boost_threadmon : <template>thread_libs ../src/threadmon.cpp ;
|
||||
template thread_libs
|
||||
## sources ##
|
||||
: <template>thread_base
|
||||
## requirements ##
|
||||
:
|
||||
## default build ##
|
||||
: debug release
|
||||
;
|
||||
|
||||
# Base names of the source files for libboost_thread.
|
||||
CPP_SOURCES = condition mutex recursive_mutex thread tss xtime once
|
||||
exceptions threadmon ;
|
||||
|
||||
dll boost_thread
|
||||
: <template>thread_libs ../src/$(CPP_SOURCES).cpp
|
||||
: <define>BOOST_THREAD_BUILD_DLL=1
|
||||
;
|
||||
|
||||
stage bin-stage
|
||||
: <dll>boost_thread
|
||||
: #<tag><runtime-link-static>"s"
|
||||
<tag><debug>"d"
|
||||
: debug release
|
||||
;
|
||||
}
|
||||
|
||||
#######################
|
||||
# Declare the Boost.Threads static link library libboost_thread.
|
||||
|
||||
# Base names of the source files for libboost_thread.
|
||||
CPP_SOURCES = condition mutex recursive_mutex thread tss xtime once exceptions ;
|
||||
|
||||
lib boost_thread : <template>thread_libs ../src/$(CPP_SOURCES).cpp ;
|
||||
|
||||
#######################
|
||||
# Stage the generated targets.
|
||||
|
||||
#stage bin-stage
|
||||
# : <lib>boost_thread $(threadmon)
|
||||
# : <tag><runtime-link-static>"s"
|
||||
# <tag><debug>"d"
|
||||
# : debug release <runtime-link>static/dynamic
|
||||
#;
|
||||
|
||||
12
build/Jamfile.v2
Normal file
12
build/Jamfile.v2
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
# Declare the uses system library
|
||||
lib pthread : : <name>pthread ;
|
||||
|
||||
project boost/thread
|
||||
: source-location ../src
|
||||
: usage-requirements <library>pthread
|
||||
;
|
||||
|
||||
CPP_SOURCES = condition mutex recursive_mutex thread tss xtime once exceptions ;
|
||||
|
||||
lib boost_thread : $(CPP_SOURCES).cpp ;
|
||||
@@ -1,32 +1,28 @@
|
||||
# Do some OS-specific setup
|
||||
|
||||
threadmon = ;
|
||||
pthreads-win32 = ;
|
||||
|
||||
if $(NT)
|
||||
{
|
||||
if $(PTW32)
|
||||
{
|
||||
local install-path = $(PTW32[1]) ;
|
||||
local lib = $(PTW32[2]) ;
|
||||
pthreads-win32 =
|
||||
<define>BOOST_HAS_PTHREADS
|
||||
<define>PtW32NoCatchWarn
|
||||
<include>$(install-path)/pre-built/include
|
||||
<library-file>$(install-path)/pre-built/lib/$(lib)
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
threadmon = <dll>../build/boost_threadmon ;
|
||||
}
|
||||
}
|
||||
pthreads-win32 = ;
|
||||
|
||||
template thread_base
|
||||
## sources ##
|
||||
:
|
||||
## requirements ##
|
||||
: <sysinclude>$(BOOST_ROOT) <threading>multi $(pthreads-win32)
|
||||
## default build ##
|
||||
:
|
||||
;
|
||||
if $(NT)
|
||||
{
|
||||
if $(PTW32)
|
||||
{
|
||||
local install-path = $(PTW32[1]) ;
|
||||
local lib = $(PTW32[2]) ;
|
||||
pthreads-win32 =
|
||||
<define>BOOST_HAS_PTHREADS
|
||||
<define>PtW32NoCatchWarn
|
||||
<include>$(install-path)/pre-built/include
|
||||
<library-file>$(install-path)/pre-built/lib/$(lib)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
template thread_base
|
||||
## sources ##
|
||||
:
|
||||
## requirements ##
|
||||
: <sysinclude>$(BOOST_ROOT) <threading>multi $(pthreads-win32)
|
||||
## default build ##
|
||||
:
|
||||
;
|
||||
}
|
||||
@@ -61,10 +61,9 @@
|
||||
states <a href="definitions.html#state"> blocked</a> and <a href="definitions.html#state">ready</a>.
|
||||
Note that "waiting" is a synonym for blocked.</p>
|
||||
<h4><a name="class-condition-synopsis"></a>Class <code>condition</code> synopsis</h4>
|
||||
<pre>
|
||||
namespace boost
|
||||
<pre>namespace boost
|
||||
{
|
||||
class condition : private <a href="../../utility/utility.htm#Class noncopyable">boost::noncopyable</a> // Exposition only.
|
||||
class condition : private <a href="../../utility/utility.htm#Class_noncopyable">boost::noncopyable</a> // Exposition only.
|
||||
// Class condition meets the <a href="overview.html#non-copyable">NonCopyable</a> requirement.
|
||||
{
|
||||
public:
|
||||
@@ -86,22 +85,19 @@ namespace boost
|
||||
</pre>
|
||||
<h4><a name="class-condition-ctors"></a>Class <code>condition</code> constructors
|
||||
and destructor</h4>
|
||||
<pre>
|
||||
condition();
|
||||
<pre>condition();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Constructs a <code>condition</code> object.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
~condition();
|
||||
<pre>~condition();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Destroys <code>*this</code>.</dt>
|
||||
</dl>
|
||||
<h4><a name="class-condition-modifiers"></a>Class <code>condition</code> modifier
|
||||
functions</h4>
|
||||
<pre>
|
||||
void notify_one();
|
||||
<pre>void notify_one();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> If there is a thread waiting on <code>*this</code>, change
|
||||
@@ -111,16 +107,14 @@ void notify_one();
|
||||
thread must still acquire the mutex again (which occurs within the call to
|
||||
one of the <code>condition</code> object's wait functions).</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
void notify_all();
|
||||
<pre>void notify_all();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Change the state of all threads waiting on <code> *this</code>
|
||||
to ready. If there are no waiting threads, <code> notify_all()</code> has
|
||||
no effect.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
template <typename ScopedLock>
|
||||
<pre>template <typename ScopedLock>
|
||||
void wait(ScopedLock& lock);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
@@ -138,8 +132,7 @@ template <typename ScopedLock>
|
||||
wake ups". The second version encapsulates this loop idiom internally
|
||||
and is generally the preferred method.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
Template<typename ScopedLock, typename Pr>
|
||||
<pre>Template<typename ScopedLock, typename Pr>
|
||||
void wait(ScopedLock& lock, Pr pred);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
@@ -149,8 +142,7 @@ Template<typename ScopedLock, typename Pr>
|
||||
<dt><b>Throws:</b> <code><a href="exceptions.html#class-lock_error">lock_error</a></code>
|
||||
if <code>!lock.locked()</code></dt>
|
||||
</dl>
|
||||
<pre>
|
||||
template <typename ScopedLock>
|
||||
<pre>template <typename ScopedLock>
|
||||
bool timed_wait(ScopedLock& lock, const <a href="xtime.html">xtime</a>& XT);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
@@ -171,16 +163,14 @@ template <typename ScopedLock>
|
||||
ups". The second version encapsulates this loop idiom internally and
|
||||
is generally the preferred method.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
Template<typename ScopedLock, typename Pr>
|
||||
<pre>Template<typename ScopedLock, typename Pr>
|
||||
bool timed_wait(ScopedLock& lock, const <a href="xtime.html">xtime</a>& XT, Pr pred);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Requires:</b> <code>ScopedLock</code> meets the <a href="lock_concept.html#ScopedLock">ScopedLock</a>
|
||||
requirements, return from <code>pred()</code> convertible to bool.</dt>
|
||||
<dt><b>Effects:</b> As if:<br>
|
||||
<pre>
|
||||
while (!pred())
|
||||
<pre>while (!pred())
|
||||
{
|
||||
if (!timed_wait(lock, XT))
|
||||
return false;
|
||||
@@ -196,8 +186,7 @@ return true;
|
||||
<h2><a name="examples"></a>Example(s)</h2>
|
||||
<p><a href="../example/condition.cpp">libs/thread/example/condition.cpp</a></p>
|
||||
<p>Typical output (dependent on scheduling policies) is:</p>
|
||||
<pre>
|
||||
sent: 0
|
||||
<pre>sent: 0
|
||||
sent: 1
|
||||
received: 0
|
||||
received: 1
|
||||
@@ -209,9 +198,7 @@ sent: 4
|
||||
received: 4
|
||||
</pre>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->09 January, 2003<!--webbot bot="Timestamp" endspan i-checksum="38582" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
@@ -222,4 +209,4 @@ received: 4
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -241,18 +241,17 @@
|
||||
there is no matching exception handler in a program [15.3/9]. That is, terminate()
|
||||
is called, and it is implementation defined whether or not the stack is unwound.</p>
|
||||
<h2><a name="acknowledgements"></a>Acknowledgments</h2>
|
||||
<p>This document has been much improved by the incorporation of comments from
|
||||
William Kempf.</p>
|
||||
<p>This document was originally written by Beman Dawes, and then much improved by the incorporation of comments from
|
||||
William Kempf, who now maintains the contents.</p>
|
||||
<p>The visibility rules are based on <a href=
|
||||
"bibliography.html#Butenhof-97">[Butenhof 97]</a>.</p>
|
||||
<hr>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->06 October, 2002<!--webbot bot="Timestamp" endspan i-checksum="38429" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
All Rights Reserved.<br>
|
||||
</i>© Copyright Beman Dawes, 2001</p>
|
||||
<p>Permission to use, copy, modify, distribute and sell this software and its
|
||||
documentation for any purpose is hereby granted without fee, provided that the
|
||||
above copyright notice appear in all copies and that both that copyright notice
|
||||
@@ -260,4 +259,4 @@
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
13
doc/faq.html
13
doc/faq.html
@@ -86,8 +86,7 @@
|
||||
"definitions.html#Thread-safe">thread-safe</a>. The following is a simple
|
||||
example of a class with copyable semantics and internal synchronization through
|
||||
a mutex member.</p>
|
||||
<pre>
|
||||
class counter
|
||||
<pre>class counter
|
||||
{
|
||||
public:
|
||||
// Doesn't need synchronization since there can be no references to *this
|
||||
@@ -145,8 +144,8 @@ private:
|
||||
a mutex member as mutable clearly documents the intended semantics.</p>
|
||||
<h2><a name="question7"></a>7. Why supply <a href="condition.html">condition variables</a>
|
||||
rather than <a href="rationale.html#Events">event variables</a>?</h2>
|
||||
<p>Condition variables result in user code much less prone to <a href=
|
||||
"definitions.html#Race condition">race conditions</a> than event variables.
|
||||
<p>Condition variables result in user code much less prone to
|
||||
<a href="definitions.html#definition-race-condition">race conditions</a> than event variables.
|
||||
See <a href="rationale.html#Events">Rationale</a> for analysis. Also see <a href="bibliography.html#Hoare-74">[Hoare
|
||||
74]</a> and <a href="bibliography.html#Schmidt-00">[Schmidt 00]</a>.</p>
|
||||
<h2><a name="question8"></a>8. Why isn't thread cancellation or termination
|
||||
@@ -167,9 +166,7 @@ private:
|
||||
greater safety by the combination of a mutex and a condition variable.</p>
|
||||
<hr>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->09 January, 2003<!--webbot bot="Timestamp" endspan i-checksum="38582" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
@@ -180,4 +177,4 @@ private:
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -145,7 +145,7 @@
|
||||
<p>A Mutex object has two states: locked and unlocked. Mutex object state can
|
||||
only be determined by an object meeting the <a href="lock_concept.html#ScopedLock">ScopedLock</a>
|
||||
requirements and constructed for the Mutex object.</p>
|
||||
<p>A Mutex is <a href="../../utility/utility.htm#Class noncopyable">NonCopyable</a>.</p>
|
||||
<p>A Mutex is <a href="../../utility/utility.htm#Class_noncopyable">NonCopyable</a>.</p>
|
||||
<p>For a Mutex type M and an object m of that type, the following expressions
|
||||
must be well-formed and have the indicated effects.</p>
|
||||
<table summary="Mutex expressions" border="1" cellpadding="5">
|
||||
@@ -223,9 +223,7 @@
|
||||
</table>
|
||||
<hr>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->09 January, 2003<!--webbot bot="Timestamp" endspan i-checksum="38582" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
@@ -236,4 +234,4 @@
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -110,7 +110,8 @@
|
||||
<p><b>Warning:</b> Multithreaded programs such as those using <b> Boost.Threads</b>
|
||||
must link to <a href="definitions.html#Thread-safe"> thread-safe</a> versions
|
||||
of all runtime libraries used by the program, including the runtime library
|
||||
for the C++ Standard Library. Otherwise <a href="definitions.html#Race condition">race
|
||||
for the C++ Standard Library. Otherwise
|
||||
<a href="definitions.html#definition-race-condition">race
|
||||
conditions</a> will occur when multiple threads simultaneously execute runtime
|
||||
library functions for <i>new</i>, <i>delete</i>, or other language features
|
||||
which imply shared state.</p>
|
||||
@@ -157,9 +158,7 @@
|
||||
as implementations are free to meet the NonCopyable requirement in other ways.</p>
|
||||
<hr>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->09 January, 2003<!--webbot bot="Timestamp" endspan i-checksum="38582" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
@@ -170,4 +169,4 @@
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -144,31 +144,27 @@
|
||||
appear to bear them out. To illustrate the analysis we'll first provide
|
||||
pseudo-code illustrating the six typical usage patterns of a thread object.</p>
|
||||
<h3>1. Simple creation of a thread.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
create_thread(&bar);
|
||||
}
|
||||
</pre>
|
||||
<h3>2. Creation of a thread that's later joined.</h3>
|
||||
<pre>
|
||||
Void foo()
|
||||
<pre>Void foo()
|
||||
{
|
||||
thread = create_thread(&bar);
|
||||
join(thread);
|
||||
}
|
||||
</pre>
|
||||
<h3>3. Simple creation of several threads in a loop.</h3>
|
||||
<pre>
|
||||
Void foo()
|
||||
<pre>Void foo()
|
||||
{
|
||||
for (int i=0; i<NUM_THREADS; ++i)
|
||||
create_thread(&bar);
|
||||
}
|
||||
</pre>
|
||||
<h3>4. Creation of several threads in a loop which are later joined.</h3>
|
||||
<pre>
|
||||
Void foo()
|
||||
<pre>Void foo()
|
||||
{
|
||||
for (int i=0; i<NUM_THREADS; ++i)
|
||||
threads[i] = create_thread(&bar);
|
||||
@@ -177,16 +173,14 @@ Void foo()
|
||||
}
|
||||
</pre>
|
||||
<h3>5. Creation of a thread whose ownership is passed to another object/method.</h3>
|
||||
<pre>
|
||||
Void foo()
|
||||
<pre>Void foo()
|
||||
{
|
||||
thread = create_thread(&bar);
|
||||
manager.owns(thread);
|
||||
}
|
||||
</pre>
|
||||
<h3>6. Creation of a thread whose ownership is shared between multiple objects.</h3>
|
||||
<pre>
|
||||
Void foo()
|
||||
<pre>Void foo()
|
||||
{
|
||||
thread = create_thread(&bar);
|
||||
manager1.add(thread);
|
||||
@@ -211,8 +205,7 @@ Void foo()
|
||||
for any of the usage patterns there would be a good argument for choosing the
|
||||
other design. So we'll code all six usage patterns using both designs.</p>
|
||||
<h3>1.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
thread thrd(&bar);
|
||||
}
|
||||
@@ -223,8 +216,7 @@ void foo()
|
||||
}
|
||||
</pre>
|
||||
<h3>2.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
thread thrd(&bar);
|
||||
thrd.join();
|
||||
@@ -237,8 +229,7 @@ void foo()
|
||||
}
|
||||
</pre>
|
||||
<h3>3.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
for (int i=0; i<NUM_THREADS; ++i)
|
||||
thread thrd(&bar);
|
||||
@@ -251,8 +242,7 @@ void foo()
|
||||
}
|
||||
</pre>
|
||||
<h3>4.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
std::auto_ptr<thread> threads[NUM_THREADS];
|
||||
for (int i=0; i<NUM_THREADS; ++i)
|
||||
@@ -271,8 +261,7 @@ void foo()
|
||||
}
|
||||
</pre>
|
||||
<h3>5.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
thread thrd* = new thread(&bar);
|
||||
manager.owns(thread);
|
||||
@@ -285,8 +274,7 @@ void foo()
|
||||
}
|
||||
</pre>
|
||||
<h3>6.</h3>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
boost::shared_ptr<thread> thrd(new thread(&bar));
|
||||
manager1.add(thrd);
|
||||
@@ -308,8 +296,7 @@ void foo()
|
||||
the presence of operator new in user code, but this can be eliminated by proper
|
||||
design of higher level concepts, such as the boost::thread_group class that
|
||||
simplifies example (4) down to:</p>
|
||||
<pre>
|
||||
void foo()
|
||||
<pre>void foo()
|
||||
{
|
||||
thread_group threads;
|
||||
for (int i=0; i<NUM_THREADS; ++i)
|
||||
@@ -366,8 +353,7 @@ void foo()
|
||||
variables are a continuing source of errors, even after previous bad experiences
|
||||
caused them to be very careful in their use of event variables. Overt problems
|
||||
can be avoided, for example, by teaming the event variable with a mutex, but
|
||||
that may just convert a <a href=
|
||||
"definitions.html#Race condition">race condition</a> into another problem,
|
||||
that may just convert a <a href="definitions.html#definition-race-condition">race condition</a> into another problem,
|
||||
such as excessive resource use. One of the most distressing aspects of the experience
|
||||
reports is the claim that many defects are latent. That is, the programs appear
|
||||
to work correctly, but contain hidden timing dependencies which will cause them
|
||||
@@ -383,9 +369,7 @@ void foo()
|
||||
<p>[Rationale provided by Beman Dawes]</p>
|
||||
<hr>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->09 January, 2003<!--webbot bot="Timestamp" endspan i-checksum="38582" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
@@ -396,4 +380,4 @@ void foo()
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -85,10 +85,8 @@
|
||||
having first been called, the thread of execution continues until its initial
|
||||
function completes.</p>
|
||||
<h4><a name="class-thread-synopsis"></a>Class <code>thread</code> synopsis</h4>
|
||||
<pre>
|
||||
namespace boost {
|
||||
class thread : <a href=
|
||||
"../../utility/utility.htm#Class noncopyable">boost::noncopyable</a> // Exposition only.
|
||||
<pre>namespace boost {
|
||||
class thread : <a href="../../utility/utility.htm#Class_noncopyable">boost::noncopyable</a> // Exposition only.
|
||||
// Class thread meets the <a href=
|
||||
"overview.html#non-copyable">NonCopyable</a> requirement.
|
||||
{
|
||||
@@ -109,8 +107,7 @@ public:
|
||||
</pre>
|
||||
<h4><a name="class-thread-ctors"></a>Class <code>thread</code> constructors and
|
||||
destructor</h4>
|
||||
<pre>
|
||||
thread();
|
||||
<pre>thread();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Constructs a <code>thread</code> object representing the
|
||||
@@ -118,8 +115,7 @@ thread();
|
||||
<dt><b>Postconditions:</b> <code>*this</code> is non-joinable.</dt>
|
||||
<dt><b>Danger:</b> <code>*this</code> is valid only within the current thread.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
thread(const <a href="../../function/index.html">boost::function0</a><void>& threadfunc);
|
||||
<pre>thread(const <a href="../../function/index.html">boost::function0</a><void>& threadfunc);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Starts a new thread of execution and constructs a <code>thread</code>
|
||||
@@ -132,8 +128,7 @@ thread(const <a href="../../function/index.html">boost::function0</a><void>
|
||||
<dt><b>Throws:</b> <code>boost::thread_resource_error</code> if a new thread
|
||||
of execution cannot be started.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
~Thread();
|
||||
<pre>~Thread();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Destroys <code>*this</code>. The actual thread of execution
|
||||
@@ -146,24 +141,21 @@ thread(const <a href="../../function/index.html">boost::function0</a><void>
|
||||
</dl>
|
||||
<h4><a name="class-thread-comparisons"></a>Class <code>thread</code> comparison
|
||||
functions</h4>
|
||||
<pre>
|
||||
bool operator==(const thread& rhs) const;
|
||||
<pre>bool operator==(const thread& rhs) const;
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Requires:</b> The thread is non-terminated or <code>*this</code> is joinable.</dt>
|
||||
<dt><b>Returns:</b> <code>true</code> if <code>*this</code> and <code> rhs</code>
|
||||
represent the same thread of execution.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
bool operator!=(const thread& rhs) const;
|
||||
<pre>bool operator!=(const thread& rhs) const;
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Requires:</b> The thread is non-terminated or <code>*this</code> is joinable.</dt>
|
||||
<dt><b>Returns:</b> <code>!(*this==rhs)</code>.</dt>
|
||||
</dl>
|
||||
<h4><a name="class-thread-modifiers"></a>Class <code>thread</code> modifier functions</h4>
|
||||
<pre>
|
||||
void join();
|
||||
<pre>void join();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Requires:</b> <code>*this</code> is joinable.</dt>
|
||||
@@ -176,15 +168,13 @@ void join();
|
||||
<a href="definitions.html#Deadlock"> deadlock</a>.</dt>
|
||||
</dl>
|
||||
<h4><a name="class-thread-statics"></a>Class <code>thread</code> static functions</h4>
|
||||
<pre>
|
||||
static void sleep(const <a href="xtime.html">xtime</a>& XT);
|
||||
<pre>static void sleep(const <a href="xtime.html">xtime</a>& XT);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> The current thread of execution blocks until <code> XT</code>
|
||||
is reached.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
static void yield();
|
||||
<pre>static void yield();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> The current thread of execution is placed in the "ready"
|
||||
@@ -200,10 +190,9 @@ static void yield();
|
||||
"definitions.html#thread-safe">thread-safe</a>, except destruction.</p>
|
||||
<h4><a name="class-thread_group-synopsis"></a>Class <code>thread_group</code>
|
||||
synopsis</h4>
|
||||
<pre>
|
||||
namespace boost {
|
||||
<pre>namespace boost {
|
||||
class thread_group : <a href=
|
||||
"../../utility/utility.htm#Class noncopyable">boost::noncopyable</a>
|
||||
"../../utility/utility.htm#Class_noncopyable">boost::noncopyable</a>
|
||||
{
|
||||
public:
|
||||
thread_group();
|
||||
@@ -218,14 +207,12 @@ namespace boost {
|
||||
</pre>
|
||||
<h4><a name="class-thread_group-ctors"></a>Class <code>thread_group</code> constructors
|
||||
and destructor</h4>
|
||||
<pre>
|
||||
thread_group();
|
||||
<pre>thread_group();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Constructs an empty <code>thread_group</code> container.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
~thread_group();
|
||||
<pre>~thread_group();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Destroys each contained thread object. Destroys <code>*this</code>.</dt>
|
||||
@@ -234,8 +221,7 @@ thread_group();
|
||||
</dl>
|
||||
<h4><a name="class-thread_group-modifiers"></a>Class <code>thread_group</code>
|
||||
modifier functions</h4>
|
||||
<pre>
|
||||
thread* create_thread(const boost::function0<void>& threadfunc);
|
||||
<pre>thread* create_thread(const boost::function0<void>& threadfunc);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Creates a new <tt>thread</tt> object that executes <tt>threadfunc</tt>
|
||||
@@ -243,16 +229,14 @@ thread* create_thread(const boost::function0<void>& threadfunc);
|
||||
<tt>thread</tt> objects.</dt>
|
||||
<dt><b>Returns:</b> Pointer to the newly created thread.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
void add_thread(thread* thrd);
|
||||
<pre>void add_thread(thread* thrd);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Adds <tt>thrd</tt> to the <tt>thread_group</tt> object's
|
||||
list of managed <tt>thread</tt> objects. The <tt>thrd</tt> object must have
|
||||
been allocated via operator new and will be deleted when the group is destroyed.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
Void remove_thread(thread* thrd);
|
||||
<pre>Void remove_thread(thread* thrd);
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Removes <code>*this</code>'s list of managed <tt>thread</tt>
|
||||
@@ -260,16 +244,14 @@ Void remove_thread(thread* thrd);
|
||||
<dt><b>Throws:</b> ? if <tt>thrd</tt> is not it <code>*this</code>'s list
|
||||
of managed <tt>thread</tt> objects.</dt>
|
||||
</dl>
|
||||
<pre>
|
||||
Void join_all();
|
||||
<pre>Void join_all();
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Effects:</b> Calls <code>join()</code> on each of the managed <tt>thread</tt>
|
||||
objects.</dt>
|
||||
</dl>
|
||||
<h2><a name="functions"></a>Functions</h2>
|
||||
<pre>
|
||||
<a name="function-spec"></a>{{function}}
|
||||
<pre><a name="function-spec"></a>{{function}}
|
||||
</pre>
|
||||
<dl class="function-semantics">
|
||||
<dt><b>Requires:</b> {{text}}</dt>
|
||||
@@ -286,15 +268,13 @@ Void join_all();
|
||||
<h3><a name="example-thread"></a>Simple usage of <code>boost::thread</code></h3>
|
||||
<p><a href="../example/thread.cpp">libs/thread/example/thread.cpp</a></p>
|
||||
<p>The output is:</p>
|
||||
<pre>
|
||||
setting alarm for 5 seconds...
|
||||
<pre>setting alarm for 5 seconds...
|
||||
alarm sounded...
|
||||
</pre>
|
||||
<h3><a name="example-thread_group"></a>Simple usage of <code>boost::thread_group</code></h3>
|
||||
<p><a href="../example/thread_group.cpp">libs/thread/example/thread_group.cpp</a></p>
|
||||
<p>The output is:</p>
|
||||
<pre>
|
||||
count = 1
|
||||
<pre>count = 1
|
||||
count = 2
|
||||
count = 3
|
||||
count = 4
|
||||
@@ -307,9 +287,7 @@ count = 10
|
||||
</pre>
|
||||
<hr>
|
||||
<p>Revised
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->
|
||||
05 November, 2001
|
||||
<!--webbot bot="Timestamp" endspan i-checksum="39359" -->
|
||||
<!--webbot bot="Timestamp" S-Type="EDITED" S-Format="%d %B, %Y" startspan -->09 January, 2003<!--webbot bot="Timestamp" endspan i-checksum="38582" -->
|
||||
</p>
|
||||
<p><i>© Copyright <a href="mailto:wekempf@cox.net">William E. Kempf</a> 2001-2002.
|
||||
All Rights Reserved.</i></p>
|
||||
@@ -320,4 +298,4 @@ count = 10
|
||||
makes no representations about the suitability of this software for any purpose.
|
||||
It is provided "as is" without express or implied warranty.</p>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -14,11 +14,13 @@ include <module@>threads.jam ;
|
||||
|
||||
template example
|
||||
## sources ##
|
||||
: <template>thread_base <lib>../build/boost_thread <lib>../../test/build/unit_test_framework $(threadmon)
|
||||
: <template>thread_base
|
||||
<dll>../build/boost_thread
|
||||
$(threadmon)
|
||||
## requirements ##
|
||||
:
|
||||
## default build ##
|
||||
: release <runtime-link>static
|
||||
: release <runtime-link>dynamic
|
||||
;
|
||||
|
||||
exe monitor : <template>example monitor.cpp ;
|
||||
|
||||
5
example/Jamfile.v2
Normal file
5
example/Jamfile.v2
Normal file
@@ -0,0 +1,5 @@
|
||||
|
||||
exe starvephil
|
||||
: starvephil.cpp ../build/boost_thread ../../test/build/unit_test_framework
|
||||
;
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <boost/thread/exceptions.hpp>
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/thread/detail/lock.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
# include <pthread.h>
|
||||
@@ -31,15 +32,61 @@ namespace boost {
|
||||
|
||||
struct xtime;
|
||||
|
||||
class condition : private noncopyable
|
||||
namespace detail {
|
||||
|
||||
class BOOST_THREAD_DECL condition_impl : private noncopyable
|
||||
{
|
||||
friend class condition;
|
||||
|
||||
public:
|
||||
condition();
|
||||
~condition();
|
||||
condition_impl();
|
||||
~condition_impl();
|
||||
|
||||
void notify_one();
|
||||
void notify_all();
|
||||
|
||||
#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS))
|
||||
void enter_wait();
|
||||
void do_wait();
|
||||
bool do_timed_wait(const xtime& xt);
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
void do_wait(pthread_mutex_t* pmutex);
|
||||
bool do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex);
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_HAS_WINTHREADS)
|
||||
void* m_gate;
|
||||
void* m_queue;
|
||||
void* m_mutex;
|
||||
unsigned m_gone; // # threads that timed out and never made it to m_queue
|
||||
unsigned long m_blocked; // # threads blocked on the condition
|
||||
unsigned m_waiting; // # threads no longer waiting for the condition but
|
||||
// still waiting to be removed from m_queue
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
pthread_cond_t m_condition;
|
||||
#elif defined(BOOST_HAS_MPTASKS)
|
||||
MPSemaphoreID m_gate;
|
||||
MPSemaphoreID m_queue;
|
||||
threads::mac::detail::scoped_critical_region m_mutex;
|
||||
threads::mac::detail::scoped_critical_region m_mutex_mutex;
|
||||
unsigned m_gone; // # threads that timed out and never made it to m_queue
|
||||
unsigned long m_blocked; // # threads blocked on the condition
|
||||
unsigned m_waiting; // # threads no longer waiting for the condition but
|
||||
// still waiting to be removed from m_queue
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class condition : private noncopyable
|
||||
{
|
||||
public:
|
||||
condition() { }
|
||||
~condition() { }
|
||||
|
||||
void notify_one() { m_impl.notify_one(); }
|
||||
void notify_all() { m_impl.notify_all(); }
|
||||
|
||||
template <typename L>
|
||||
void wait(L& lock)
|
||||
{
|
||||
@@ -84,79 +131,63 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
detail::condition_impl m_impl;
|
||||
|
||||
template <typename M>
|
||||
void do_wait(M& mutex)
|
||||
{
|
||||
#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS))
|
||||
enter_wait();
|
||||
m_impl.enter_wait();
|
||||
#endif
|
||||
|
||||
typedef detail::thread::lock_ops<M> lock_ops;
|
||||
typedef detail::thread::lock_ops<M>
|
||||
#if defined(__HP_aCC) && __HP_aCC <= 33900 && !defined(BOOST_STRICT_CONFIG)
|
||||
# define lock_ops lock_ops_ // HP confuses lock_ops witht the template
|
||||
#endif
|
||||
lock_ops;
|
||||
|
||||
typename lock_ops::lock_state state;
|
||||
lock_ops::unlock(mutex, state);
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
do_wait(state.pmutex);
|
||||
m_impl.do_wait(state.pmutex);
|
||||
#elif (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS))
|
||||
do_wait();
|
||||
m_impl.do_wait();
|
||||
#endif
|
||||
|
||||
lock_ops::lock(mutex, state);
|
||||
#undef lock_ops
|
||||
}
|
||||
|
||||
template <typename M>
|
||||
bool do_timed_wait(M& mutex, const xtime& xt)
|
||||
{
|
||||
#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS))
|
||||
enter_wait();
|
||||
m_impl.enter_wait();
|
||||
#endif
|
||||
|
||||
typedef detail::thread::lock_ops<M> lock_ops;
|
||||
typedef detail::thread::lock_ops<M>
|
||||
#if defined(__HP_aCC) && __HP_aCC <= 33900 && !defined(BOOST_STRICT_CONFIG)
|
||||
# define lock_ops lock_ops_ // HP confuses lock_ops witht the template
|
||||
#endif
|
||||
lock_ops;
|
||||
|
||||
typename lock_ops::lock_state state;
|
||||
lock_ops::unlock(mutex, state);
|
||||
|
||||
bool ret = false;
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
ret = do_timed_wait(xt, state.pmutex);
|
||||
ret = m_impl.do_timed_wait(xt, state.pmutex);
|
||||
#elif (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS))
|
||||
ret = do_timed_wait(xt);
|
||||
ret = m_impl.do_timed_wait(xt);
|
||||
#endif
|
||||
|
||||
lock_ops::lock(mutex, state);
|
||||
#undef lock_ops
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#if (defined(BOOST_HAS_WINTHREADS) || defined(BOOST_HAS_MPTASKS))
|
||||
void enter_wait();
|
||||
void do_wait();
|
||||
bool do_timed_wait(const xtime& xt);
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
void do_wait(pthread_mutex_t* pmutex);
|
||||
bool do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex);
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_HAS_WINTHREADS)
|
||||
void* m_gate;
|
||||
void* m_queue;
|
||||
void* m_mutex;
|
||||
unsigned m_gone; // # threads that timed out and never made it to the m_queue
|
||||
unsigned long m_blocked; // # threads m_blocked m_waiting for the condition
|
||||
unsigned m_waiting; // # threads m_waiting no longer m_waiting for the condition but still
|
||||
// m_waiting to be removed from the m_queue
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
pthread_cond_t m_condition;
|
||||
#elif defined(BOOST_HAS_MPTASKS)
|
||||
MPSemaphoreID m_gate;
|
||||
MPSemaphoreID m_queue;
|
||||
threads::mac::detail::scoped_critical_region m_mutex;
|
||||
threads::mac::detail::scoped_critical_region m_mutex_mutex;
|
||||
unsigned m_gone; // # threads that timed out and never made it to the m_queue
|
||||
unsigned long m_blocked; // # threads m_blocked m_waiting for the condition
|
||||
unsigned m_waiting; // # threads m_waiting no longer m_waiting for the condition but still
|
||||
// m_waiting to be removed from the m_queue
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace boost
|
||||
@@ -164,7 +195,8 @@ private:
|
||||
// Change Log:
|
||||
// 8 Feb 01 WEKEMPF Initial version.
|
||||
// 22 May 01 WEKEMPF Modified to use xtime for time outs.
|
||||
// 23 May 01 WEKEMPF Removed "duration" timed_waits, as they are too difficult
|
||||
// to use with spurious wakeups.
|
||||
// 23 May 01 WEKEMPF Removed "duration" timed_waits, as they are too
|
||||
// difficult to use with spurious wakeups.
|
||||
// 3 Jan 03 WEKEMPF Modified for DLL implementation.
|
||||
|
||||
#endif // BOOST_CONDITION_WEK070601_HPP
|
||||
|
||||
14
include/boost/thread/detail/config.hpp
Normal file
14
include/boost/thread/detail/config.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#ifndef BOOST_THREAD_CONFIG_WEK01032003_HPP
|
||||
#define BOOST_THREAD_CONFIG_WEK01032003_HPP
|
||||
|
||||
#if defined(BOOST_HAS_WINTHREADS)
|
||||
# if defined(BOOST_THREAD_BUILD_DLL)
|
||||
# define BOOST_THREAD_DECL __declspec(dllexport)
|
||||
# else
|
||||
# define BOOST_THREAD_DECL __declspec(dllimport)
|
||||
# endif
|
||||
#else
|
||||
# define BOOST_THREAD_DECL
|
||||
#endif // BOOST_THREAD_SHARED_LIB
|
||||
|
||||
#endif // BOOST_THREAD_CONFIG_WEK1032003_HPP
|
||||
14
include/boost/thread/detail/threadmon.hpp
Normal file
14
include/boost/thread/detail/threadmon.hpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <boost/config.hpp>
|
||||
#ifndef BOOST_HAS_THREADS
|
||||
# error Thread support is unavailable!
|
||||
#endif
|
||||
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_WINTHREADS
|
||||
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
extern "C" BOOST_THREAD_DECL int on_thread_exit(void (__cdecl * func)(void));
|
||||
|
||||
#endif // BOOST_HAS_WINTHREADS
|
||||
@@ -12,6 +12,9 @@
|
||||
#ifndef BOOST_THREAD_EXCEPTIONS_PDM070801_H
|
||||
#define BOOST_THREAD_EXCEPTIONS_PDM070801_H
|
||||
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
// pdm: Sorry, but this class is used all over the place & I end up
|
||||
// with recursive headers if I don't separate it
|
||||
// wek: Not sure why recursive headers would cause compilation problems
|
||||
@@ -22,13 +25,13 @@
|
||||
|
||||
namespace boost {
|
||||
|
||||
class lock_error : public std::logic_error
|
||||
class BOOST_THREAD_DECL lock_error : public std::logic_error
|
||||
{
|
||||
public:
|
||||
lock_error();
|
||||
};
|
||||
|
||||
class thread_resource_error : public std::runtime_error
|
||||
class BOOST_THREAD_DECL thread_resource_error : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
thread_resource_error();
|
||||
@@ -36,4 +39,7 @@ public:
|
||||
|
||||
} // namespace boost
|
||||
|
||||
// Change log:
|
||||
// 3 Jan 03 WEKEMPF Modified for DLL implementation.
|
||||
|
||||
#endif // BOOST_THREAD_CONFIG_PDM070801_H
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/thread/detail/lock.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
# include <pthread.h>
|
||||
@@ -32,7 +33,7 @@ namespace boost {
|
||||
|
||||
struct xtime;
|
||||
|
||||
class mutex : private noncopyable
|
||||
class BOOST_THREAD_DECL mutex : private noncopyable
|
||||
{
|
||||
public:
|
||||
friend class detail::thread::lock_ops<mutex>;
|
||||
@@ -70,7 +71,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
class try_mutex : private noncopyable
|
||||
class BOOST_THREAD_DECL try_mutex : private noncopyable
|
||||
{
|
||||
public:
|
||||
friend class detail::thread::lock_ops<try_mutex>;
|
||||
@@ -110,7 +111,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
class timed_mutex : private noncopyable
|
||||
class BOOST_THREAD_DECL timed_mutex : private noncopyable
|
||||
{
|
||||
public:
|
||||
friend class detail::thread::lock_ops<timed_mutex>;
|
||||
@@ -160,5 +161,6 @@ private:
|
||||
// 8 Feb 01 WEKEMPF Initial version.
|
||||
// 22 May 01 WEKEMPF Modified to use xtime for time outs. Factored out
|
||||
// to three classes, mutex, try_mutex and timed_mutex.
|
||||
// 3 Jan 03 WEKEMPF Modified for DLL implementation.
|
||||
|
||||
#endif // BOOST_MUTEX_WEK070601_HPP
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
# error Thread support is unavailable!
|
||||
#endif
|
||||
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
@@ -35,7 +37,7 @@ typedef long once_flag;
|
||||
|
||||
#endif
|
||||
|
||||
void call_once(void (*func)(), once_flag& flag);
|
||||
void BOOST_THREAD_DECL call_once(void (*func)(), once_flag& flag);
|
||||
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/thread/detail/lock.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
# include <pthread.h>
|
||||
@@ -30,7 +31,7 @@ namespace boost {
|
||||
|
||||
struct xtime;
|
||||
|
||||
class recursive_mutex : private noncopyable
|
||||
class BOOST_THREAD_DECL recursive_mutex : private noncopyable
|
||||
{
|
||||
public:
|
||||
friend class detail::thread::lock_ops<recursive_mutex>;
|
||||
@@ -73,7 +74,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
class recursive_try_mutex : private noncopyable
|
||||
class BOOST_THREAD_DECL recursive_try_mutex : private noncopyable
|
||||
{
|
||||
public:
|
||||
friend class detail::thread::lock_ops<recursive_try_mutex>;
|
||||
@@ -118,7 +119,7 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
class recursive_timed_mutex : private noncopyable
|
||||
class BOOST_THREAD_DECL recursive_timed_mutex : private noncopyable
|
||||
{
|
||||
public:
|
||||
friend class detail::thread::lock_ops<recursive_timed_mutex>;
|
||||
@@ -170,5 +171,7 @@ private:
|
||||
// 1 Jun 01 WEKEMPF Modified to use xtime for time outs. Factored out
|
||||
// to three classes, mutex, try_mutex and timed_mutex.
|
||||
// 11 Jun 01 WEKEMPF Modified to use PTHREAD_MUTEX_RECURSIVE if available.
|
||||
// 3 Jan 03 WEKEMPF Modified for DLL implementation.
|
||||
|
||||
|
||||
#endif // BOOST_RECURSIVE_MUTEX_WEK070601_HPP
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
@@ -34,7 +35,7 @@ namespace boost {
|
||||
|
||||
struct xtime;
|
||||
|
||||
class thread : private noncopyable
|
||||
class BOOST_THREAD_DECL thread : private noncopyable
|
||||
{
|
||||
public:
|
||||
thread();
|
||||
@@ -63,7 +64,7 @@ private:
|
||||
bool m_joinable;
|
||||
};
|
||||
|
||||
class thread_group : private noncopyable
|
||||
class BOOST_THREAD_DECL thread_group : private noncopyable
|
||||
{
|
||||
public:
|
||||
thread_group();
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#endif
|
||||
|
||||
#include <boost/utility.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
#if defined(BOOST_HAS_PTHREADS)
|
||||
# include <pthread.h>
|
||||
@@ -28,7 +29,7 @@
|
||||
namespace boost {
|
||||
|
||||
namespace detail {
|
||||
class tss : private noncopyable
|
||||
class BOOST_THREAD_DECL tss : private noncopyable
|
||||
{
|
||||
public:
|
||||
tss(void (*cleanup)(void*)=0);
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
#ifndef BOOST_XTIME_WEK070601_HPP
|
||||
#define BOOST_XTIME_WEK070601_HPP
|
||||
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/cstdint.hpp>
|
||||
#include <boost/thread/detail/config.hpp>
|
||||
|
||||
namespace boost {
|
||||
|
||||
@@ -39,7 +40,7 @@ struct xtime
|
||||
int_fast32_t nsec;
|
||||
};
|
||||
|
||||
int xtime_get(struct xtime* xtp, int clock_type);
|
||||
int BOOST_THREAD_DECL xtime_get(struct xtime* xtp, int clock_type);
|
||||
inline int xtime_cmp(const xtime& xt1, const xtime& xt2)
|
||||
{
|
||||
int res = (int)(xt1.sec - xt2.sec);
|
||||
|
||||
@@ -32,12 +32,15 @@
|
||||
|
||||
namespace boost {
|
||||
|
||||
namespace detail {
|
||||
|
||||
#if defined(BOOST_HAS_WINTHREADS)
|
||||
condition::condition()
|
||||
condition_impl::condition_impl()
|
||||
: m_gone(0), m_blocked(0), m_waiting(0)
|
||||
{
|
||||
m_gate = reinterpret_cast<void*>(CreateSemaphore(0, 1, 1, 0));
|
||||
m_queue = reinterpret_cast<void*>(CreateSemaphore(0, 0, std::numeric_limits<long>::max(), 0));
|
||||
m_queue = reinterpret_cast<void*>(CreateSemaphore(0, 0,
|
||||
std::numeric_limits<long>::max(), 0));
|
||||
m_mutex = reinterpret_cast<void*>(CreateMutex(0, 0, 0));
|
||||
|
||||
if (!m_gate || !m_queue || !m_mutex)
|
||||
@@ -63,7 +66,7 @@ condition::condition()
|
||||
}
|
||||
}
|
||||
|
||||
condition::~condition()
|
||||
condition_impl::~condition_impl()
|
||||
{
|
||||
int res = 0;
|
||||
res = CloseHandle(reinterpret_cast<HANDLE>(m_gate));
|
||||
@@ -74,7 +77,7 @@ condition::~condition()
|
||||
assert(res);
|
||||
}
|
||||
|
||||
void condition::notify_one()
|
||||
void condition_impl::notify_one()
|
||||
{
|
||||
unsigned signals = 0;
|
||||
|
||||
@@ -126,7 +129,7 @@ void condition::notify_one()
|
||||
}
|
||||
}
|
||||
|
||||
void condition::notify_all()
|
||||
void condition_impl::notify_all()
|
||||
{
|
||||
unsigned signals = 0;
|
||||
|
||||
@@ -177,7 +180,7 @@ void condition::notify_all()
|
||||
}
|
||||
}
|
||||
|
||||
void condition::enter_wait()
|
||||
void condition_impl::enter_wait()
|
||||
{
|
||||
int res = 0;
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_gate), INFINITE);
|
||||
@@ -187,7 +190,7 @@ void condition::enter_wait()
|
||||
assert(res);
|
||||
}
|
||||
|
||||
void condition::do_wait()
|
||||
void condition_impl::do_wait()
|
||||
{
|
||||
int res = 0;
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
|
||||
@@ -206,7 +209,8 @@ void condition::do_wait()
|
||||
{
|
||||
if (m_blocked != 0)
|
||||
{
|
||||
res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0); // open m_gate
|
||||
res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1,
|
||||
0); // open m_gate
|
||||
assert(res);
|
||||
was_waiting = 0;
|
||||
}
|
||||
@@ -234,7 +238,8 @@ void condition::do_wait()
|
||||
for (/**/ ; was_gone; --was_gone)
|
||||
{
|
||||
// better now than spurious later
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue),
|
||||
INFINITE);
|
||||
assert(res == WAIT_OBJECT_0);
|
||||
}
|
||||
res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
|
||||
@@ -242,7 +247,7 @@ void condition::do_wait()
|
||||
}
|
||||
}
|
||||
|
||||
bool condition::do_timed_wait(const xtime& xt)
|
||||
bool condition_impl::do_timed_wait(const xtime& xt)
|
||||
{
|
||||
bool ret = false;
|
||||
unsigned int res = 0;
|
||||
@@ -252,7 +257,8 @@ bool condition::do_timed_wait(const xtime& xt)
|
||||
int milliseconds;
|
||||
to_duration(xt, milliseconds);
|
||||
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), milliseconds);
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue),
|
||||
milliseconds);
|
||||
assert(res != WAIT_FAILED && res != WAIT_ABANDONED);
|
||||
ret = (res == WAIT_OBJECT_0);
|
||||
|
||||
@@ -287,7 +293,8 @@ bool condition::do_timed_wait(const xtime& xt)
|
||||
{
|
||||
if (m_blocked != 0)
|
||||
{
|
||||
res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0); // open m_gate
|
||||
res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1,
|
||||
0); // open m_gate
|
||||
assert(res);
|
||||
was_waiting = 0;
|
||||
}
|
||||
@@ -315,7 +322,8 @@ bool condition::do_timed_wait(const xtime& xt)
|
||||
for (/**/ ; was_gone; --was_gone)
|
||||
{
|
||||
// better now than spurious later
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue), INFINITE);
|
||||
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_queue),
|
||||
INFINITE);
|
||||
assert(res == WAIT_OBJECT_0);
|
||||
}
|
||||
res = ReleaseSemaphore(reinterpret_cast<HANDLE>(m_gate), 1, 0);
|
||||
@@ -325,7 +333,7 @@ bool condition::do_timed_wait(const xtime& xt)
|
||||
return ret;
|
||||
}
|
||||
#elif defined(BOOST_HAS_PTHREADS)
|
||||
condition::condition()
|
||||
condition_impl::condition_impl()
|
||||
{
|
||||
int res = 0;
|
||||
res = pthread_cond_init(&m_condition, 0);
|
||||
@@ -333,35 +341,35 @@ condition::condition()
|
||||
throw thread_resource_error();
|
||||
}
|
||||
|
||||
condition::~condition()
|
||||
condition_impl::~condition_impl()
|
||||
{
|
||||
int res = 0;
|
||||
res = pthread_cond_destroy(&m_condition);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
void condition::notify_one()
|
||||
void condition_impl::notify_one()
|
||||
{
|
||||
int res = 0;
|
||||
res = pthread_cond_signal(&m_condition);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
void condition::notify_all()
|
||||
void condition_impl::notify_all()
|
||||
{
|
||||
int res = 0;
|
||||
res = pthread_cond_broadcast(&m_condition);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
void condition::do_wait(pthread_mutex_t* pmutex)
|
||||
void condition_impl::do_wait(pthread_mutex_t* pmutex)
|
||||
{
|
||||
int res = 0;
|
||||
res = pthread_cond_wait(&m_condition, pmutex);
|
||||
assert(res == 0);
|
||||
}
|
||||
|
||||
bool condition::do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex)
|
||||
bool condition_impl::do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex)
|
||||
{
|
||||
timespec ts;
|
||||
to_timespec(xt, ts);
|
||||
@@ -377,7 +385,7 @@ bool condition::do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex)
|
||||
using threads::mac::detail::safe_enter_critical_region;
|
||||
using threads::mac::detail::safe_wait_on_semaphore;
|
||||
|
||||
condition::condition()
|
||||
condition_impl::condition_impl()
|
||||
: m_gone(0), m_blocked(0), m_waiting(0)
|
||||
{
|
||||
threads::mac::detail::thread_init();
|
||||
@@ -405,7 +413,7 @@ condition::condition()
|
||||
}
|
||||
}
|
||||
|
||||
condition::~condition()
|
||||
condition_impl::~condition_impl()
|
||||
{
|
||||
OSStatus lStatus = noErr;
|
||||
lStatus = MPDeleteSemaphore(m_gate);
|
||||
@@ -414,7 +422,7 @@ condition::~condition()
|
||||
assert(lStatus == noErr);
|
||||
}
|
||||
|
||||
void condition::notify_one()
|
||||
void condition_impl::notify_one()
|
||||
{
|
||||
unsigned signals = 0;
|
||||
|
||||
@@ -466,7 +474,7 @@ void condition::notify_one()
|
||||
}
|
||||
}
|
||||
|
||||
void condition::notify_all()
|
||||
void condition_impl::notify_all()
|
||||
{
|
||||
unsigned signals = 0;
|
||||
|
||||
@@ -518,7 +526,7 @@ void condition::notify_all()
|
||||
}
|
||||
}
|
||||
|
||||
void condition::enter_wait()
|
||||
void condition_impl::enter_wait()
|
||||
{
|
||||
OSStatus lStatus = noErr;
|
||||
lStatus = safe_wait_on_semaphore(m_gate, kDurationForever);
|
||||
@@ -528,7 +536,7 @@ void condition::enter_wait()
|
||||
assert(lStatus == noErr);
|
||||
}
|
||||
|
||||
void condition::do_wait()
|
||||
void condition_impl::do_wait()
|
||||
{
|
||||
OSStatus lStatus = noErr;
|
||||
lStatus = safe_wait_on_semaphore(m_queue, kDurationForever);
|
||||
@@ -583,7 +591,7 @@ void condition::do_wait()
|
||||
}
|
||||
}
|
||||
|
||||
bool condition::do_timed_wait(const xtime& xt)
|
||||
bool condition_impl::do_timed_wait(const xtime& xt)
|
||||
{
|
||||
int milliseconds;
|
||||
to_duration(xt, milliseconds);
|
||||
@@ -653,8 +661,11 @@ bool condition::do_timed_wait(const xtime& xt)
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace boost
|
||||
|
||||
// Change Log:
|
||||
// 8 Feb 01 WEKEMPF Initial version.
|
||||
// 22 May 01 WEKEMPF Modified to use xtime for time outs.
|
||||
// 3 Jan 03 WEKEMPF Modified for DLL implementation.
|
||||
|
||||
@@ -122,7 +122,7 @@ void call_once(void (*func)(), once_flag& flag)
|
||||
#else
|
||||
std::ostringstream strm;
|
||||
strm << "2AC1A572DB6944B0A65C38C4140AF2F4" << std::hex << GetCurrentProcessId() << &flag;
|
||||
HANDLE mutex = CreateMutex(NULL, FALSE, strm.str().c_str());
|
||||
HANDLE mutex = CreateMutexA(NULL, FALSE, strm.str().c_str());
|
||||
#endif
|
||||
assert(mutex != NULL);
|
||||
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// threadmon.cpp : Defines the entry point for the DLL application.
|
||||
//
|
||||
|
||||
#define BOOST_THREADMON_EXPORTS
|
||||
#include "threadmon.hpp"
|
||||
#include <boost/config.hpp>
|
||||
|
||||
#ifdef BOOST_HAS_WINTHREADS
|
||||
#if defined(BOOST_HAS_WINTHREADS)
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#if defined(BOOST_THREAD_BUILD_DLL)
|
||||
|
||||
#include <boost/thread/detail/threadmon.hpp>
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#include <windows.h>
|
||||
|
||||
#ifdef BOOST_MSVC
|
||||
@@ -46,13 +49,18 @@ BOOL WINAPI DllMain(HANDLE module, DWORD reason, LPVOID)
|
||||
case DLL_THREAD_DETACH:
|
||||
{
|
||||
// Call the thread's exit handlers.
|
||||
exit_handlers* handlers = static_cast<exit_handlers*>(TlsGetValue(key));
|
||||
exit_handlers* handlers =
|
||||
static_cast<exit_handlers*>(TlsGetValue(key));
|
||||
if (handlers)
|
||||
{
|
||||
for (exit_handlers::iterator it = handlers->begin(); it != handlers->end(); ++it)
|
||||
for (exit_handlers::iterator it = handlers->begin();
|
||||
it != handlers->end(); ++it)
|
||||
{
|
||||
(*it)();
|
||||
}
|
||||
|
||||
// Remove the exit handler list from the registered lists and then destroy it.
|
||||
// Remove the exit handler list from the registered lists
|
||||
// and then destroy it.
|
||||
EnterCriticalSection(&cs);
|
||||
registry.erase(handlers);
|
||||
LeaveCriticalSection(&cs);
|
||||
@@ -62,22 +70,31 @@ BOOL WINAPI DllMain(HANDLE module, DWORD reason, LPVOID)
|
||||
break;
|
||||
case DLL_PROCESS_DETACH:
|
||||
{
|
||||
// Assume the main thread is ending (call its handlers) and all other threads
|
||||
// have already ended. If this DLL is loaded and unloaded dynamically at run time
|
||||
// Assume the main thread is ending (call its handlers) and
|
||||
// all other threads have already ended. If this DLL is
|
||||
// loaded and unloaded dynamically at run time
|
||||
// this is a bad assumption, but this is the best we can do.
|
||||
exit_handlers* handlers = static_cast<exit_handlers*>(TlsGetValue(key));
|
||||
exit_handlers* handlers =
|
||||
static_cast<exit_handlers*>(TlsGetValue(key));
|
||||
if (handlers)
|
||||
{
|
||||
for (exit_handlers::iterator it = handlers->begin(); it != handlers->end(); ++it)
|
||||
for (exit_handlers::iterator it = handlers->begin();
|
||||
it != handlers->end(); ++it)
|
||||
{
|
||||
(*it)();
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy any remaining exit handlers. Above we assumed there'd only be the main
|
||||
// thread left, but to insure we don't get memory leaks we won't make that assumption
|
||||
// Destroy any remaining exit handlers. Above we assumed
|
||||
// there'd only be the main thread left, but to insure we
|
||||
// don't get memory leaks we won't make that assumption
|
||||
// here.
|
||||
EnterCriticalSection(&cs);
|
||||
for (registered_handlers::iterator it = registry.begin(); it != registry.end(); ++it)
|
||||
for (registered_handlers::iterator it = registry.begin();
|
||||
it != registry.end(); ++it)
|
||||
{
|
||||
delete (*it);
|
||||
}
|
||||
LeaveCriticalSection(&cs);
|
||||
DeleteCriticalSection(&cs);
|
||||
TlsFree(key);
|
||||
@@ -87,7 +104,7 @@ BOOL WINAPI DllMain(HANDLE module, DWORD reason, LPVOID)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int on_thread_exit(void (__cdecl * func)(void))
|
||||
extern "C" BOOST_THREAD_DECL int on_thread_exit(void (__cdecl * func)(void))
|
||||
{
|
||||
// Get the exit handlers for the current thread, creating and registering
|
||||
// one if it doesn't exist.
|
||||
@@ -97,7 +114,7 @@ int on_thread_exit(void (__cdecl * func)(void))
|
||||
try
|
||||
{
|
||||
handlers = new exit_handlers;
|
||||
// Handle "broken" implementations of operator new that don't throw.
|
||||
// Handle broken implementations of operator new that don't throw.
|
||||
if (!handlers)
|
||||
return -1;
|
||||
}
|
||||
@@ -129,8 +146,8 @@ int on_thread_exit(void (__cdecl * func)(void))
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to add the handler to the list of exit handlers. If it's been previously
|
||||
// added just report success and exit.
|
||||
// Attempt to add the handler to the list of exit handlers. If it's been
|
||||
// previously added just report success and exit.
|
||||
try
|
||||
{
|
||||
handlers->push_front(func);
|
||||
@@ -143,4 +160,6 @@ int on_thread_exit(void (__cdecl * func)(void))
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif // BOOST_THREAD_BUILD_DLL
|
||||
|
||||
#endif // BOOST_HAS_WINTHREADS
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
#include <boost/config.hpp>
|
||||
#ifndef BOOST_HAS_THREADS
|
||||
# error Thread support is unavailable!
|
||||
#endif
|
||||
|
||||
#ifdef BOOST_HAS_WINTHREADS
|
||||
|
||||
// The following ifdef block is the standard way of creating macros which make exporting
|
||||
// from a DLL simpler. All files within this DLL are compiled with the BOOST_THREADMON_EXPORTS
|
||||
// symbol defined on the command line. this symbol should not be defined on any project
|
||||
// that uses this DLL. This way any other project whose source files include this file see
|
||||
// BOOST_THREADMON_API functions as being imported from a DLL, wheras this DLL sees symbols
|
||||
// defined with this macro as being exported.
|
||||
|
||||
#ifdef BOOST_THREADMON_EXPORTS
|
||||
#define BOOST_THREADMON_API __declspec(dllexport)
|
||||
#else
|
||||
#define BOOST_THREADMON_API __declspec(dllimport)
|
||||
#endif
|
||||
|
||||
extern "C" BOOST_THREADMON_API int on_thread_exit(void (__cdecl * func)(void));
|
||||
|
||||
#endif // BOOST_HAS_WINTHREADS
|
||||
@@ -83,7 +83,7 @@ namespace {
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void to_duration(const boost::xtime& xt, int& milliseconds)
|
||||
inline void to_duration(boost::xtime xt, int& milliseconds)
|
||||
{
|
||||
boost::xtime cur;
|
||||
int res = 0;
|
||||
@@ -94,6 +94,11 @@ namespace {
|
||||
milliseconds = 0;
|
||||
else
|
||||
{
|
||||
if (cur.nsec > xt.nsec)
|
||||
{
|
||||
xt.nsec += NANOSECONDS_PER_SECOND;
|
||||
--xt.sec;
|
||||
}
|
||||
milliseconds = ((xt.sec - cur.sec) * MILLISECONDS_PER_SECOND) +
|
||||
(((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MILLISECOND/2)) /
|
||||
NANOSECONDS_PER_MILLISECOND);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#endif
|
||||
|
||||
#if defined(BOOST_HAS_WINTHREADS)
|
||||
#include "threadmon.hpp"
|
||||
#include <boost/thread/detail/threadmon.hpp>
|
||||
#include <map>
|
||||
namespace {
|
||||
typedef std::pair<void(*)(void*), void*> cleanup_info;
|
||||
|
||||
50
test/Jamfile
50
test/Jamfile
@@ -1,12 +1,10 @@
|
||||
# (C) Copyright William E. Kempf 2001. Permission to copy, use, modify, sell and
|
||||
# distribute this software is granted provided this copyright notice appears
|
||||
# in all copies. This software is provided "as is" without express or implied
|
||||
# warranty, and with no claim as to its suitability for any purpose.
|
||||
# (C) Copyright William E. Kempf 2001. Permission to copy, use, modify, sell
|
||||
# and distribute this software is granted provided this copyright notice
|
||||
# appears in all copies. This software is provided "as is" without express or
|
||||
# implied warranty, and with no claim as to its suitability for any purpose.
|
||||
#
|
||||
# Boost.Threads test Jamfile
|
||||
#
|
||||
# Declares the following targets:
|
||||
# 1. test_thread, a unit test executable.
|
||||
# Additional configuration variables used:
|
||||
# 1. PTW32 may be used on Win32 platforms to specify that the pthreads-win32
|
||||
# library should be used instead of "native" threads. This feature is
|
||||
@@ -30,23 +28,25 @@ include threads.jam ;
|
||||
SEARCH on testing.jam = $(BOOST_BUILD_PATH) ;
|
||||
include testing.jam ;
|
||||
|
||||
sources = test.cpp test_thread.cpp test_mutex.cpp test_condition.cpp test_tss.cpp test_once.cpp ;
|
||||
{
|
||||
template test
|
||||
## sources ##
|
||||
: <template>thread_base
|
||||
<dll>../build/boost_thread
|
||||
<lib>../../test/build/boost_unit_test_framework
|
||||
# $(threadmon)
|
||||
## requirements ##
|
||||
:
|
||||
## default build ##
|
||||
: # debug release <runtime-link>static/dynamic
|
||||
;
|
||||
|
||||
template test
|
||||
## sources ##
|
||||
: <template>thread_base <lib>../build/boost_thread <lib>../../test/build/unit_test_framework $(threadmon)
|
||||
## requirements ##
|
||||
:
|
||||
## default build ##
|
||||
: debug release <runtime-link>static/dynamic
|
||||
;
|
||||
|
||||
#######################
|
||||
# Declare the Boost.Threads unit test program test_thread.
|
||||
|
||||
run test_thread.cpp <template>test ;
|
||||
run test_mutex.cpp <template>test ;
|
||||
run test_condition.cpp <template>test ;
|
||||
run test_tss.cpp <template>test ;
|
||||
run test_once.cpp <template>test ;
|
||||
run test_xtime.cpp <template>test ;
|
||||
test-suite "threads"
|
||||
: [ run test_thread.cpp <template>test ]
|
||||
[ run test_mutex.cpp <template>test ]
|
||||
[ run test_condition.cpp <template>test ]
|
||||
[ run test_tss.cpp <template>test ]
|
||||
[ run test_once.cpp <template>test ]
|
||||
[ run test_xtime.cpp <template>test ]
|
||||
;
|
||||
}
|
||||
|
||||
@@ -4,100 +4,83 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace
|
||||
#include "util.inl"
|
||||
|
||||
struct condition_test_data
|
||||
{
|
||||
struct condition_test_data
|
||||
{
|
||||
condition_test_data() : notified(0), awoken(0) { }
|
||||
condition_test_data() : notified(0), awoken(0) { }
|
||||
|
||||
boost::mutex mutex;
|
||||
boost::condition condition;
|
||||
int notified;
|
||||
int awoken;
|
||||
};
|
||||
boost::mutex mutex;
|
||||
boost::condition condition;
|
||||
int notified;
|
||||
int awoken;
|
||||
};
|
||||
|
||||
void condition_test_thread(void* param)
|
||||
{
|
||||
condition_test_data* data = static_cast<condition_test_data*>(param);
|
||||
boost::mutex::scoped_lock lock(data->mutex);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
while (!(data->notified > 0))
|
||||
data->condition.wait(lock);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
data->awoken++;
|
||||
}
|
||||
|
||||
class thread_adapter
|
||||
{
|
||||
public:
|
||||
thread_adapter(void (*func)(void*), void* param) : _func(func), _param(param) { }
|
||||
void operator()() const { _func(_param); }
|
||||
private:
|
||||
void (*_func)(void*);
|
||||
void* _param;
|
||||
};
|
||||
|
||||
struct cond_predicate
|
||||
{
|
||||
cond_predicate(int& var, int val) : _var(var), _val(val) { }
|
||||
|
||||
bool operator()() { return _var == _val; }
|
||||
|
||||
int& _var;
|
||||
int _val;
|
||||
};
|
||||
|
||||
void condition_test_waits(void* param)
|
||||
{
|
||||
condition_test_data* data = static_cast<condition_test_data*>(param);
|
||||
|
||||
boost::mutex::scoped_lock lock(data->mutex);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
|
||||
// Test wait.
|
||||
while (data->notified != 1)
|
||||
data->condition.wait(lock);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data->notified, 1);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
|
||||
// Test predicate wait.
|
||||
data->condition.wait(lock, cond_predicate(data->notified, 2));
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data->notified, 2);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
|
||||
// Test timed_wait.
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 10;
|
||||
while (data->notified != 3)
|
||||
data->condition.timed_wait(lock, xt);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data->notified, 3);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
|
||||
// Test predicate timed_wait.
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 10;
|
||||
cond_predicate pred(data->notified, 4);
|
||||
BOOST_CHECK(data->condition.timed_wait(lock, xt, pred));
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK(pred());
|
||||
BOOST_CHECK_EQUAL(data->notified, 4);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
}
|
||||
void condition_test_thread(condition_test_data* data)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(data->mutex);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
while (!(data->notified > 0))
|
||||
data->condition.wait(lock);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
data->awoken++;
|
||||
}
|
||||
|
||||
void test_condition_notify_one()
|
||||
struct cond_predicate
|
||||
{
|
||||
cond_predicate(int& var, int val) : _var(var), _val(val) { }
|
||||
|
||||
bool operator()() { return _var == _val; }
|
||||
|
||||
int& _var;
|
||||
int _val;
|
||||
};
|
||||
|
||||
void condition_test_waits(condition_test_data* data)
|
||||
{
|
||||
boost::mutex::scoped_lock lock(data->mutex);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
|
||||
// Test wait.
|
||||
while (data->notified != 1)
|
||||
data->condition.wait(lock);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data->notified, 1);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
|
||||
// Test predicate wait.
|
||||
data->condition.wait(lock, cond_predicate(data->notified, 2));
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data->notified, 2);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
|
||||
// Test timed_wait.
|
||||
boost::xtime xt = delay(10);
|
||||
while (data->notified != 3)
|
||||
data->condition.timed_wait(lock, xt);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data->notified, 3);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
|
||||
// Test predicate timed_wait.
|
||||
xt = delay(10);
|
||||
cond_predicate pred(data->notified, 4);
|
||||
BOOST_CHECK(data->condition.timed_wait(lock, xt, pred));
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK(pred());
|
||||
BOOST_CHECK_EQUAL(data->notified, 4);
|
||||
data->awoken++;
|
||||
data->condition.notify_one();
|
||||
}
|
||||
|
||||
void do_test_condition_notify_one()
|
||||
{
|
||||
condition_test_data data;
|
||||
|
||||
boost::thread thread(thread_adapter(&condition_test_thread, &data));
|
||||
boost::thread thread(bind(&condition_test_thread, &data));
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(data.mutex);
|
||||
@@ -110,14 +93,19 @@ void test_condition_notify_one()
|
||||
BOOST_CHECK_EQUAL(data.awoken, 1);
|
||||
}
|
||||
|
||||
void test_condition_notify_all()
|
||||
void test_condition_notify_one()
|
||||
{
|
||||
timed_test(&do_test_condition_notify_one, 2, execution_monitor::use_mutex);
|
||||
}
|
||||
|
||||
void do_test_condition_notify_all()
|
||||
{
|
||||
const int NUMTHREADS = 5;
|
||||
boost::thread_group threads;
|
||||
condition_test_data data;
|
||||
|
||||
for (int i = 0; i < NUMTHREADS; ++i)
|
||||
threads.create_thread(thread_adapter(&condition_test_thread, &data));
|
||||
threads.create_thread(bind(&condition_test_thread, &data));
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(data.mutex);
|
||||
@@ -130,21 +118,25 @@ void test_condition_notify_all()
|
||||
BOOST_CHECK_EQUAL(data.awoken, NUMTHREADS);
|
||||
}
|
||||
|
||||
void test_condition_waits()
|
||||
void test_condition_notify_all()
|
||||
{
|
||||
// We should have already tested notify_one here, so
|
||||
// a timed test with the default execution_monitor::use_condition
|
||||
// should be OK, and gives the fastest performance
|
||||
timed_test(&do_test_condition_notify_all, 3);
|
||||
}
|
||||
|
||||
void do_test_condition_waits()
|
||||
{
|
||||
condition_test_data data;
|
||||
|
||||
boost::thread thread(thread_adapter(&condition_test_waits, &data));
|
||||
|
||||
boost::xtime xt;
|
||||
boost::thread thread(bind(&condition_test_waits, &data));
|
||||
|
||||
{
|
||||
boost::mutex::scoped_lock lock(data.mutex);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 1;
|
||||
boost::thread::sleep(xt);
|
||||
boost::thread::sleep(delay(1));
|
||||
data.notified++;
|
||||
data.condition.notify_one();
|
||||
while (data.awoken != 1)
|
||||
@@ -152,9 +144,7 @@ void test_condition_waits()
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data.awoken, 1);
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 1;
|
||||
boost::thread::sleep(xt);
|
||||
boost::thread::sleep(delay(1));
|
||||
data.notified++;
|
||||
data.condition.notify_one();
|
||||
while (data.awoken != 2)
|
||||
@@ -162,9 +152,7 @@ void test_condition_waits()
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data.awoken, 2);
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 1;
|
||||
boost::thread::sleep(xt);
|
||||
boost::thread::sleep(delay(1));
|
||||
data.notified++;
|
||||
data.condition.notify_one();
|
||||
while (data.awoken != 3)
|
||||
@@ -172,9 +160,7 @@ void test_condition_waits()
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK_EQUAL(data.awoken, 3);
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 1;
|
||||
boost::thread::sleep(xt);
|
||||
boost::thread::sleep(delay(1));
|
||||
data.notified++;
|
||||
data.condition.notify_one();
|
||||
while (data.awoken != 4)
|
||||
@@ -183,16 +169,22 @@ void test_condition_waits()
|
||||
BOOST_CHECK_EQUAL(data.awoken, 4);
|
||||
}
|
||||
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 1;
|
||||
boost::thread::sleep(xt);
|
||||
thread.join();
|
||||
BOOST_CHECK_EQUAL(data.awoken, 4);
|
||||
}
|
||||
|
||||
void test_condition_waits()
|
||||
{
|
||||
// We should have already tested notify_one here, so
|
||||
// a timed test with the default execution_monitor::use_condition
|
||||
// should be OK, and gives the fastest performance
|
||||
timed_test(&do_test_condition_waits, 12);
|
||||
}
|
||||
|
||||
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
|
||||
{
|
||||
boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: condition test suite");
|
||||
boost::unit_test_framework::test_suite* test =
|
||||
BOOST_TEST_SUITE("Boost.Threads: condition test suite");
|
||||
|
||||
test->add(BOOST_TEST_CASE(&test_condition_notify_one));
|
||||
test->add(BOOST_TEST_CASE(&test_condition_notify_all));
|
||||
|
||||
@@ -6,22 +6,8 @@
|
||||
#include <boost/test/unit_test.hpp>
|
||||
#include <boost/test/unit_test_suite_ex.hpp>
|
||||
|
||||
namespace
|
||||
{
|
||||
inline bool xtime_in_range(boost::xtime& xt, int less_seconds, int greater_seconds)
|
||||
{
|
||||
boost::xtime cur;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&cur, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
|
||||
boost::xtime less = cur;
|
||||
less.sec += less_seconds;
|
||||
|
||||
boost::xtime greater = cur;
|
||||
greater.sec += greater_seconds;
|
||||
|
||||
return (boost::xtime_cmp(xt, less) >= 0) && (boost::xtime_cmp(xt, greater) <= 0);
|
||||
}
|
||||
}
|
||||
#define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_sleep_only
|
||||
#include "util.inl"
|
||||
|
||||
template <typename M>
|
||||
struct test_lock
|
||||
@@ -43,9 +29,7 @@ struct test_lock
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
|
||||
// Construct and initialize an xtime for a fast time out.
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.nsec += 100000000;
|
||||
boost::xtime xt = delay(0, 100);
|
||||
|
||||
// Test the lock and the mutex with condition variables.
|
||||
// No one is going to notify this condition variable. We expect to
|
||||
@@ -85,9 +69,7 @@ struct test_trylock
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
|
||||
// Construct and initialize an xtime for a fast time out.
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.nsec += 100000000;
|
||||
boost::xtime xt = delay(0, 100);
|
||||
|
||||
// Test the lock and the mutex with condition variables.
|
||||
// No one is going to notify this condition variable. We expect to
|
||||
@@ -121,9 +103,7 @@ struct test_timedlock
|
||||
// Test the lock's constructors.
|
||||
{
|
||||
// Construct and initialize an xtime for a fast time out.
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.nsec += 100000000;
|
||||
boost::xtime xt = delay(0, 100);
|
||||
|
||||
timed_lock_type lock(mutex, xt);
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
@@ -136,16 +116,14 @@ struct test_timedlock
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
|
||||
// Construct and initialize an xtime for a fast time out.
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.nsec += 100000000;
|
||||
boost::xtime xt = delay(0, 100);
|
||||
|
||||
// Test the lock and the mutex with condition variables.
|
||||
// No one is going to notify this condition variable. We expect to
|
||||
// time out.
|
||||
BOOST_CHECK(!condition.timed_wait(lock, xt));
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
BOOST_CHECK(xtime_in_range(xt, -1, 0));
|
||||
BOOST_CHECK(in_range(xt));
|
||||
|
||||
// Test the lock, unlock and timedlock methods.
|
||||
lock.unlock();
|
||||
@@ -154,8 +132,7 @@ struct test_timedlock
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
lock.unlock();
|
||||
BOOST_CHECK(!lock);
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.nsec += 100000000;
|
||||
xt = delay(0, 100);
|
||||
BOOST_CHECK(lock.timed_lock(xt));
|
||||
BOOST_CHECK(lock ? true : false);
|
||||
}
|
||||
@@ -175,38 +152,63 @@ struct test_recursive_lock
|
||||
}
|
||||
};
|
||||
|
||||
void test_mutex()
|
||||
void do_test_mutex()
|
||||
{
|
||||
test_lock<boost::mutex>()();
|
||||
}
|
||||
|
||||
void test_try_mutex()
|
||||
void test_mutex()
|
||||
{
|
||||
timed_test(&do_test_mutex, 3);
|
||||
}
|
||||
|
||||
void do_test_try_mutex()
|
||||
{
|
||||
test_lock<boost::try_mutex>()();
|
||||
test_trylock<boost::try_mutex>()();
|
||||
}
|
||||
|
||||
void test_timed_mutex()
|
||||
void test_try_mutex()
|
||||
{
|
||||
timed_test(&do_test_try_mutex, 3);
|
||||
}
|
||||
|
||||
void do_test_timed_mutex()
|
||||
{
|
||||
test_lock<boost::timed_mutex>()();
|
||||
test_trylock<boost::timed_mutex>()();
|
||||
test_timedlock<boost::timed_mutex>()();
|
||||
}
|
||||
|
||||
void test_recursive_mutex()
|
||||
void test_timed_mutex()
|
||||
{
|
||||
timed_test(&do_test_timed_mutex, 3);
|
||||
}
|
||||
|
||||
void do_test_recursive_mutex()
|
||||
{
|
||||
test_lock<boost::recursive_mutex>()();
|
||||
test_recursive_lock<boost::recursive_mutex>()();
|
||||
}
|
||||
|
||||
void test_recursive_try_mutex()
|
||||
void test_recursive_mutex()
|
||||
{
|
||||
timed_test(&do_test_recursive_mutex, 3);
|
||||
}
|
||||
|
||||
void do_test_recursive_try_mutex()
|
||||
{
|
||||
test_lock<boost::recursive_try_mutex>()();
|
||||
test_trylock<boost::recursive_try_mutex>()();
|
||||
test_recursive_lock<boost::recursive_try_mutex>()();
|
||||
}
|
||||
|
||||
void test_recursive_timed_mutex()
|
||||
void test_recursive_try_mutex()
|
||||
{
|
||||
timed_test(&do_test_recursive_try_mutex, 3);
|
||||
}
|
||||
|
||||
void do_test_recursive_timed_mutex()
|
||||
{
|
||||
test_lock<boost::recursive_timed_mutex>()();
|
||||
test_trylock<boost::recursive_timed_mutex>()();
|
||||
@@ -214,6 +216,11 @@ void test_recursive_timed_mutex()
|
||||
test_recursive_lock<boost::recursive_timed_mutex>()();
|
||||
}
|
||||
|
||||
void test_recursive_timed_mutex()
|
||||
{
|
||||
timed_test(&do_test_recursive_timed_mutex, 3);
|
||||
}
|
||||
|
||||
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
|
||||
{
|
||||
boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: mutex test suite");
|
||||
|
||||
@@ -3,23 +3,22 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace
|
||||
#include "util.inl"
|
||||
|
||||
int once_value = 0;
|
||||
boost::once_flag once = BOOST_ONCE_INIT;
|
||||
|
||||
void init_once_value()
|
||||
{
|
||||
int once_value = 0;
|
||||
boost::once_flag once = BOOST_ONCE_INIT;
|
||||
|
||||
void init_once_value()
|
||||
{
|
||||
once_value++;
|
||||
}
|
||||
|
||||
void test_once_thread()
|
||||
{
|
||||
boost::call_once(init_once_value, once);
|
||||
}
|
||||
once_value++;
|
||||
}
|
||||
|
||||
void test_once()
|
||||
void test_once_thread()
|
||||
{
|
||||
boost::call_once(init_once_value, once);
|
||||
}
|
||||
|
||||
void do_test_once()
|
||||
{
|
||||
const int NUMTHREADS=5;
|
||||
boost::thread_group threads;
|
||||
@@ -29,6 +28,11 @@ void test_once()
|
||||
BOOST_CHECK_EQUAL(once_value, 1);
|
||||
}
|
||||
|
||||
void test_once()
|
||||
{
|
||||
timed_test(&do_test_once, 2);
|
||||
}
|
||||
|
||||
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
|
||||
{
|
||||
boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: once test suite");
|
||||
|
||||
@@ -3,66 +3,33 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace
|
||||
#define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_sleep_only
|
||||
#include "util.inl"
|
||||
|
||||
int test_value;
|
||||
|
||||
void simple_thread()
|
||||
{
|
||||
inline bool xtime_in_range(boost::xtime& xt, int less_seconds, int greater_seconds)
|
||||
{
|
||||
boost::xtime cur;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&cur, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
test_value = 999;
|
||||
}
|
||||
|
||||
boost::xtime less = cur;
|
||||
less.sec += less_seconds;
|
||||
|
||||
boost::xtime greater = cur;
|
||||
greater.sec += greater_seconds;
|
||||
|
||||
return (boost::xtime_cmp(xt, less) >= 0) && (boost::xtime_cmp(xt, greater) <= 0);
|
||||
}
|
||||
|
||||
int test_value;
|
||||
|
||||
void simple_thread()
|
||||
{
|
||||
test_value = 999;
|
||||
}
|
||||
|
||||
struct thread_adapter
|
||||
{
|
||||
thread_adapter(void (*func)(boost::thread& parent), boost::thread& parent)
|
||||
: func(func), parent(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()()
|
||||
{
|
||||
(*func)(parent);
|
||||
}
|
||||
|
||||
void (*func)(boost::thread& parent);
|
||||
boost::thread& parent;
|
||||
};
|
||||
|
||||
void comparison_thread(boost::thread& parent)
|
||||
{
|
||||
boost::thread thrd;
|
||||
BOOST_TEST(thrd != parent);
|
||||
BOOST_TEST(thrd == boost::thread());
|
||||
}
|
||||
void comparison_thread(boost::thread* parent)
|
||||
{
|
||||
boost::thread thrd;
|
||||
BOOST_TEST(thrd != *parent);
|
||||
BOOST_TEST(thrd == boost::thread());
|
||||
}
|
||||
|
||||
void test_sleep()
|
||||
{
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC), static_cast<int>(boost::TIME_UTC));
|
||||
xt.sec += 3;
|
||||
|
||||
boost::xtime xt = delay(3);
|
||||
boost::thread::sleep(xt);
|
||||
|
||||
// Insure it's in a range instead of checking actual equality due to time lapse
|
||||
BOOST_CHECK(xtime_in_range(xt, -1, 0));
|
||||
BOOST_CHECK(in_range(xt));
|
||||
}
|
||||
|
||||
void test_creation()
|
||||
void do_test_creation()
|
||||
{
|
||||
test_value = 0;
|
||||
boost::thread thrd(&simple_thread);
|
||||
@@ -70,13 +37,23 @@ void test_creation()
|
||||
BOOST_CHECK_EQUAL(test_value, 999);
|
||||
}
|
||||
|
||||
void test_comparison()
|
||||
void test_creation()
|
||||
{
|
||||
timed_test(&do_test_creation, 1);
|
||||
}
|
||||
|
||||
void do_test_comparison()
|
||||
{
|
||||
boost::thread self;
|
||||
boost::thread thrd(thread_adapter(comparison_thread, self));
|
||||
boost::thread thrd(bind(&comparison_thread, &self));
|
||||
thrd.join();
|
||||
}
|
||||
|
||||
void test_comparison()
|
||||
{
|
||||
timed_test(&do_test_comparison, 1);
|
||||
}
|
||||
|
||||
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
|
||||
{
|
||||
boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: thread test suite");
|
||||
|
||||
@@ -4,42 +4,41 @@
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
namespace
|
||||
#include "util.inl"
|
||||
|
||||
boost::mutex tss_mutex;
|
||||
int tss_instances = 0;
|
||||
|
||||
struct tss_value_t
|
||||
{
|
||||
boost::mutex tss_mutex;
|
||||
int tss_instances = 0;
|
||||
|
||||
struct tss_value_t
|
||||
tss_value_t()
|
||||
{
|
||||
tss_value_t()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(tss_mutex);
|
||||
++tss_instances;
|
||||
value = 0;
|
||||
}
|
||||
~tss_value_t()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(tss_mutex);
|
||||
--tss_instances;
|
||||
}
|
||||
int value;
|
||||
};
|
||||
|
||||
boost::thread_specific_ptr<tss_value_t> tss_value;
|
||||
|
||||
void test_tss_thread()
|
||||
boost::mutex::scoped_lock lock(tss_mutex);
|
||||
++tss_instances;
|
||||
value = 0;
|
||||
}
|
||||
~tss_value_t()
|
||||
{
|
||||
tss_value.reset(new tss_value_t());
|
||||
for (int i=0; i<1000; ++i)
|
||||
{
|
||||
int& n = tss_value->value;
|
||||
BOOST_CHECK_EQUAL(n, i);
|
||||
++n;
|
||||
}
|
||||
boost::mutex::scoped_lock lock(tss_mutex);
|
||||
--tss_instances;
|
||||
}
|
||||
int value;
|
||||
};
|
||||
|
||||
boost::thread_specific_ptr<tss_value_t> tss_value;
|
||||
|
||||
void test_tss_thread()
|
||||
{
|
||||
tss_value.reset(new tss_value_t());
|
||||
for (int i=0; i<1000; ++i)
|
||||
{
|
||||
int& n = tss_value->value;
|
||||
BOOST_CHECK_EQUAL(n, i);
|
||||
++n;
|
||||
}
|
||||
}
|
||||
|
||||
void test_tss()
|
||||
void do_test_tss()
|
||||
{
|
||||
const int NUMTHREADS=5;
|
||||
boost::thread_group threads;
|
||||
@@ -49,6 +48,11 @@ void test_tss()
|
||||
BOOST_CHECK_EQUAL(tss_instances, 0);
|
||||
}
|
||||
|
||||
void test_tss()
|
||||
{
|
||||
timed_test(&do_test_tss, 2);
|
||||
}
|
||||
|
||||
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
|
||||
{
|
||||
boost::unit_test_framework::test_suite* test = BOOST_TEST_SUITE("Boost.Threads: tss test suite");
|
||||
|
||||
150
test/util.inl
Normal file
150
test/util.inl
Normal file
@@ -0,0 +1,150 @@
|
||||
#if !defined(UTIL_INL_WEK01242003)
|
||||
#define UTIL_INL_WEK01242003
|
||||
|
||||
#include <boost/thread/xtime.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
|
||||
#ifndef DEFAULT_EXECUTION_MONITOR_TYPE
|
||||
# define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_condition
|
||||
#endif
|
||||
|
||||
namespace
|
||||
{
|
||||
inline boost::xtime delay(int secs, int msecs=0, int nsecs=0)
|
||||
{
|
||||
const int MILLISECONDS_PER_SECOND = 1000;
|
||||
const int NANOSECONDS_PER_SECOND = 1000000000;
|
||||
const int NANOSECONDS_PER_MILLISECOND = 1000000;
|
||||
|
||||
boost::xtime xt;
|
||||
BOOST_CHECK_EQUAL(boost::xtime_get(&xt, boost::TIME_UTC),
|
||||
static_cast<int>(boost::TIME_UTC));
|
||||
|
||||
nsecs += xt.nsec;
|
||||
msecs += nsecs / NANOSECONDS_PER_MILLISECOND;
|
||||
secs += msecs / MILLISECONDS_PER_SECOND;
|
||||
nsecs += (msecs % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND;
|
||||
xt.nsec = nsecs % NANOSECONDS_PER_SECOND;
|
||||
xt.sec += secs + (nsecs / NANOSECONDS_PER_SECOND);
|
||||
|
||||
return xt;
|
||||
}
|
||||
|
||||
inline bool in_range(const boost::xtime& xt, int secs=1)
|
||||
{
|
||||
boost::xtime min = delay(-secs);
|
||||
boost::xtime max = delay(0);
|
||||
return (boost::xtime_cmp(xt, min) >= 0) && (boost::xtime_cmp(xt, max) <= 0);
|
||||
}
|
||||
|
||||
class execution_monitor
|
||||
{
|
||||
public:
|
||||
enum wait_type { use_sleep_only, use_mutex, use_condition };
|
||||
|
||||
execution_monitor(wait_type type, int secs)
|
||||
: done(false), type(type), secs(secs) { }
|
||||
void start()
|
||||
{
|
||||
if (type != use_sleep_only) {
|
||||
boost::mutex::scoped_lock lock(mutex); done = false;
|
||||
} else {
|
||||
done = false;
|
||||
}
|
||||
}
|
||||
void finish()
|
||||
{
|
||||
if (type != use_sleep_only) {
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
done = true;
|
||||
if (type == use_condition)
|
||||
cond.notify_one();
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
bool wait()
|
||||
{
|
||||
boost::xtime xt = delay(secs);
|
||||
if (type != use_condition)
|
||||
boost::thread::sleep(xt);
|
||||
if (type != use_sleep_only) {
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
while (type == use_condition && !done) {
|
||||
if (!cond.timed_wait(lock, xt))
|
||||
break;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
return done;
|
||||
}
|
||||
|
||||
private:
|
||||
boost::mutex mutex;
|
||||
boost::condition cond;
|
||||
bool done;
|
||||
wait_type type;
|
||||
int secs;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
class indirect_adapter
|
||||
{
|
||||
public:
|
||||
indirect_adapter(F func, execution_monitor& monitor)
|
||||
: func(func), monitor(monitor) { }
|
||||
void operator()() const
|
||||
{
|
||||
try
|
||||
{
|
||||
boost::thread thrd(func);
|
||||
thrd.join();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
monitor.finish();
|
||||
throw;
|
||||
}
|
||||
monitor.finish();
|
||||
}
|
||||
|
||||
private:
|
||||
F func;
|
||||
execution_monitor& monitor;
|
||||
};
|
||||
|
||||
template <typename F>
|
||||
void timed_test(F func, int secs,
|
||||
execution_monitor::wait_type type=DEFAULT_EXECUTION_MONITOR_TYPE)
|
||||
{
|
||||
execution_monitor monitor(type, secs);
|
||||
indirect_adapter<F> ifunc(func, monitor);
|
||||
monitor.start();
|
||||
boost::thread thrd(ifunc);
|
||||
BOOST_REQUIRE_MESSAGE(monitor.wait(),
|
||||
"Timed test didn't complete in time, possible deadlock.");
|
||||
}
|
||||
|
||||
template <typename F, typename T>
|
||||
class binder
|
||||
{
|
||||
public:
|
||||
binder(const F& func, const T& param)
|
||||
: func(func), param(param) { }
|
||||
void operator()() const { func(param); }
|
||||
|
||||
private:
|
||||
F func;
|
||||
T param;
|
||||
};
|
||||
|
||||
template <typename F, typename T>
|
||||
binder<F, T> bind(const F& func, const T& param)
|
||||
{
|
||||
return binder<F, T>(func, param);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#endif
|
||||
@@ -26,5 +26,10 @@ template tutorial
|
||||
exe helloworld : <template>tutorial helloworld.cpp ;
|
||||
exe helloworld2 : <template>tutorial helloworld2.cpp ;
|
||||
exe helloworld3 : <template>tutorial helloworld3.cpp ;
|
||||
exe helloworld4 : <template>tutorial helloworld4.cpp ;
|
||||
exe factorial : <template>tutorial factorial.cpp ;
|
||||
exe factorial2 : <template>tutorial factorial2.cpp ;
|
||||
exe factorial3 : <template>tutorial factorial3.cpp ;
|
||||
exe counter : <template>tutorial counter.cpp ;
|
||||
exe bounded_buffer : <template>tutorial bounded_buffer.cpp ;
|
||||
exe once : <template>tutorial once.cpp ;
|
||||
|
||||
63
tutorial/bounded_buffer.cpp
Normal file
63
tutorial/bounded_buffer.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include <boost/thread/condition.hpp>
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
|
||||
class bounded_buffer : private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
typedef boost::mutex::scoped_lock lock;
|
||||
bounded_buffer(int n) : begin(0), end(0), buffered(0), circular_buf(n) { }
|
||||
void send (int m) {
|
||||
lock lk(monitor);
|
||||
while (buffered == circular_buf.size())
|
||||
buffer_not_full.wait(lk);
|
||||
circular_buf[end] = m;
|
||||
end = (end+1) % circular_buf.size();
|
||||
++buffered;
|
||||
buffer_not_empty.notify_one();
|
||||
}
|
||||
int receive() {
|
||||
lock lk(monitor);
|
||||
while (buffered == 0)
|
||||
buffer_not_empty.wait(lk);
|
||||
int i = circular_buf[begin];
|
||||
begin = (begin+1) % circular_buf.size();
|
||||
--buffered;
|
||||
buffer_not_full.notify_one();
|
||||
return i;
|
||||
}
|
||||
private:
|
||||
int begin, end, buffered;
|
||||
std::vector<int> circular_buf;
|
||||
boost::condition buffer_not_full, buffer_not_empty;
|
||||
boost::mutex monitor;
|
||||
};
|
||||
bounded_buffer buf(2);
|
||||
|
||||
void sender() {
|
||||
int n = 0;
|
||||
while (n < 100) {
|
||||
buf.send(n);
|
||||
std::cout << "sent: " << n << std::endl;
|
||||
++n;
|
||||
}
|
||||
buf.send(-1);
|
||||
}
|
||||
|
||||
void receiver() {
|
||||
int n;
|
||||
do {
|
||||
n = buf.receive();
|
||||
std::cout << "received: " << n << std::endl;
|
||||
} while (n != -1); // -1 indicates end of buffer
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::thread thrd1(&sender);
|
||||
boost::thread thrd2(&receiver);
|
||||
thrd1.join();
|
||||
thrd2.join();
|
||||
}
|
||||
22
tutorial/counter.cpp
Normal file
22
tutorial/counter.cpp
Normal file
@@ -0,0 +1,22 @@
|
||||
#include <boost/thread/mutex.hpp>
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <iostream>
|
||||
|
||||
boost::mutex mutex;
|
||||
int counter=0;
|
||||
|
||||
void change_count()
|
||||
{
|
||||
boost::mutex::scoped_lock lock(mutex);
|
||||
int i = ++counter;
|
||||
std::cout << "count == " << i << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
const int num_threads = 4;
|
||||
boost::thread_group thrds;
|
||||
for (int i=0; i < num_threads; ++i)
|
||||
thrds.create_thread(&change_count);
|
||||
thrds.join_all();
|
||||
}
|
||||
30
tutorial/factorial3.cpp
Normal file
30
tutorial/factorial3.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <iostream>
|
||||
|
||||
const int NUM_CALCS=5;
|
||||
|
||||
class factorial
|
||||
{
|
||||
public:
|
||||
factorial(int x, int& res) : x(x), res(res) { }
|
||||
void operator()() { res = calculate(x); }
|
||||
int result() const { return res; }
|
||||
|
||||
private:
|
||||
int calculate(int x) { return x <= 1 ? 1 : x * calculate(x-1); }
|
||||
|
||||
private:
|
||||
int x;
|
||||
int& res;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
int results[NUM_CALCS];
|
||||
boost::thread_group thrds;
|
||||
for (int i=0; i < NUM_CALCS; ++i)
|
||||
thrds.create_thread(factorial(i*10, results[i]));
|
||||
thrds.join_all();
|
||||
for (int i=0; i < NUM_CALCS; ++i)
|
||||
std::cout << i*10 << "! = " << results[i] << std::endl;
|
||||
}
|
||||
@@ -3,13 +3,12 @@
|
||||
|
||||
struct helloworld
|
||||
{
|
||||
helloworld(const char* who) : m_who(who) { }
|
||||
void operator()() { std::cout << m_who << "says, \"Hello World.\"" << std::endl; }
|
||||
const char* m_who;
|
||||
helloworld() { }
|
||||
void operator()() { std::cout << "Hello World." << std::endl; }
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::thread thrd(helloworld("Bob"));
|
||||
boost::thread thrd(helloworld());
|
||||
thrd.join();
|
||||
}
|
||||
@@ -1,14 +1,15 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <iostream>
|
||||
|
||||
void helloworld(const char* who)
|
||||
struct helloworld
|
||||
{
|
||||
std::cout << who << "says, \"Hello World.\"" << std::endl;
|
||||
}
|
||||
helloworld(const char* who) : m_who(who) { }
|
||||
void operator()() { std::cout << m_who << "says, \"Hello World.\"" << std::endl; }
|
||||
const char* m_who;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::thread thrd(boost::bind(&helloworld, "Bob"));
|
||||
boost::thread thrd(helloworld("Bob"));
|
||||
thrd.join();
|
||||
}
|
||||
14
tutorial/helloworld4.cpp
Normal file
14
tutorial/helloworld4.cpp
Normal file
@@ -0,0 +1,14 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
#include <iostream>
|
||||
|
||||
void helloworld(const char* who)
|
||||
{
|
||||
std::cout << who << "says, \"Hello World.\"" << std::endl;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
boost::thread thrd(boost::bind(&helloworld, "Bob"));
|
||||
thrd.join();
|
||||
}
|
||||
25
tutorial/once.cpp
Normal file
25
tutorial/once.cpp
Normal file
@@ -0,0 +1,25 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/once.hpp>
|
||||
#include <cassert>
|
||||
|
||||
int value=0;
|
||||
boost::once_flag once = BOOST_ONCE_INIT;
|
||||
|
||||
void init()
|
||||
{
|
||||
++value;
|
||||
}
|
||||
|
||||
void thread_proc()
|
||||
{
|
||||
boost::call_once(&init, once);
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
boost::thread_group threads;
|
||||
for (int i=0; i<5; ++i)
|
||||
threads.create_thread(&thread_proc);
|
||||
threads.join_all();
|
||||
assert(value == 1);
|
||||
}
|
||||
30
tutorial/tss.cpp
Normal file
30
tutorial/tss.cpp
Normal file
@@ -0,0 +1,30 @@
|
||||
#include <boost/thread/thread.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
#include <cassert>
|
||||
|
||||
boost::thread_specific_ptr<int> value;
|
||||
|
||||
void increment()
|
||||
{
|
||||
int* p = value.get();
|
||||
++*p;
|
||||
}
|
||||
|
||||
void thread_proc()
|
||||
{
|
||||
value.reset(new int(0)); // initialize the thread's storage
|
||||
for (int i=0; i<10; ++i)
|
||||
{
|
||||
increment();
|
||||
int* p = value.get();
|
||||
assert(*p == i+1);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
boost::thread_group threads;
|
||||
for (int i=0; i<5; ++i)
|
||||
threads.create_thread(&thread_proc);
|
||||
threads.join_all();
|
||||
}
|
||||
Reference in New Issue
Block a user