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

Compare commits

..

24 Commits

Author SHA1 Message Date
nobody
57ed9fd6e7 This commit was manufactured by cvs2svn to create tag 'thread_base'.
[SVN r17054]
2003-01-27 22:44:07 +00:00
William E. Kempf
125193dcfa Turned all tests into 'timed_test's.
[SVN r17053]
2003-01-27 22:44:06 +00:00
Beman Dawes
77efa9810d Local include util.inl
[SVN r17041]
2003-01-25 14:23:36 +00:00
William E. Kempf
7f03d1917b Removed accidental sleep
[SVN r17035]
2003-01-24 18:15:22 +00:00
William E. Kempf
55b4ca9350 Added 'timed_test' support to prevent deadlocks in tests
[SVN r17032]
2003-01-24 16:51:13 +00:00
William E. Kempf
7196a0f9d2 Updated build files
[SVN r17021]
2003-01-23 23:23:36 +00:00
William E. Kempf
2caabde5ca Updated Jamfile to use test-suite
[SVN r17014]
2003-01-23 19:47:29 +00:00
Vladimir Prus
137d7663c1 Change use-requirements to usage-requirements in some library Jamfiles.
[SVN r16884]
2003-01-13 12:31:12 +00:00
Vladimir Prus
508b71a921 Update V2 Jamfile for Boost.
[SVN r16882]
2003-01-13 10:05:01 +00:00
Beman Dawes
5d90820005 fix bookmarks
[SVN r16827]
2003-01-09 13:37:41 +00:00
Beman Dawes
84727e90b1 fix invalid bookmarks
[SVN r16825]
2003-01-09 13:22:04 +00:00
William E. Kempf
9a1e3d3320 Added <boost/thread.hpp>.
[SVN r16796]
2003-01-08 15:08:32 +00:00
William E. Kempf
d33e0c8ee1 Fixed Unicode problem (CreateMutex - CreateMutexA)
[SVN r16770]
2003-01-06 16:06:37 +00:00
William E. Kempf
3332649480 Fixed bug in to_duration.
[SVN r16769]
2003-01-06 15:55:57 +00:00
William E. Kempf
c918b66199 Switched to a DLL implementation
[SVN r16742]
2003-01-03 21:03:43 +00:00
William E. Kempf
dbbf56e17a Updated to new test library name
[SVN r16732]
2003-01-02 16:26:03 +00:00
William E. Kempf
c77500c15a Updated Jamfile to remove unused code
[SVN r16725]
2002-12-30 23:14:30 +00:00
Dave Abrahams
75084aaa96 HP aCC workarounds
[SVN r16518]
2002-12-04 17:56:59 +00:00
William E. Kempf
35714c8f1c Added new tutorials
[SVN r16007]
2002-10-28 14:17:49 +00:00
William E. Kempf
3699cc97a6 Added new tutorial files
[SVN r16005]
2002-10-28 14:10:14 +00:00
Vladimir Prus
5a7377acda Made Boost.Thread compile with V2.
[SVN r15969]
2002-10-23 13:22:56 +00:00
Beman Dawes
6aaee629b5 Fix acknowledgements and copyright
[SVN r15759]
2002-10-07 00:07:46 +00:00
Björn Karlsson
b465fe569c Merged from branch to trunk
[SVN r15613]
2002-10-01 15:00:37 +00:00
Björn Karlsson
5e6f72a688 Merged from branch to trunk
[SVN r15524]
2002-09-26 09:17:19 +00:00
44 changed files with 958 additions and 604 deletions

View File

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

View File

@@ -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 ##
:
;
}

View File

@@ -61,10 +61,9 @@
states <a href="definitions.html#state"> blocked</a> and <a href="definitions.html#state">ready</a>.
Note that &quot;waiting&quot; 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 &lt;typename ScopedLock&gt;
<pre>template &lt;typename ScopedLock&gt;
void wait(ScopedLock&amp; lock);
</pre>
<dl class="function-semantics">
@@ -138,8 +132,7 @@ template &lt;typename ScopedLock&gt;
wake ups&quot;. The second version encapsulates this loop idiom internally
and is generally the preferred method.</dt>
</dl>
<pre>
Template&lt;typename ScopedLock, typename Pr&gt;
<pre>Template&lt;typename ScopedLock, typename Pr&gt;
void wait(ScopedLock&amp; lock, Pr pred);
</pre>
<dl class="function-semantics">
@@ -149,8 +142,7 @@ Template&lt;typename ScopedLock, typename Pr&gt;
<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 &lt;typename ScopedLock&gt;
<pre>template &lt;typename ScopedLock&gt;
bool timed_wait(ScopedLock&amp; lock, const <a href="xtime.html">xtime</a>&amp; XT);
</pre>
<dl class="function-semantics">
@@ -171,16 +163,14 @@ template &lt;typename ScopedLock&gt;
ups&quot;. The second version encapsulates this loop idiom internally and
is generally the preferred method.</dt>
</dl>
<pre>
Template&lt;typename ScopedLock, typename Pr&gt;
<pre>Template&lt;typename ScopedLock, typename Pr&gt;
bool timed_wait(ScopedLock&amp; lock, const <a href="xtime.html">xtime</a>&amp; 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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -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&#39;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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -144,31 +144,27 @@
appear to bear them out. To illustrate the analysis we&#39;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(&amp;bar);
}
</pre>
<h3>2. Creation of a thread that's later joined.</h3>
<pre>
Void foo()
<pre>Void foo()
{
thread = create_thread(&amp;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&lt;NUM_THREADS; ++i)
create_thread(&amp;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&lt;NUM_THREADS; ++i)
threads[i] = create_thread(&amp;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(&amp;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(&amp;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&#39;ll code all six usage patterns using both designs.</p>
<h3>1.</h3>
<pre>
void foo()
<pre>void foo()
{
thread thrd(&amp;bar);
}
@@ -223,8 +216,7 @@ void foo()
}
</pre>
<h3>2.</h3>
<pre>
void foo()
<pre>void foo()
{
thread thrd(&amp;bar);
thrd.join();
@@ -237,8 +229,7 @@ void foo()
}
</pre>
<h3>3.</h3>
<pre>
void foo()
<pre>void foo()
{
for (int i=0; i&lt;NUM_THREADS; ++i)
thread thrd(&amp;bar);
@@ -251,8 +242,7 @@ void foo()
}
</pre>
<h3>4.</h3>
<pre>
void foo()
<pre>void foo()
{
std::auto_ptr&lt;thread&gt; threads[NUM_THREADS];
for (int i=0; i&lt;NUM_THREADS; ++i)
@@ -271,8 +261,7 @@ void foo()
}
</pre>
<h3>5.</h3>
<pre>
void foo()
<pre>void foo()
{
thread thrd* = new thread(&amp;bar);
manager.owns(thread);
@@ -285,8 +274,7 @@ void foo()
}
</pre>
<h3>6.</h3>
<pre>
void foo()
<pre>void foo()
{
boost::shared_ptr&lt;thread&gt; thrd(new thread(&amp;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&lt;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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -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>&lt;void&gt;&amp; threadfunc);
<pre>thread(const <a href="../../function/index.html">boost::function0</a>&lt;void&gt;&amp; 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>&lt;void&gt
<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>&lt;void&gt
</dl>
<h4><a name="class-thread-comparisons"></a>Class <code>thread</code> comparison
functions</h4>
<pre>
bool operator==(const thread&amp; rhs) const;
<pre>bool operator==(const thread&amp; 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&amp; rhs) const;
<pre>bool operator!=(const thread&amp; 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>&amp; XT);
<pre>static void sleep(const <a href="xtime.html">xtime</a>&amp; 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 &quot;ready&quot;
@@ -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&lt;void&gt;&amp; threadfunc);
<pre>thread* create_thread(const boost::function0&lt;void&gt;&amp; 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&lt;void&gt;&amp; 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&#39;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>&#39;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>&#39;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>&copy; 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 &quot;as is&quot; without express or implied warranty.</p>
</body>
</html>
</html>

View File

@@ -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
View File

@@ -0,0 +1,5 @@
exe starvephil
: starvephil.cpp ../build/boost_thread ../../test/build/unit_test_framework
;

View File

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

View 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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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
View 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
View 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;
}

View File

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

View File

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