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

Compare commits

...

32 Commits

Author SHA1 Message Date
nobody
38592947ba This commit was manufactured by cvs2svn to create tag
'Version_1_33_0'.

[SVN r30532]
2005-08-12 03:25:34 +00:00
Douglas Gregor
9ca3c5b13f Be slightly more forgiving about timing
[SVN r30483]
2005-08-05 13:17:42 +00:00
nobody
7445f79dd4 This commit was manufactured by cvs2svn to create branch 'RC_1_33_0'.
[SVN r30300]
2005-07-28 18:22:24 +00:00
Douglas Gregor
aa240e61d9 Clear up broken links, tabs, etc
[SVN r30077]
2005-07-14 15:47:04 +00:00
Douglas Gregor
2954e932ce Fix test_read_write_mutex, from Tobias Schwinger
[SVN r29988]
2005-07-11 15:06:09 +00:00
Douglas Gregor
5be79cc858 Unbolidify Threads title
[SVN r29938]
2005-07-07 18:06:26 +00:00
Martin Wille
4a9d97d22d -- made the initialization order for the members of
boost::detail::thread::read_write_mutex_impl
   consistent with the declaration.


[SVN r29913]
2005-07-06 21:36:56 +00:00
Martin Wille
f4f3433854 -- fixed order of member initialization
[SVN r29881]
2005-07-03 16:51:35 +00:00
Michael Glassford
26bffa3740 Rename binder to eliminate name conflicts with type traits.
[SVN r29229]
2005-05-26 14:12:41 +00:00
Michael Glassford
69e52a9882 Fix CW 8.3 warnings.
[SVN r28638]
2005-05-03 20:42:09 +00:00
Michael Glassford
cc8de48849 Commit Caleb Epstein patch to fix errors due to Boost.Test not being thread safe.
[SVN r28537]
2005-04-29 20:38:46 +00:00
Michael Glassford
9d7c119f94 Fixed CodeWarrior 8.3 warnings.
[SVN r28497]
2005-04-27 01:22:12 +00:00
Vladimir Prus
6ba9fd1b60 Sync with V1
[SVN r28369]
2005-04-21 08:17:12 +00:00
Michael Glassford
fb6250eb94 Completely rewrite tests to eliminate most calls to Sleep(), which was making the tests take far too long.
[SVN r28331]
2005-04-19 20:52:08 +00:00
Michael Glassford
bc73368c96 Remove line that could cause unnecessary extra waiting.
[SVN r28330]
2005-04-19 20:49:59 +00:00
Michael Glassford
3068f0c62c Fix undefined BOOST_READ_WRITE_MUTEX_TRACE in non-BOOST_HAS_WINTHREADS builds.
[SVN r27924]
2005-04-02 04:31:10 +00:00
Michael Glassford
8e00803c83 Don't include explicit instantiations in release builds (they increase the code size significantly).
[SVN r27887]
2005-03-30 16:59:07 +00:00
Michael Glassford
087b69b629 Fix build errors on some Win32 machines.
[SVN r27884]
2005-03-30 14:51:09 +00:00
Michael Glassford
3b237267fb Added more debugging aids, assertions. Corrected errors, including hangs, at the cost or making the read-write mutex even larger. The whole read-write mutex is too complicated; the design needs to be revisited.
[SVN r27874]
2005-03-29 21:46:38 +00:00
Michael Glassford
b9dbb1ed45 Expand read_write_mutex regression tests.
[SVN r27873]
2005-03-29 21:23:39 +00:00
Michael Glassford
41d3b29ec0 Add assert() at beginning of thread::join().
[SVN r27868]
2005-03-29 16:19:32 +00:00
Michael Glassford
05ceb8b1e2 Fix Sourceforge issue #828980, "comparisons in libs/thread/src/timeconv.inl".
[SVN r27867]
2005-03-29 15:43:00 +00:00
Michael Glassford
80d3925b8d Remove obsolete header.
[SVN r27718]
2005-03-17 02:03:31 +00:00
Michael Glassford
2cd6cbeacc Change assert() in recursive_try_mutex::do_trylock() to handle the case of pthread_mutex_trylock returning EBUSY.
[SVN r27717]
2005-03-17 01:55:44 +00:00
Michael Glassford
6382846f6c Add thread_group::size() method.
[SVN r27716]
2005-03-17 01:46:11 +00:00
Stefan Slapeta
349d0fd74b replaced BOOST_TEST
[SVN r27054]
2005-02-03 13:48:49 +00:00
Beman Dawes
9c88855bf4 correct date (Andreas Wachowski)
[SVN r26826]
2005-01-24 01:51:16 +00:00
Roland Schwarz
f0e6cdfcb5 ptw32 can be used again
[SVN r26802]
2005-01-22 13:43:48 +00:00
Roland Schwarz
af9864a1b5 Making use of BOOST_HAS_DECLSPEC macro
[SVN r26801]
2005-01-22 13:22:29 +00:00
Stefan Slapeta
8ac145e667 removed requirement of static rt for static lib
[SVN r26480]
2004-12-09 10:42:09 +00:00
Michael Glassford
39f7afc7d0 Reword comment to make its meaning clearer.
[SVN r26453]
2004-12-06 21:00:16 +00:00
Aleksey Gurtovoy
113b974bb7 merge RC_1_32_0 fixes
[SVN r26333]
2004-11-28 04:44:21 +00:00
21 changed files with 1714 additions and 656 deletions

View File

@@ -46,7 +46,6 @@ import ./threads ;
: ## requirements ##
<sysinclude>$(BOOST_ROOT) #:should be unnecessary (because already included in thread_base)
<define>BOOST_THREAD_BUILD_LIB=1
<runtime-link>static
# the common names rule ensures that the library will
# be named according to the rules used by the install
# and auto-link features:
@@ -102,7 +101,8 @@ import ./threads ;
<template>boost_thread_lib_base
: ## requirements ##
<define>BOOST_THREAD_LIB_NAME=$(boost_thread_lib_name_ptw32)
$(pthreads-win32)
$(boost_thread_lib_settings_ptw32)
: ## default build ##
;
dll $(boost_thread_lib_name_ptw32)
@@ -110,7 +110,8 @@ import ./threads ;
<template>boost_thread_dll_base
: ## requirements ##
<define>BOOST_THREAD_LIB_NAME=$(boost_thread_lib_name_ptw32)
$(pthreads-win32)
$(boost_thread_lib_settings_ptw32)
: ## default build ##
;
stage bin-stage

View File

@@ -54,7 +54,7 @@ last-revision="$Date$">
<title>ACM Computing Surveys</title>
<volumenum>Vol. 5</volumenum>
<issuenum>No. 4</issuenum>
<date>December, 1983</date>
<date>December, 1973</date>
</biblioset>
<biblioset relation="article">
<author>0-201-63392-2

View File

@@ -195,7 +195,7 @@
and constructed for the Mutex object.</para>
<para>A Mutex is
<ulink url="../../utility/utility.htm#Class%20noncopyable">
<ulink url="../../libs/utility/utility.htm#Class%20noncopyable">
NonCopyable</ulink>.</para>
<para>For a Mutex type <code>M</code>
and an object <code>m</code> of that type,
@@ -1180,7 +1180,7 @@
the specified time, if any). A
read-lock will be granted to all pending readers
before any other thread can acquire a write-lock.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1188,7 +1188,7 @@
<entry>write-lock</entry>
<entry>Grant the write-lock immediately, if and
only if there are no pending read-lock requests.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1200,7 +1200,7 @@
for read-locks exist. If other write-lock
requests exist, the lock is granted in accordance
with the intra-class scheduling policy.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1212,18 +1212,18 @@
for read-locks exist. If other write-lock
requests exist, the lock is granted in accordance
with the intra-class scheduling policy.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
<entry>read-locked</entry>
<entry>promote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
<row>
<entry>write-locked</entry>
<entry>demote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
</tbody>
</tgroup>
@@ -1261,7 +1261,7 @@
<entry>Grant the additional read-lock immediately,
<emphasis role="bold">IF</emphasis> no outstanding
requests for a write-lock exist; otherwise TODO.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1272,7 +1272,7 @@
releases its lock. The read lock will be granted
once no other outstanding write-lock requests
exist.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1289,7 +1289,7 @@
is granted in accordance with the intra-class
scheduling policy. This request will be granted
before any new read-lock requests are granted.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1301,18 +1301,18 @@
granted in accordance with the intra-class
scheduling policy. This request will be granted
before any new read-lock requests are granted.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
<entry>read-locked</entry>
<entry>promote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
<row>
<entry>write-locked</entry>
<entry>demote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
</tbody>
</tgroup>
@@ -1355,7 +1355,7 @@
write-locks is granted and released. If other
read-lock requests exist, all read-locks will be
granted as a group.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1366,7 +1366,7 @@
outstanding write-lock requests exist, they will
have to wait until all current read-lock requests
are serviced.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1385,7 +1385,7 @@
lock will be granted to one of them in accordance
with the intra-class scheduling policy.</para>
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1399,18 +1399,18 @@
released. If other write-lock requests exist,
this lock will be granted in accordance with the
intra-class scheduling policy.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
<entry>read-locked</entry>
<entry>promote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
<row>
<entry>write-locked</entry>
<entry>demote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
</tbody>
</tgroup>
@@ -1451,7 +1451,7 @@
write-lock requests exist, this lock will not
be granted until at least one of the write-locks
is granted and released.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1464,7 +1464,7 @@
exist, exactly one read-lock request will be
granted before the next write-lock is granted.
</para>
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
@@ -1485,7 +1485,7 @@
in accordance with the intra-class
scheduling policy.</para></entry>
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</row>
<row>
<entry>write-locked</entry>
@@ -1498,18 +1498,18 @@
released. If other write-lock requests exist,
this lock will be granted in accordance with
the intra-class scheduling policy.
<p>TODO: try-lock, timed-lock.</p>
<para>TODO: try-lock, timed-lock.</para>
</entry>
</row>
<row>
<entry>read-locked</entry>
<entry>promote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
<row>
<entry>write-locked</entry>
<entry>demote</entry>
<entry><p>TODO</p></entry>
<entry><para>TODO</para></entry>
</row>
</tbody>
</tgroup>
@@ -1553,7 +1553,7 @@
requirements and constructed for the ReadWriteMutex object.</para>
<para>A ReadWriteMutex is
<ulink url="../../utility/utility.htm#Class%20noncopyable">NonCopyable</ulink>.
<ulink url="../../libs/utility/utility.htm#Class%20noncopyable">NonCopyable</ulink>.
</para>
<para>For a ReadWriteMutex type <code>M</code>,

8
doc/index.html Normal file
View File

@@ -0,0 +1,8 @@
<html>
<head>
<meta http-equiv="refresh" content="0; URL=../../../doc/html/threads.html">
</head>
<body>
Automatic redirection failed, please go to <a href="../../../doc/html/threads.html">../../../doc/html/threads.html</a>
</body>
</html>

View File

@@ -32,7 +32,7 @@ xmlns:xi="http://www.w3.org/2001/XInclude">
<librarycategory name="category:concurrent" />
<title>Boost.Threads</title>
</libraryinfo>
<title>&Boost.Threads;</title>
<title>Boost.Threads</title>
<xi:include href="overview.xml"/>
<xi:include href="design.xml"/>
<xi:include href="concepts.xml"/>

View File

@@ -17,54 +17,50 @@
// insist on threading support being available:
#include <boost/config/requires_threads.hpp>
#if defined(BOOST_HAS_WINTHREADS)
# if defined(BOOST_THREAD_BUILD_DLL) //Build dll
# define BOOST_THREAD_DECL __declspec(dllexport)
# elif defined(BOOST_THREAD_BUILD_LIB) //Build lib
# define BOOST_THREAD_DECL
# elif defined(BOOST_THREAD_USE_DLL) //Use dll
# define BOOST_THREAD_DECL __declspec(dllimport)
# define BOOST_DYN_LINK
# elif defined(BOOST_THREAD_USE_LIB) //Use lib
# define BOOST_THREAD_DECL
# else //Use default
#if defined(BOOST_THREAD_BUILD_DLL) //Build dll
#elif defined(BOOST_THREAD_BUILD_LIB) //Build lib
#elif defined(BOOST_THREAD_USE_DLL) //Use dll
#elif defined(BOOST_THREAD_USE_LIB) //Use lib
#else //Use default
# if defined(BOOST_HAS_WINTHREADS)
# if defined(BOOST_MSVC) || defined(BOOST_INTEL_WIN)
//For VC++, choose according to threading library setting
# if defined(_DLL)
//Threading library is dll: use Boost.Threads dll
# define BOOST_THREAD_USE_DLL
# define BOOST_THREAD_DECL __declspec(dllimport)
# define BOOST_DYN_LINK
# else
//Threading library is lib: used Boost.Threads lib
# define BOOST_THREAD_USE_LIB
# define BOOST_THREAD_DECL
# endif
//For compilers supporting auto-tss cleanup
//with Boost.Threads lib, use Boost.Threads lib
# define BOOST_THREAD_USE_LIB
# else
//For compilers not yet supporting auto-tss cleanup
//with Boost.Threads lib, use Boost.Threads dll
# define BOOST_THREAD_USE_DLL
# define BOOST_THREAD_DECL __declspec(dllimport)
# define BOOST_DYN_LINK
# endif
# else
# define BOOST_THREAD_USE_LIB
# endif
#endif
#if defined(BOOST_HAS_DECLSPEC)
# if defined(BOOST_THREAD_BUILD_DLL) //Build dll
# define BOOST_THREAD_DECL __declspec(dllexport)
# elif defined(BOOST_THREAD_USE_DLL) //Use dll
# define BOOST_THREAD_DECL __declspec(dllimport)
# else
# define BOOST_THREAD_DECL
# endif
#else
# define BOOST_THREAD_DECL
# if defined(BOOST_THREAD_USE_LIB) //Use dll
# define BOOST_THREAD_USE_DLL
# define BOOST_DYN_LINK
# elif defined(BOOST_THREAD_USE_DLL) //Use lib
# define BOOST_THREAD_USE_LIB
# else //Use default
# define BOOST_THREAD_USE_LIB
# endif
#endif // BOOST_HAS_WINTHREADS
#endif // BOOST_HAS_DECLSPEC
//
// Automatically link to the correct build variant where possible.
//
#if !defined(BOOST_ALL_NO_LIB) && !defined(BOOST_THREAD_NO_LIB) && !defined(BOOST_THREAD_BUILD_DLL) && !defined(BOOST_THREAD_BUILD_LIB)
//
// Tell the autolink to link dynamically, this will get undef'ed by auto_link.hpp
// once it's done with it:
//
#if defined(BOOST_THREAD_USE_DLL)
# define BOOST_DYN_LINK
#endif
//
// Set the name of our library, this will get undef'ed by auto_link.hpp
// once it's done with it:
//
@@ -82,3 +78,9 @@
#endif // auto-linking disabled
#endif // BOOST_THREAD_CONFIG_WEK1032003_HPP
// Change Log:
// 22 Jan 05 Roland Schwarz (speedsnail)
// Usage of BOOST_HAS_DECLSPEC macro.
// Default again is static lib usage.
// BOOST_DYN_LINK only defined when autolink included.

View File

@@ -1,5 +1,5 @@
// Copyright (C) 2002-2003
// David Moore
// David Moore, Michael Glassford
//
// Original scoped_lock implementation
// Copyright (C) 2001
@@ -174,7 +174,7 @@ public:
m_state = read_write_lock_state::write_locked;
}
//If allow_unlock = true, set_lock always succeedes and
//If allow_unlock = true, set_lock always succeeds and
//the function result indicates whether an unlock was required.
//If allow_unlock = false, set_lock may fail;
//the function result indicates whether it succeeded.
@@ -448,7 +448,7 @@ public:
return read_write_lock_ops<TryReadWriteMutex>::try_promote(m_mutex) ? (m_state = read_write_lock_state::write_locked, true) : false;
}
//If allow_unlock = true, set_lock always succeedes and
//If allow_unlock = true, set_lock always succeeds and
//the function result indicates whether an unlock was required.
//If allow_unlock = false, set_lock may fail;
//the function result indicates whether it succeeded.
@@ -806,7 +806,7 @@ public:
return read_write_lock_ops<TimedReadWriteMutex>::timed_promote(m_mutex, xt) ? (m_state = read_write_lock_state::write_locked, true) : false;
}
//If allow_unlock = true, set_lock always succeedes and
//If allow_unlock = true, set_lock always succeeds and
//the function result indicates whether an unlock was required.
//If allow_unlock = false, set_lock may fail;
//the function result indicates whether it succeeded.

View File

@@ -1,5 +1,5 @@
// Copyright (C) 2002-2003
// David Moore, William E. Kempf
// David Moore, William E. Kempf, Michael Glassford
//
// Permission to use, copy, modify, distribute and sell this software
// and its documentation for any purpose is hereby granted without fee,
@@ -19,6 +19,7 @@
#include <boost/thread/detail/config.hpp>
#include <boost/utility.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/detail/lock.hpp>
@@ -33,7 +34,7 @@ namespace read_write_scheduling_policy {
writer_priority, //Prefer writers; can starve readers
reader_priority, //Prefer readers; can starve writers
alternating_many_reads, //Alternate readers and writers; before a writer, release all queued readers
alternating_single_read //Alternate readers and writers; before a writer, release only on queued reader
alternating_single_read //Alternate readers and writers; before a writer, release only one queued reader
};
} // namespace read_write_scheduling_policy
@@ -51,27 +52,28 @@ struct read_write_mutex_impl
typedef detail::thread::scoped_try_lock<Mutex> scoped_try_lock;
typedef detail::thread::scoped_timed_lock<Mutex> scoped_timed_lock;
read_write_mutex_impl(read_write_scheduling_policy::read_write_scheduling_policy_enum sp)
: m_num_waiting_writers(0),
m_num_waiting_readers(0),
m_num_readers_to_wake(0),
m_state_waiting_promotion(false),
m_state(0),
m_sp(sp),
m_readers_next(true) { }
read_write_mutex_impl(read_write_scheduling_policy::read_write_scheduling_policy_enum sp);
#if !BOOST_WORKAROUND(__BORLANDC__,<= 0x564)
~read_write_mutex_impl();
#endif
Mutex m_prot;
const read_write_scheduling_policy::read_write_scheduling_policy_enum m_sp;
int m_state; //-1 = write lock; 0 = unlocked; >0 = read locked
boost::condition m_waiting_writers;
boost::condition m_waiting_readers;
boost::condition m_waiting_promotion;
int m_num_waiting_writers;
int m_num_waiting_readers;
int m_num_readers_to_wake;
boost::condition m_waiting_promotion;
bool m_state_waiting_promotion;
int m_state; // -1 = excl locked
// 0 = unlocked
// 1-> INT_MAX - shared locked
const read_write_scheduling_policy::read_write_scheduling_policy_enum m_sp;
int m_num_waking_writers;
int m_num_waking_readers;
int m_num_max_waking_writers; //Debug only
int m_num_max_waking_readers; //Debug only
bool m_readers_next;
void do_read_lock();
@@ -96,12 +98,20 @@ struct read_write_mutex_impl
private:
void do_unlock_scheduling_impl();
void do_timeout_scheduling_impl();
void do_demote_scheduling_impl();
void do_scheduling_impl();
bool do_demote_to_read_lock_impl();
enum scheduling_reason
{
scheduling_reason_unlock,
scheduling_reason_timeout,
scheduling_reason_demote
};
void do_scheduling_impl(const scheduling_reason reason);
bool do_wake_one_reader(void);
bool do_wake_all_readers(void);
bool do_wake_writer(void);
bool waker_exists(void);
};
} // namespace detail
@@ -112,8 +122,8 @@ class BOOST_THREAD_DECL read_write_mutex : private noncopyable
{
public:
read_write_mutex(read_write_scheduling_policy::read_write_scheduling_policy_enum sp) : m_impl(sp) { }
~read_write_mutex() { }
read_write_mutex(read_write_scheduling_policy::read_write_scheduling_policy_enum sp);
~read_write_mutex();
read_write_scheduling_policy::read_write_scheduling_policy_enum policy() const { return m_impl.m_sp; }
@@ -151,8 +161,8 @@ class BOOST_THREAD_DECL try_read_write_mutex : private noncopyable
{
public:
try_read_write_mutex(read_write_scheduling_policy::read_write_scheduling_policy_enum sp) : m_impl(sp) { }
~try_read_write_mutex() { }
try_read_write_mutex(read_write_scheduling_policy::read_write_scheduling_policy_enum sp);
~try_read_write_mutex();
read_write_scheduling_policy::read_write_scheduling_policy_enum policy() const { return m_impl.m_sp; }
@@ -201,8 +211,8 @@ class BOOST_THREAD_DECL timed_read_write_mutex : private noncopyable
{
public:
timed_read_write_mutex(read_write_scheduling_policy::read_write_scheduling_policy_enum sp) : m_impl(sp) { }
~timed_read_write_mutex() { }
timed_read_write_mutex(read_write_scheduling_policy::read_write_scheduling_policy_enum sp);
~timed_read_write_mutex();
read_write_scheduling_policy::read_write_scheduling_policy_enum policy() const { return m_impl.m_sp; }

View File

@@ -70,6 +70,7 @@ public:
void add_thread(thread* thrd);
void remove_thread(thread* thrd);
void join_all();
int size();
private:
std::list<thread*> m_threads;

View File

@@ -11,11 +11,17 @@
#include <boost/thread/detail/config.hpp>
#include <boost/detail/workaround.hpp>
#include <boost/thread/once.hpp>
#include <cstdio>
#include <cassert>
#if defined(BOOST_HAS_WINTHREADS)
# if BOOST_WORKAROUND(__BORLANDC__,<= 0x551)
using std::size_t;
# endif
# include <windows.h>
# if defined(BOOST_NO_STRINGSTREAM)
# include <strstream>

File diff suppressed because it is too large Load Diff

View File

@@ -519,7 +519,7 @@ bool recursive_try_mutex::do_trylock()
# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE)
int res = 0;
res = pthread_mutex_trylock(&m_mutex);
assert(res == 0);
assert(res == 0 || res == EBUSY);
if (res == 0)
{

View File

@@ -220,6 +220,7 @@ bool thread::operator!=(const thread& other) const
void thread::join()
{
assert(m_joinable); //See race condition comment below
int res = 0;
#if defined(BOOST_HAS_WINTHREADS)
res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_thread), INFINITE);
@@ -367,4 +368,9 @@ void thread_group::join_all()
}
}
int thread_group::size()
{
return m_threads.size();
}
} // namespace boost

View File

@@ -27,7 +27,7 @@ inline void to_time(int milliseconds, boost::xtime& xt)
xt.nsec += ((milliseconds % MILLISECONDS_PER_SECOND) *
NANOSECONDS_PER_MILLISECOND);
if (xt.nsec > NANOSECONDS_PER_SECOND)
if (xt.nsec >= NANOSECONDS_PER_SECOND)
{
++xt.sec;
xt.nsec -= NANOSECONDS_PER_SECOND;
@@ -39,7 +39,7 @@ inline void to_timespec(const boost::xtime& xt, timespec& ts)
{
ts.tv_sec = static_cast<int>(xt.sec);
ts.tv_nsec = static_cast<int>(xt.nsec);
if(ts.tv_nsec > NANOSECONDS_PER_SECOND)
if(ts.tv_nsec >= NANOSECONDS_PER_SECOND)
{
ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND;
ts.tv_nsec %= NANOSECONDS_PER_SECOND;
@@ -75,7 +75,7 @@ inline void to_timespec_duration(const boost::xtime& xt, timespec& ts)
ts.tv_sec -= 1;
ts.tv_nsec += NANOSECONDS_PER_SECOND;
}
if(ts.tv_nsec > NANOSECONDS_PER_SECOND)
if(ts.tv_nsec >= NANOSECONDS_PER_SECOND)
{
ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND;
ts.tv_nsec %= NANOSECONDS_PER_SECOND;

View File

@@ -95,7 +95,7 @@ int xtime_get(struct xtime* xtp, int clock_type)
# endif
static const boost::uint64_t TIMESPEC_TO_FILETIME_OFFSET =
UINT64_C(116444736000000000);
const boost::uint64_t ft64 =
(static_cast<boost::uint64_t>(ft.dwHighDateTime) << 32)
+ ft.dwLowDateTime;

View File

@@ -19,18 +19,27 @@
import testing ;
project
: requirements <library>../build//boost_thread
<library>../../test/build//boost_unit_test_framework
: requirements <library>../../test/build//boost_unit_test_framework
<threading>multi
;
rule thread-run ( sources )
{
return
[ run $(sources) ../build//boost_thread ]
[ run $(sources) ../build//boost_thread/<link>static
: : : : $(sources[1]:B)_lib ]
;
}
{
test-suite "threads"
: [ run test_thread.cpp ]
[ run test_mutex.cpp ]
[ run test_condition.cpp ]
[ run test_tss.cpp ]
[ run test_once.cpp ]
[ run test_xtime.cpp ]
[ run test_barrier.cpp ]
: [ thread-run test_thread.cpp ]
[ thread-run test_mutex.cpp ]
[ thread-run test_condition.cpp ]
[ thread-run test_tss.cpp ]
[ thread-run test_once.cpp ]
[ thread-run test_xtime.cpp ]
[ thread-run test_barrier.cpp ]
;
}

View File

@@ -48,7 +48,7 @@ void test_barrier()
g.join_all();
BOOST_TEST(global_parameter == 5);
BOOST_CHECK(global_parameter == 5);
}
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])

View File

@@ -17,7 +17,6 @@
#include <boost/thread/condition.hpp>
#include <boost/test/unit_test.hpp>
#include <boost/test/unit_test_suite_ex.hpp>
#define DEFAULT_EXECUTION_MONITOR_TYPE execution_monitor::use_sleep_only
#include <libs/thread/test/util.inl>

View File

@@ -9,6 +9,8 @@
// about the suitability of this software for any purpose.
// It is provided "as is" without express or implied warranty.
#include <boost/thread/detail/config.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/xtime.hpp>
#include <boost/thread/read_write_mutex.hpp>
@@ -17,314 +19,767 @@
#include <iostream>
#define TS_CHECK(pred) \
do { if (!(pred)) BOOST_ERROR (#pred); } while (0)
#define TS_CHECK_MSG(pred, msg) \
do { if (!(pred)) BOOST_ERROR (msg); } while (0)
namespace {
int shared_val = 0;
boost::xtime xsecs(int secs)
{
//Create an xtime that is secs seconds from now
boost::xtime ret;
BOOST_TEST(boost::TIME_UTC == boost::xtime_get(&ret, boost::TIME_UTC));
TS_CHECK (boost::TIME_UTC == boost::xtime_get(&ret, boost::TIME_UTC));
ret.sec += secs;
return ret;
}
#define MESSAGE "w1=" << w1.value_ << ", w2=" << w2.value_ << ", r1=" << r1.value_ << ", r2=" << r2.value_
template <typename RW>
class thread_adapter
{
public:
thread_adapter(void (*func)(void*,RW &), void* param1,RW &param2)
: _func(func), _param1(param1) ,_param2(param2){ }
void operator()() const { _func(_param1, _param2); }
thread_adapter(
void (*func)(void*, RW&),
void* param1,
RW &param2
)
: func_(func)
, param1_(param1)
, param2_(param2)
{}
void operator()() const
{
func_(param1_, param2_);
}
private:
void (*_func)(void*, RW &);
void* _param1;
RW& _param2;
void (*func_)(void*, RW&);
void* param1_;
RW& param2_;
};
const int k_data_init = -1;
template <typename RW>
struct data
{
data(int id, RW &m, int secs=0)
: m_id(id), m_value(-1), m_secs(secs), m_rw(m)
{
}
int m_id;
int m_value;
int m_secs;
data(
int id,
RW& m,
int wait_for_lock_secs,
int sleep_with_lock_secs,
bool demote_after_write = false
)
: id_(id)
, wait_for_lock_secs_(wait_for_lock_secs)
, sleep_with_lock_secs_(sleep_with_lock_secs)
, test_promotion_and_demotion_(demote_after_write)
, value_(k_data_init)
, rw_(m)
{}
RW& m_rw; // Reader/Writer mutex
int id_;
int wait_for_lock_secs_;
int sleep_with_lock_secs_;
bool test_promotion_and_demotion_;
int value_;
RW& rw_;
};
// plain_writer excercises the "infinite" lock for each
// read_write_mutex type.
template<typename RW>
void plain_writer(void *arg,RW &rw)
void plain_writer(void* arg, RW& rw)
{
data<RW> *pdata = (data<RW> *) arg;
// std::cout << "-->W" << pdata->m_id << "\n";
typename RW::scoped_read_write_lock l(rw, boost::read_write_lock_state::write_locked);
boost::thread::sleep(xsecs(3));
shared_val += 10;
pdata->m_value = shared_val;
}
template<typename RW>
void plain_reader(void *arg,RW &rw)
{
data<RW> *pdata = (data<RW> *) arg;
typename RW::scoped_read_write_lock l(rw, boost::read_write_lock_state::read_locked);
pdata->m_value = shared_val;
}
template<typename RW>
void try_writer(void *arg,RW &rw)
{
data<RW> *pdata = (data<RW> *) arg;
// std::cout << "-->W" << pdata->m_id << "\n";
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::unlocked);
if (l.try_write_lock())
try
{
data<RW>* pdata = (data<RW>*) arg;
TS_CHECK_MSG(pdata->wait_for_lock_secs_ == 0, "pdata->wait_for_lock_secs_: " << pdata->wait_for_lock_secs_);
boost::thread::sleep(xsecs(3));
shared_val += 10;
typename RW::scoped_read_write_lock l(
rw,
pdata->test_promotion_and_demotion_
? boost::read_write_lock_state::read_locked
: boost::read_write_lock_state::write_locked
);
pdata->m_value = shared_val;
bool succeeded = true;
if (pdata->test_promotion_and_demotion_)
{
try
{
l.promote();
}
catch(const boost::lock_error&)
{
succeeded = false;
}
}
if (succeeded)
{
if (pdata->sleep_with_lock_secs_ > 0)
boost::thread::sleep(xsecs(pdata->sleep_with_lock_secs_));
shared_val += 10;
if (pdata->test_promotion_and_demotion_)
l.demote();
pdata->value_ = shared_val;
}
}
}
template<typename RW>
void try_reader(void *arg,RW &rw)
{
data<RW> *pdata = (data<RW> *) arg;
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::unlocked);
if (l.try_read_lock())
catch(...)
{
pdata->m_value = shared_val;
TS_CHECK_MSG(false, "plain_writer() exception!");
throw;
}
}
template<typename RW>
void timed_writer(void *arg,RW &rw)
void plain_reader(void* arg, RW& rw)
{
data<RW> *pdata = (data<RW> *) arg;
boost::xtime xt;
xt = xsecs(pdata->m_secs);
typename RW::scoped_timed_read_write_lock l(rw,boost::read_write_lock_state::unlocked);
if (l.timed_write_lock(xt))
try
{
boost::thread::sleep(xsecs(3));
shared_val += 10;
data<RW>* pdata = (data<RW>*)arg;
TS_CHECK(!pdata->test_promotion_and_demotion_);
TS_CHECK_MSG(pdata->wait_for_lock_secs_ == 0, "pdata->wait_for_lock_secs_: " << pdata->wait_for_lock_secs_);
pdata->m_value = shared_val;
typename RW::scoped_read_write_lock l(rw, boost::read_write_lock_state::read_locked);
if (pdata->sleep_with_lock_secs_ > 0)
boost::thread::sleep(xsecs(pdata->sleep_with_lock_secs_));
pdata->value_ = shared_val;
}
catch(...)
{
TS_CHECK_MSG(false, "plain_reader() exception!");
throw;
}
}
template<typename RW>
void timed_reader(void *arg,RW &rw)
void try_writer(void* arg, RW& rw)
{
data<RW> *pdata = (data<RW> *) arg;
boost::xtime xt;
xt = xsecs(pdata->m_secs);
typename RW::scoped_timed_read_write_lock l(rw,boost::read_write_lock_state::unlocked);
if (l.timed_read_lock(xt))
try
{
pdata->m_value = shared_val;
data<RW>* pdata = (data<RW>*) arg;
TS_CHECK_MSG(pdata->wait_for_lock_secs_ == 0, "pdata->wait_for_lock_secs_: " << pdata->wait_for_lock_secs_);
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::unlocked);
bool succeeded = false;
if (pdata->test_promotion_and_demotion_)
succeeded = l.try_read_lock() && l.try_promote();
else
succeeded = l.try_write_lock();
if (succeeded)
{
if (pdata->sleep_with_lock_secs_ > 0)
boost::thread::sleep(xsecs(pdata->sleep_with_lock_secs_));
shared_val += 10;
if (pdata->test_promotion_and_demotion_)
l.demote();
pdata->value_ = shared_val;
}
}
catch(...)
{
TS_CHECK_MSG(false, "try_writer() exception!");
throw;
}
}
template<typename RW>
void dump_times(const char *prefix,data<RW> *pdata)
void try_reader(void*arg, RW& rw)
{
std::cout << " " << prefix << pdata->m_id <<
" In:" << pdata->m_start.LowPart <<
" Holding:" << pdata->m_holding.LowPart <<
" Out: " << pdata->m_end.LowPart << std::endl;
try
{
data<RW>* pdata = (data<RW>*)arg;
TS_CHECK(!pdata->test_promotion_and_demotion_);
TS_CHECK_MSG(pdata->wait_for_lock_secs_ == 0, "pdata->wait_for_lock_secs_: " << pdata->wait_for_lock_secs_);
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::unlocked);
if (l.try_read_lock())
{
if (pdata->sleep_with_lock_secs_ > 0)
boost::thread::sleep(xsecs(pdata->sleep_with_lock_secs_));
pdata->value_ = shared_val;
}
}
catch(...)
{
TS_CHECK_MSG(false, "try_reader() exception!");
throw;
}
}
template<typename RW>
void test_plain_read_write_mutex(RW &rw)
void timed_writer(void* arg, RW& rw)
{
try
{
data<RW>* pdata = (data<RW>*)arg;
typename RW::scoped_timed_read_write_lock l(rw, boost::read_write_lock_state::unlocked);
bool succeeded = false;
boost::xtime xt = xsecs(pdata->wait_for_lock_secs_);
if (pdata->test_promotion_and_demotion_)
succeeded = l.timed_read_lock(xt) && l.timed_promote(xt);
else
succeeded = l.timed_write_lock(xt);
if (succeeded)
{
if (pdata->sleep_with_lock_secs_ > 0)
boost::thread::sleep(xsecs(pdata->sleep_with_lock_secs_));
shared_val += 10;
if (pdata->test_promotion_and_demotion_)
l.demote();
pdata->value_ = shared_val;
}
}
catch(...)
{
TS_CHECK_MSG(false, "timed_writer() exception!");
throw;
}
}
template<typename RW>
void timed_reader(void* arg, RW& rw)
{
try
{
data<RW>* pdata = (data<RW>*)arg;
TS_CHECK(!pdata->test_promotion_and_demotion_);
typename RW::scoped_timed_read_write_lock l(rw,boost::read_write_lock_state::unlocked);
boost::xtime xt = xsecs(pdata->wait_for_lock_secs_);
if (l.timed_read_lock(xt))
{
if (pdata->sleep_with_lock_secs_ > 0)
boost::thread::sleep(xsecs(pdata->sleep_with_lock_secs_));
pdata->value_ = shared_val;
}
}
catch(...)
{
TS_CHECK_MSG(false, "timed_reader() exception!");
throw;
}
}
template<typename RW>
void clear_data(data<RW>& data1, data<RW>& data2, data<RW>& data3, data<RW>& data4)
{
shared_val = 0;
data<RW> r1(1,rw);
data<RW> r2(2,rw);
data<RW> w1(1,rw);
data<RW> w2(2,rw);
data1.value_ = k_data_init;
data2.value_ = k_data_init;
data3.value_ = k_data_init;
data4.value_ = k_data_init;
}
// Writer one launches, holds the lock for 3 seconds.
boost::thread tw1(thread_adapter<RW>(plain_writer,&w1,rw));
bool shared_test_writelocked = false;
bool shared_test_readlocked = false;
bool shared_test_unlocked = false;
// Writer two launches, tries to grab the lock, "clearly"
// after Writer one will already be holding it.
boost::thread::sleep(xsecs(1));
boost::thread tw2(thread_adapter<RW>(plain_writer,&w2,rw));
// Reader one launches, "clearly" after writer two, and "clearly"
// while writer 1 still holds the lock
boost::thread::sleep(xsecs(1));
boost::thread tr1(thread_adapter<RW>(plain_reader,&r1,rw));
boost::thread tr2(thread_adapter<RW>(plain_reader,&r2,rw));
tr2.join();
tr1.join();
tw2.join();
tw1.join();
if (rw.policy() == boost::read_write_scheduling_policy::writer_priority)
template<typename RW>
void run_try_tests(void* arg, RW& rw)
{
try
{
BOOST_TEST(w1.m_value == 10);
BOOST_TEST(w2.m_value == 20);
BOOST_TEST(r1.m_value == 20); // Readers get in after 2nd writer
BOOST_TEST(r2.m_value == 20);
TS_CHECK(shared_test_writelocked || shared_test_readlocked || shared_test_unlocked);
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::unlocked);
if (shared_test_writelocked)
{
//Verify that write lock blocks other write locks
TS_CHECK(!l.try_write_lock());
//Verify that write lock blocks read locks
TS_CHECK(!l.try_read_lock());
}
else if (shared_test_readlocked)
{
//Verify that read lock blocks write locks
TS_CHECK(!l.try_write_lock());
//Verify that read lock does not block other read locks
TS_CHECK(l.try_read_lock());
//Verify that read lock blocks promotion
TS_CHECK(!l.try_promote());
}
else if (shared_test_unlocked)
{
//Verify that unlocked does not blocks write locks
TS_CHECK(l.try_write_lock());
//Verify that unlocked does not block demotion
TS_CHECK(l.try_demote());
l.unlock();
//Verify that unlocked does not block read locks
TS_CHECK(l.try_read_lock());
//Verify that unlocked does not block promotion
TS_CHECK(l.try_promote());
l.unlock();
}
}
else if (rw.policy() == boost::read_write_scheduling_policy::reader_priority)
catch(...)
{
BOOST_TEST(w1.m_value == 10);
BOOST_TEST(w2.m_value == 20);
BOOST_TEST(r1.m_value == 10); // Readers get in before 2nd writer
BOOST_TEST(r2.m_value == 10);
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_many_reads)
{
BOOST_TEST(w1.m_value == 10);
BOOST_TEST(w2.m_value == 20);
BOOST_TEST(r1.m_value == 10); // Readers get in before 2nd writer
BOOST_TEST(r2.m_value == 10);
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_single_read)
{
BOOST_TEST(w1.m_value == 10);
BOOST_TEST(w2.m_value == 20);
// One Reader gets in before 2nd writer, but we can't tell
// which reader will "win", so just check their sum.
BOOST_TEST((r1.m_value + r2.m_value == 30));
TS_CHECK_MSG(false, "run_try_tests() exception!");
throw;
}
}
template<typename RW>
void test_try_read_write_mutex(RW &rw)
void test_plain_read_write_mutex(RW& rw, bool test_promotion_and_demotion)
{
data<RW> r1(1,rw);
data<RW> w1(2,rw);
data<RW> w2(3,rw);
//Verify that a write lock prevents both readers and writers from obtaining a lock
{
shared_val = 0;
data<RW> r1(1, rw, 0, 0);
data<RW> r2(2, rw, 0, 0);
data<RW> w1(3, rw, 0, 0);
data<RW> w2(4, rw, 0, 0);
// We start with some specialized tests for "try" behavior
//Write-lock the mutex and queue up other readers and writers
shared_val = 0;
typename RW::scoped_read_write_lock l(rw, boost::read_write_lock_state::write_locked);
// Writer one launches, holds the lock for 3 seconds.
boost::thread tr1(thread_adapter<RW>(plain_reader, &r1, rw));
boost::thread tr2(thread_adapter<RW>(plain_reader, &r2, rw));
boost::thread tw1(thread_adapter<RW>(plain_writer, &w1, rw));
boost::thread tw2(thread_adapter<RW>(plain_writer, &w2, rw));
boost::thread tw1(thread_adapter<RW>(try_writer,&w1,rw));
boost::thread::sleep(xsecs(1));
// Reader one launches, "clearly" after writer #1 holds the lock
// and before it releases the lock.
boost::thread::sleep(xsecs(1));
boost::thread tr1(thread_adapter<RW>(try_reader,&r1,rw));
//At this point, neither queued readers nor queued writers should have obtained access
// Writer two launches in the same timeframe.
boost::thread tw2(thread_adapter<RW>(try_writer,&w2,rw));
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init, MESSAGE);
tw2.join();
tr1.join();
tw1.join();
if (test_promotion_and_demotion)
{
l.demote();
boost::thread::sleep(xsecs(1));
//:boost::thread tr3(thread_adapter<RW>(plain_reader, &r3, rw));
BOOST_TEST(w1.m_value == 10);
BOOST_TEST(r1.m_value == -1); // Try would return w/o waiting
BOOST_TEST(w2.m_value == -1); // Try would return w/o waiting
if (rw.policy() == boost::read_write_scheduling_policy::writer_priority)
{
//Expected result:
//Since writers have priority, demotion doesn't release any readers.
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init, MESSAGE);
}
else if (rw.policy() == boost::read_write_scheduling_policy::reader_priority)
{
//Expected result:
//Since readers have priority, demotion releases all readers.
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_many_reads)
{
//Expected result:
//Since readers can be released many at a time, demotion releases all queued readers.
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
//:TS_CHECK_MSG(r3.value_ == k_data_init, MESSAGE);
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_single_read)
{
//Expected result:
//Since readers can be released only one at a time, demotion releases one queued reader.
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init || r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init || r2.value_ == 0, MESSAGE);
TS_CHECK_MSG(r1.value_ != r2.value_, MESSAGE);
}
}
l.unlock();
// We finish by repeating the plain tests with the try lock
// This is important to verify that try locks are proper read_write_mutexes as
// well.
test_plain_read_write_mutex(rw);
tr2.join();
tr1.join();
tw2.join();
tw1.join();
if (rw.policy() == boost::read_write_scheduling_policy::writer_priority)
{
if (!test_promotion_and_demotion)
{
//Expected result:
//1) either w1 or w2 obtains and releases the lock
//2) the other of w1 and w2 obtains and releases the lock
//3) r1 and r2 obtain and release the lock "simultaneously"
TS_CHECK_MSG(w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 20, MESSAGE);
TS_CHECK_MSG(r2.value_ == 20, MESSAGE);
}
else
{
//Expected result:
//The same, except that either w1 or w2 (but not both) may
//fail to promote to a write lock,
//and r1, r2, or both may "sneak in" ahead of w1 and/or w2
//by obtaining a read lock before w1 or w2 can promote
//their initial read lock to a write lock.
TS_CHECK_MSG(w1.value_ == k_data_init || w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init || w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init || r1.value_ == 10 || r1.value_ == 20, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init || r2.value_ == 10 || r2.value_ == 20, MESSAGE);
}
}
else if (rw.policy() == boost::read_write_scheduling_policy::reader_priority)
{
if (!test_promotion_and_demotion)
{
//Expected result:
//1) r1 and r2 obtain and release the lock "simultaneously"
//2) either w1 or w2 obtains and releases the lock
//3) the other of w1 and w2 obtains and releases the lock
TS_CHECK_MSG(w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
}
else
{
//Expected result:
//The same, except that either w1 or w2 (but not both) may
//fail to promote to a write lock.
TS_CHECK_MSG(w1.value_ == k_data_init || w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init || w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
}
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_many_reads)
{
if (!test_promotion_and_demotion)
{
//Expected result:
//1) r1 and r2 obtain and release the lock "simultaneously"
//2) either w1 or w2 obtains and releases the lock
//3) the other of w1 and w2 obtains and releases the lock
TS_CHECK_MSG(w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
}
else
{
//Expected result:
//The same, except that either w1 or w2 (but not both) may
//fail to promote to a write lock.
TS_CHECK_MSG(w1.value_ == k_data_init || w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init || w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
}
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_single_read)
{
if (!test_promotion_and_demotion)
{
//Expected result:
//1) either r1 or r2 obtains and releases the lock
//2) either w1 or w2 obtains and releases the lock
//3) the other of r1 and r2 obtains and releases the lock
//4) the other of w1 and w2 obtains and release the lock
TS_CHECK_MSG(w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0 || r1.value_ == 10, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0 || r2.value_ == 10, MESSAGE);
TS_CHECK_MSG(r1.value_ != r2.value_, MESSAGE);
}
else
{
//Expected result:
//Since w1 and w2 start as read locks, r1, r2, w1, and w2
//obtain read locks "simultaneously". Each of w1 and w2,
//after it obtain a read lock, attempts to promote to a
//write lock; this attempt fails if the other has
//already done so and currently holds the write lock;
//otherwise it will succeed as soon as any other
//read locks have been released.
//In other words, any ordering is possible, and either
//w1 or w2 (but not both) may fail to obtain the lock
//altogether.
TS_CHECK_MSG(w1.value_ == k_data_init || w1.value_ == 10 || w1.value_ == 20, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init || w2.value_ == 10 || w2.value_ == 20, MESSAGE);
TS_CHECK_MSG(w1.value_ != w2.value_, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0 || r1.value_ == 10 || r1.value_ == 20, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0 || r2.value_ == 10 || r2.value_ == 20, MESSAGE);
}
}
}
//Verify that a read lock prevents readers but not writers from obtaining a lock
{
shared_val = 0;
data<RW> r1(1, rw, 0, 0);
data<RW> r2(2, rw, 0, 0);
data<RW> w1(3, rw, 0, 0);
data<RW> w2(4, rw, 0, 0);
//Read-lock the mutex and queue up other readers and writers
typename RW::scoped_read_write_lock l(rw, boost::read_write_lock_state::read_locked);
boost::thread tr1(thread_adapter<RW>(plain_reader, &r1, rw));
boost::thread tr2(thread_adapter<RW>(plain_reader, &r2, rw));
boost::thread::sleep(xsecs(1));
boost::thread tw1(thread_adapter<RW>(plain_writer, &w1, rw));
boost::thread tw2(thread_adapter<RW>(plain_writer, &w2, rw));
boost::thread::sleep(xsecs(1));
//Expected result: all readers passed through before the writers entered
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
if (test_promotion_and_demotion)
{
l.promote();
}
l.unlock();
tr2.join();
tr1.join();
tw2.join();
tw1.join();
}
//Verify that a read lock prevents readers but not writers from obtaining a lock
{
shared_val = 0;
data<RW> r1(1, rw, 0, 0);
data<RW> r2(2, rw, 0, 0);
data<RW> w1(3, rw, 0, 0);
data<RW> w2(4, rw, 0, 0);
//Read-lock the mutex and queue up other readers and writers
typename RW::scoped_read_write_lock l(rw, boost::read_write_lock_state::read_locked);
boost::thread tw1(thread_adapter<RW>(plain_writer, &w1, rw));
boost::thread tw2(thread_adapter<RW>(plain_writer, &w2, rw));
boost::thread::sleep(xsecs(1));
boost::thread tr1(thread_adapter<RW>(plain_reader, &r1, rw));
boost::thread tr2(thread_adapter<RW>(plain_reader, &r2, rw));
boost::thread::sleep(xsecs(1));
if (rw.policy() == boost::read_write_scheduling_policy::writer_priority)
{
//Expected result:
//Writers have priority, so no readers have been released
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init, MESSAGE);
}
else if (rw.policy() == boost::read_write_scheduling_policy::reader_priority)
{
//Expected result:
//Readers have priority, so all readers have been released
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == 0, MESSAGE);
TS_CHECK_MSG(r2.value_ == 0, MESSAGE);
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_many_reads)
{
//Expected result:
//It's the writers' turn, so no readers have been released
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init, MESSAGE);
}
else if (rw.policy() == boost::read_write_scheduling_policy::alternating_single_read)
{
//Expected result:
//It's the writers' turn, so no readers have been released
TS_CHECK_MSG(w1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(w2.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r1.value_ == k_data_init, MESSAGE);
TS_CHECK_MSG(r2.value_ == k_data_init, MESSAGE);
}
if (test_promotion_and_demotion)
{
l.promote();
}
l.unlock();
tr2.join();
tr1.join();
tw2.join();
tw1.join();
}
}
template<typename RW>
void test_timed_read_write_mutex(RW &rw)
void test_try_read_write_mutex(RW& rw, bool test_promotion_and_demotion)
{
data<RW> r1(1,rw,1);
data<RW> r2(2,rw,3);
data<RW> w1(3,rw,3);
data<RW> w2(4,rw,1);
//Repeat the plain tests with the try lock.
//This is important to verify that try locks are proper
//read_write_mutexes as well.
// We begin with some specialized tests for "timed" behavior
test_plain_read_write_mutex(rw, test_promotion_and_demotion);
shared_val = 0;
//Verify try_* operations with write-locked mutex
{
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::write_locked);
// Writer one will hold the lock for 3 seconds.
boost::thread tw1(thread_adapter<RW>(timed_writer,&w1,rw));
shared_test_writelocked = true;
shared_test_readlocked = false;
shared_test_unlocked = false;
boost::thread test_thread(thread_adapter<RW>(run_try_tests, NULL, rw));
test_thread.join();
}
boost::thread::sleep(xsecs(1));
// Writer two will "clearly" try for the lock after the readers
// have tried for it. Writer will wait up 1 second for the lock.
// This write will fail.
boost::thread tw2(thread_adapter<RW>(timed_writer,&w2,rw));
//Verify try_* operations with read-locked mutex
{
typename RW::scoped_try_read_write_lock l(rw, boost::read_write_lock_state::read_locked);
shared_test_writelocked = false;
shared_test_readlocked = true;
shared_test_unlocked = false;
boost::thread test_thread(thread_adapter<RW>(run_try_tests, NULL, rw));
test_thread.join();
}
// Readers one and two will "clearly" try for the lock after writer
// one already holds it. 1st reader will wait 1 second, and will fail
// to get the lock. 2nd reader will wait 3 seconds, and will get
// the lock.
//Verify try_* operations with unlocked mutex
{
shared_test_writelocked = false;
shared_test_readlocked = false;
shared_test_unlocked = true;
boost::thread test_thread(thread_adapter<RW>(run_try_tests, NULL, rw));
test_thread.join();
}
}
boost::thread tr1(thread_adapter<RW>(timed_reader,&r1,rw));
boost::thread tr2(thread_adapter<RW>(timed_reader,&r2,rw));
template<typename RW>
void test_timed_read_write_mutex(RW& rw, bool test_promotion_and_demotion)
{
//Repeat the try tests with the timed lock.
//This is important to verify that timed locks are proper
//try locks as well.
test_try_read_write_mutex(rw, test_promotion_and_demotion);
tw1.join();
tr1.join();
tr2.join();
tw2.join();
BOOST_TEST(w1.m_value == 10);
BOOST_TEST(r1.m_value == -1);
BOOST_TEST(r2.m_value == 10);
BOOST_TEST(w2.m_value == -1);
// We follow by repeating the try tests with the timed lock.
// This is important to verify that timed locks are proper try locks as
// well
test_try_read_write_mutex(rw);
//:More tests here
}
} // namespace
void test_read_write_mutex()
void do_test_read_write_mutex(bool test_promotion_and_demotion)
{
int i;
for(i = (int) boost::read_write_scheduling_policy::writer_priority;
//Run every test for each scheduling policy
for(int i = (int) boost::read_write_scheduling_policy::writer_priority;
i <= (int) boost::read_write_scheduling_policy::alternating_single_read;
i++)
{
boost::read_write_mutex plain_rw(static_cast<boost::read_write_scheduling_policy::read_write_scheduling_policy_enum>(i));
boost::try_read_write_mutex try_rw(static_cast<boost::read_write_scheduling_policy::read_write_scheduling_policy_enum>(i));
boost::timed_read_write_mutex timed_rw(static_cast<boost::read_write_scheduling_policy::read_write_scheduling_policy_enum>(i));
std::cout << "plain test, sp=" << i
<< (test_promotion_and_demotion ? " with promotion & demotion" : " without promotion & demotion")
<< "\n";
std::cout.flush();
std::cout << "plain test, sp=" << i << "\n";
test_plain_read_write_mutex(plain_rw);
{
boost::read_write_mutex plain_rw(static_cast<boost::read_write_scheduling_policy::read_write_scheduling_policy_enum>(i));
test_plain_read_write_mutex(plain_rw, test_promotion_and_demotion);
}
std::cout << "try test, sp=" << i << "\n";
test_try_read_write_mutex(try_rw);
std::cout << "try test, sp=" << i
<< (test_promotion_and_demotion ? " with promotion & demotion" : " without promotion & demotion")
<< "\n";
std::cout.flush();
std::cout << "timed test, sp=" << i << "\n";
test_timed_read_write_mutex(timed_rw);
{
boost::try_read_write_mutex try_rw(static_cast<boost::read_write_scheduling_policy::read_write_scheduling_policy_enum>(i));
test_try_read_write_mutex(try_rw, test_promotion_and_demotion);
}
std::cout << "timed test, sp=" << i
<< (test_promotion_and_demotion ? " with promotion & demotion" : " without promotion & demotion")
<< "\n";
std::cout.flush();
{
boost::timed_read_write_mutex timed_rw(static_cast<boost::read_write_scheduling_policy::read_write_scheduling_policy_enum>(i));
test_timed_read_write_mutex(timed_rw, test_promotion_and_demotion);
}
}
}
void test_read_write_mutex()
{
do_test_read_write_mutex(false);
do_test_read_write_mutex(true);
}
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
{
boost::unit_test_framework::test_suite* test =

View File

@@ -29,9 +29,9 @@ void simple_thread()
void comparison_thread(boost::thread* parent)
{
boost::thread thrd;
BOOST_TEST(thrd != *parent);
BOOST_CHECK(thrd != *parent);
boost::thread thrd2;
BOOST_TEST(thrd == thrd2);
BOOST_CHECK(thrd == thrd2);
}
void test_sleep()
@@ -41,7 +41,7 @@ void test_sleep()
// Ensure it's in a range instead of checking actual equality due to time
// lapse
BOOST_CHECK(in_range(xt));
BOOST_CHECK(in_range(xt, 2));
}
void do_test_creation()

View File

@@ -30,8 +30,8 @@ inline boost::xtime delay(int secs, int msecs=0, int nsecs=0)
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));
if (boost::TIME_UTC != boost::xtime_get (&xt, boost::TIME_UTC))
BOOST_ERROR ("boost::xtime_get != boost::TIME_UTC");
nsecs += xt.nsec;
msecs += nsecs / NANOSECONDS_PER_MILLISECOND;
@@ -140,10 +140,10 @@ void timed_test(F func, int secs,
}
template <typename F, typename T>
class binder
class thread_binder
{
public:
binder(const F& func, const T& param)
thread_binder(const F& func, const T& param)
: func(func), param(param) { }
void operator()() const { func(param); }
@@ -153,9 +153,9 @@ private:
};
template <typename F, typename T>
binder<F, T> bind(const F& func, const T& param)
thread_binder<F, T> bind(const F& func, const T& param)
{
return binder<F, T>(func, param);
return thread_binder<F, T>(func, param);
}
} // namespace