diff --git a/doc/basic_lock.html b/doc/basic_lock.html new file mode 100644 index 00000000..dceca444 --- /dev/null +++ b/doc/basic_lock.html @@ -0,0 +1,122 @@ + + + + + +Boost.Threads, basic_lock + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

basic_lock

+
+ +
+ +

This template class defines a generic lock concept type. The +mutex, try_mutex, timed_mutex, +recursive_mutex, recursive_try_mutex and +recursive_timed_mutex all use this template to define their +lock types.

+ +

Like all the Boost.Threads lock models, the +basic_lock is meant to be short lived and is not thread safe, so should not be shared +between threads.

+ +

Header

+ +
+#include <boost/thread/xlock.hpp>
+    This header is usually not included directly by programmers.
+
+ +

Public Interface

+ +
+ +
+    template <typename M>
+    class basic_lock : private boost::noncopyable
+    {
+    public:
+        typedef M mutex_type;
+        
+        explicit basic_lock(M& mx, bool lock_it=true);
+        ~basic_lock();
+        
+        void lock();
+        void unlock();
+        
+        operator const void*() const;
+    };
+
+ +
+ +

Constructor

+ +
+    explicit basic_lock(M& mx, bool lock_it=true);
+
+ +

Constructs a basic_lock and if lock_it is true then calls lock.

+ +

Destructor

+ +
+    ~basic_lock();
+
+ +

Destructs the basic_lock and if locked calls unlock.

+ +

lock

+ +
+    void lock();
+
+ +

Locks the associated mutex model. If the basic_lock +is already locked then a lock_error is thrown. Depending on the +locking strategy of the +mutex model if the calling thread already owns a lock through +another lock model this may cause a deadlock or for a +lock_error to be thrown.

+ +

unlock

+ +
+    void unlock();
+
+ +

Unlocks the associated mutex model. If the basic_lock +is not already locked then a lock_error is thrown.

+ +

const void* Conversion

+ +
+    operator const void*() const;
+
+ +

Implicitly converts the lock to a value that can be used in boolean expressions to test if the +lock is currently locked or not.

+ +

Example Usage

+ +

See the example given in the documentation for the mutex class.

+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/basic_timedlock.html b/doc/basic_timedlock.html new file mode 100644 index 00000000..8544b17a --- /dev/null +++ b/doc/basic_timedlock.html @@ -0,0 +1,164 @@ + + + + + +Boost.Threads, basic_timedlock + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

basic_timedlock

+
+ +
+ +

This template class defines a lock type that allows a +program to attempt to lock the associated mutex model +blocking only for a specified amount of time. The timed_mutex and +recursive_timed_mutex use this template to define their +timed_lock types.

+ +

Like all the Boost.Threads lock models, the +basic_timedlock is meant to be short lived and is not thread safe, so should not +be shared between threads.

+ +

Header

+ +
+#include <boost/thread/xlock.hpp>
+   This header is usually not included directly by programmers.  Instead it's
+   included by the mutex model that exposes it.
+
+ +

Public Interface

+ +
+ +
+    template <typename M>
+    class basic_timedlock : private boost::noncopyable
+    {
+    public:
+        typedef M mutex_type;
+
+        basic_timedlock(M& mx, const boost::xtime& xt);
+        basic_timedlock(M& mx, bool lock_it);
+        ~basic_timedlock();
+        
+        void lock();
+        bool timed_lock(const xtime& xt);
+        void unlock();
+        
+        operator const void*() const;
+    };
+
+ +
Constructors
+ +
+    basic_timedlock(M& mx, const xtime& xt);
+
+ +

Constructs a basic_timedlock and calls timed_lock with xt.

+ +
+    basic_timedlock(M& mx, bool lock_it);
+
+ +

Constructs a basic_timedlock and if lock_it is true then +calls lock. + +

Destructor
+ +
+    ~basic_timedlock();
+
+ +

Destructs the basic_timedlock and if locked calls unlock.

+ +

lock

+ +
+    void lock();
+
+ +

Locks the associated mutex model. If the basic_timedlock +is already locked then a lock_error is thrown. Depending on the +locking strategy of the +mutex model if the calling thread already owns a lock through +another lock model this may cause a deadlock or for a +lock_error to be thrown.

+ +

timed_lock

+ +
+    bool timed_lock(const xtime& xt);
+
+ +

Attempts to lock the associated mutex model. If the +basic_timedlock is already locked then a lock_error is +thrown. If the mutex model is already locked by another thread +this operation will wait until xt before returning false.

+ +

unlock

+ +
+    void unlock();
+
+ +

Unlocks the associated mutex model. If the basic_timedlock +is not already locked then a lock_error is thrown.

+ +

const void* Conversion

+ +
+    operator const void*() const;
+
+ +

Implicitly converts the lock to a value that can be used in boolean expressions to test if the +lock is currently locked or not.

+ +

Example Usage

+ +
+#include <boost/thread/mutex.hpp>
+#include <iostream>
+
+int main(int, char*[])
+{
+   boost::mutex mutex;
+   boost::xtime xt;
+   boost::get_xtime(&xt, boost::TIME_UTC);
+   xt.sec += 1;
+   boost::mutex::timed_lock lock(mutex, xt);
+   if (lock)
+      std::cout << "locked" << std::endl;
+   else
+      std::cout << "unlocked" << std::endl;
+   return 0;
+}
+
+ +

The output is:

+ +
+locked
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/basic_trylock.html b/doc/basic_trylock.html new file mode 100644 index 00000000..6c2eabc2 --- /dev/null +++ b/doc/basic_trylock.html @@ -0,0 +1,163 @@ + + + + + +Boost.Threads, basic_trylock + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

basic_trylock

+
+ +
+ +

This template class defines a lock type that allows +the program to attempt to lock the associated mutex model +with out blocking. The try_mutex, timed_mutex, +recursive_try_mutex and +recursive_timed_mutex use this template to define their +try_lock types.

+ +

Like all the Boost.Threads lock models, the +basic_trylock is meant to be short lived and is not thread safe, so should not be +shared between threads.

+ +

Header

+ +
+#include <boost/thread/xlock.hpp>
+   This header is usually not included directly by programmers.
+
+ +

Public Interface

+ +
+ +
+    template <typename M>
+    class basic_trylock : private boost::noncopyable
+    {
+    public:
+        typedef M mutex_type;
+        
+        explicit basic_trylock(M& mx);
+        basic_trylock(M& mx, bool lock_it);
+        ~basic_trylock();
+        
+        void lock();
+        bool try_lock();
+        void unlock();
+        
+        operator const void*() const;
+    };
+
+ +
+ +

Constructors

+ +
+    explicit basic_trylock(M& mx);
+
+ +

Constructs a basic_trylock and calls try_lock.

+ +
+    basic_trylock(M& mx, bool lock_it);
+
+ +

Constructs a basic_trylock and if lock_it is true then +calls lock. + +

Destructor

+ +
+    ~basic_trylock();
+
+ +

Destructs the basic_trylock and if locked calls unlock.

+ +

lock

+ +
+    void lock();
+
+ +

Locks the associated mutex model. If the basic_trylock +is already locked then a lock_error is thrown. Depending on the +locking strategy of the +mutex model if the calling thread already owns a lock through +another lock model this may cause a deadlock or for a +lock_error to be thrown.

+ +

try_lock

+ +
+    bool try_lock();
+
+ +

Attempts to lock the associated mutex_model. If the basic_trylock +is already locked then a lock_error is thrown. If the +mutex model is already locked by another thread this attempt fails +immediately with out blocking and returns false.

+ +

unlock

+ +
+    void unlock();
+
+ +

Unlocks the associated mutex model. If the basic_trylock +is not already locked then a lock_error is thrown.

+ +

const void* Conversion

+ +
+    operator const void*() const;
+
+ +

Implicitly converts the lock to a value that can be used in boolean expressions to test if the +lock is currently locked or not.

+ +

Example Usage

+ +
+#include <boost/thread/mutex.hpp>
+#include <iostream>
+
+int main(int, char*[])
+{
+   boost::mutex mutex;
+   boost::mutex::try_lock lock(mutex);
+   if (lock)
+      std::cout << "locked" << std::endl;
+   else
+      std::cout << "unlocked" << std::endl;
+   return 0;
+}
+
+ +

The output is:

+ +
+locked
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/condition.html b/doc/condition.html new file mode 100644 index 00000000..20f5bb3e --- /dev/null +++ b/doc/condition.html @@ -0,0 +1,250 @@ + + + + + +Boost.Threads, condition + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

condition

+
+ +
+ +

A condition is a synchronization primitive that can be employed to suspend a thread +and reactivate it when a particular condition is met. A condition always works in +conjunction with a mutex model. The +mutex model must be locked prior to waiting on the condition, +which is insured by passing a lock model to the wait +methods. While the thread is suspended waiting on the condition the +mutex model is unlocked. When the thread returns from a call +to one of the wait methods the mutex model is again +locked.

+ +

The condition type is often used to implement the +Monitor Object pattern.

+ +

Header

+ +
+#include <boost/thread/condition.hpp>
+
+ +

Public Interface

+ +
+ +
+    class condition : private boost::noncopyable
+    {
+    public:
+       condition();
+       ~condition();
+
+       void notify_one();
+       void notify_all();
+       template <typename L>
+          void wait(L& lock);
+       template <typename L, typename Pr>
+          void wait(L& lock, Pr pred);
+       template <typename L>
+          bool timed_wait(L& lock, const xtime& xt);
+       template <typename L, typename Pr>
+          bool timed_wait(L& lock, const xtime& xt, Pr pred);
+    };
+
+ +
+ +

Constructor

+ +
+    condition();
+
+ +

Constructs a condition.

+ +

Destructor

+ +
+    ~condition();
+
+ +

Destructs a condition.

+ +

notify_one

+ +
+    void notify_one();
+
+ +

If there are any waiting threads releases at least one.

+ +

notify_all

+ +
+    void notify_all();
+
+ +

Releases all waiting threads.

+ +

wait

+ +
+    template <typename L>
+       void wait(L& lock);
+
+ +

Releases the lock on the mutex model associated with lock, +waits for the condition to be notified and then reacquires the lock all in an atomic fashion. +This version should always be used within a loop checking that the state logically associated +with the condition has become true. With out the loop race conditions can ensue due to +possible "spurious wake ups". The second version encapsulates this idiom internally and should +generally be the preferred method.

+ +
+    template <typename L, typename Pr>
+       void wait(L& lock, Pr pred);
+
+ +

Releases the lock on the mutex model associated with lock, +waits for the condition to be notified and for pred to be true then +reacquires the lock +all in an atomic fashion.

+ +

timed_wait

+ +
+    template <typename L>
+       bool timed_wait(L& lock, const xtime& xt);
+
+ +

Releases the lock on the mutex model associated with the lock, +waits for the condition to be notified or until xt and then reacquires the lock +all in an atomic fashion. If the wait timed out the return value is false. This version should +always be used within a loop checking that the state logically associated with the condition has +become true. With out the loop race conditions can ensue due to "spurious wake ups". The second version +encapsulates this idiom internally and should generally be the preferred method.

+ +
+    template <typename L, typename Pr>
+       bool timed_wait(L& lock, const xtime& xt, Pr pred);
+
+ +

Releases the lock on the mutex model associated with the lock, +waits for the condition to be notified and for pred to be true or until xt +and then reacquires the lock all in an atomic fashion. If the wait timed out the return value is false.

+ +

Example Usage

+ +
+#include <iostream>
+#include <vector>
+#include <boost/utility.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/thread/thread.hpp>
+
+class bounded_buffer : private boost::noncopyable
+{
+private:
+    int begin, end, buffered;
+    std::vector<int> circular_buf;
+    boost::condition buffer_not_full, buffer_not_empty;
+    boost::mutex monitor;
+
+    typedef boost::mutex::lock lock;
+
+public:
+    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 && !finished)
+            buffer_not_empty.wait(lk);
+        int i = circular_buf[begin];
+        begin = (begin+1) % circular_buf.size();
+        --buffered;
+        buffer_not_full.notify_one();
+        return i;
+    }
+};
+
+bounded_buffer buf(2);
+
+void sender(void*) {
+    int n = 0;
+    while (n < 100) {
+       buf.send(n);
+       std::cout << "sent: " << n << std::endl;
+       ++n;
+    }
+    buf.send(-1);
+}
+
+void receiver(void*) {
+    int n;
+    do {
+       n = buf.receive();
+       std::cout << "received: " << n << std::endl;
+    } while (n != -1); // -1 indicates end of buffer
+}
+
+int main(int, char*[])
+{
+    boost::thread::create(&sender, 0);
+    boost::thread::create(&receiver, 0);
+    boost::thread::join_all();
+    return 0;
+}
+
+ +

Typical output (dependent on scheduling policies) is:

+ +
+sent: 0
+sent: 1
+received: 0
+received: 1
+sent: 2
+sent: 3
+received: 2
+received: 3
+sent: 4
+received: 4
+
+ +
+ +

References

+ +

Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann: +Pattern-Oriented Software Architecture - Volume 2 (Patterns for Concurrent +and Networked Objects), Wiley, 2000.

+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/config.html b/doc/config.html new file mode 100644 index 00000000..4b943a93 --- /dev/null +++ b/doc/config.html @@ -0,0 +1,96 @@ + + + + + +Boost.Threads, Configuration Information + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

Configuration Information

+
+ +
+ +

Boost.Threads uses several configuration macros in <boost/config.hpp>. +These macros are documented here. Most of the macros are +of interest only to developers attempting to provide new implementations of Boost.Threads. +The one exception to this is BOOST_HAS_THREADS.

+ + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Macro + + Meaning +
+ BOOST_HAS_THREADS + + Indicates that threading support is available. This means both that there is a + platform specific implementation for Boost.Threads and that threading + support has been enabled in a platform specific manner. For instance, on the + Win32 platform there's an implementation for Boost.Threads but unless + the program is compiled against one of the multi-threading runtimes (when the + compiler will predefine the macro _MT) this macro remains undefined. +
+ BOOST_HAS_WINTHREADS + + Indicates that the platform has Win32 threading libraries that should be used + to implement Boost.Threads. +
+ BOOST_HAS_PTHREADS + + Indicates that the platform has POSIX pthreads libraries that should be used + to implement Boost.Threads. +
+ BOOST_HAS_FTIME + + Indicates that the implementation should use GetSystemTimeAsFileTime() and + the FILETIME type to calculate the current time. This is an implementation + detail used by boost::detail::getcurtime(). +
+ BOOST_HAS_GETTTIMEOFDAY + + Indicates that the implementation should use gettimeofday() to calculate the + current time. This is an implementation detail used by boost::detail::getcurtime(). +
+
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/index.html b/doc/index.html new file mode 100644 index 00000000..29e3c20e --- /dev/null +++ b/doc/index.html @@ -0,0 +1,64 @@ + + + + + +Boost.Threads, Index + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

Index

+

(version 1.00, 8 February 2001)

+
+ +
+ +

Contents

+ + + +
+ +

© Copyright William E. Kempf +2001

+

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 and this permission notice appear + in supporting documentation. William E. Kempf makes no representations + about the suitability of this software for any purpose. + It is provided "as is" without express or implied warranty.

+ +

 

+ + + diff --git a/doc/introduction.html b/doc/introduction.html new file mode 100644 index 00000000..eb6402e6 --- /dev/null +++ b/doc/introduction.html @@ -0,0 +1,127 @@ + + + + + +Boost.Threads, Introduction + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

Introduction

+
+ +
+ +

Motivation

+ +

With client/server and three-tier architectures becoming common place in today's +world it's becoming increasingly important for programs to be able to handle parallel +processing. Modern day operating systems usually provide some support for this +through native thread APIs. Unfortunately, writing portable code that makes use +of parallel processing in C++ is made very difficult by a lack of a standard interface +for these native APIs. Further, these APIs are almost universally C APIs and fail to +take advantage of C++'s strengths, or to address C++'s issues.

+ +

The Boost.Threads library is an attempt to define a portable interface for writing +parallel processes in C++.

+ +

Goals

+ +

The Boost.Threads library has several goals that should help to set it apart from +other solutions. These goals are listed in order of precedence with full descriptions +below.

+ +

+ +

Iterative Phases

+ +

Another goal of the Boost.Threads was for it to take a dynamic, iterative +approach in its development. The computing industry is still exploring the concepts of parallel programming. +Most thread libraries supply only simple primitive concepts for thread synchronization. +These concepts are very simple, but they are very difficult to use safely or to provide +formal proofs for constructs built on top of them. Until recently, these primitives +were "state of the art" and the only concepts available to programmers. Recently +there has been a lot of research in other concepts, such as in "Communicating Sequential +Processes." Boost.Threads was designed in iterative steps, providing the building +blocks necessary for the next step, and giving the researcher the tools necessary to +explore new concepts in a portable manner.

+ +

Given the goal of following a dynamic, iterative approach Boost.Threads shall go through +several growth cycles. Each phase in its development shall be roughly documented here.

+ +

Phase 1, Synchronization Primitives

+ +

Boost is all about providing high quality libraries with implementations for many platforms. +Unfortunately, there's a big problem faced by developers wishing to supply such high quality +libraries, namely thread safety. The C++ standard doesn't address threads at all, but real +world programs often make use of native threading support. A portable library that doesn't +address the issue of thread safety is there for not much help to a programmer who wants to +use the library in his multi-threaded application. So there's a very great need for portable +primitives that will allow the library developer to create thread safe implementations. This +need far out weighs the need for portable methods to create and manage threads.

+ +

Because of this need, the first phase of Boost.Threads focuses solely on providing +portable primitive concepts for thread synchronization. Types provided in this phase include +the semaphore, mutex/try_mutex/timed_mutex, +recursive_mutex/recursive_try_mutex/recursive_timed_mutex, +basic_lock, basic_trylock, +basic_timedlock and lock_error. +These are considered the "core" synchronization primitives, though there are others that will +be added in later phases.

+ +

Phase 2, Thread Management and Thread Specific Storage

+ +

This phase addresses the creation and management of threads and provides a mechanism for +thread specific storage (data associated with a thread instance). Thread management is a tricky +issue in C++, so this phase addresses only the basic needs of multi-threaded program. Later +phases are likely to add additional functionality in this area. This phase of Boost.Threads +adds the thread and tss types. With these additions +the Boost.Threads library can be considered minimal but complete.

+ +

The Next Phase

+ +

The next phase shall address more advanced synchronization concepts, such as read/write mutexes +and barriers.

+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/lock_concept.html b/doc/lock_concept.html new file mode 100644 index 00000000..34b8581b --- /dev/null +++ b/doc/lock_concept.html @@ -0,0 +1,60 @@ + + + + + +Boost.Threads, Lock Concept + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

Lock Concept

+
+ +
+ +

The purpose of a lock concept is to provide an exception safe means for acquiring and +releasing a mutex model. In other words they are an +implementation of the Scoped Locking1 pattern.

+ +

Lock models are constructed with a reference to a mutex model +and typically acquire ownership of the mutex model by setting +its state to locked. They also insure ownership is relinquished in the destructor. Lock +models also expose methods to query the lock status and to manually lock and unlock the +mutex model.

+ +

Instances of lock models are meant to be short lived, expected to be used at block scope +only. In particular it should be noted that lock models are not thread safe. Lock models +must maintain state to indicate whether or not they've been locked and this state is not +protected by any synchronization concepts. For this reason an instance of a lock model +should never be shared between multiple threads.

+ +

Boost.Threads provides three templates used to expose lock models for specific +mutex models: basic_lock, +basic_trylock and +basic_timedlock.

+ +
+ +

Foot Notes

+ +

1 Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann: +Pattern-Oriented Software Architecture - Volume 2 (Patterns for Concurrent +and Networked Objects), Wiley, 2000.

+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/lock_error.html b/doc/lock_error.html new file mode 100644 index 00000000..383cf243 --- /dev/null +++ b/doc/lock_error.html @@ -0,0 +1,87 @@ + + + + + +Boost.Threads, lock_error + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

lock_error

+
+ +
+ +

The lock_error class defines an exception type that is thrown by lock operations that +would deadlock or unlock operations performed by a thread that does not own the lock.

+ +

Header

+ +
+#include <boost/thread/thread.hpp>
+
+ +

Public Interface

+ +
+    class lock_error : public std::runtime_error
+    {
+    public:
+       lock_error();
+    };
+
+ +

Constructor

+ +
+    lock_error();
+
+ +

Constructs a lock_error.

+ +

Example Usage

+ +
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+
+int main(int, char*[])
+{
+    boost::mutex mutex;
+    boost::mutex::lock lock(mutex);
+    try
+    {
+       boost::mutex::lock deadlock(mutex);
+       std::cout << "lock succeeded" << std::endl;
+    }
+    catch (boost::lock_error& err)
+    {
+       std::cout << err.what() << " - deadlock occurred." << std::endl;
+    }
+}
+
+ +

The output is:

+ +
+thread lock error - deadlock occurred.
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/mutex.html b/doc/mutex.html new file mode 100644 index 00000000..fc78a609 --- /dev/null +++ b/doc/mutex.html @@ -0,0 +1,210 @@ + + + + + +Boost.Threads, mutex + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

mutex / try_mutex / timed_mutex

+
+ +
+ +

The mutex, try_mutex and timed_mutex classes define full featured +mutex models. These type should be used to synchronize access to +shared resources when recursive locking mechanics need not be employed. Each type adds another +lock concept, including the basic_lock, +basic_trylock and basic_timedlock +types. For the best possible performance you should use the mutex type that supports only the lock +types that you need.

+ +

The mutex, try_mutex and timed_mutex use an Unspecified +locking strategy, so attempts to recursively lock +them or attempts to unlock them by threads that don't own a lock on them result in undefined behavior. +This strategy allows implementations to be as efficient as possible on any given platform. It is, however, +recommended that implementations include some debugging support to detect misuse when NDEBUG is +not defined.

+ +

Like all the Boost.Threads mutex models, the mutex, +try_mutex and timed_mutex leave the +scheduling policy as Unspecified. +Programmers should assume that threads waiting for a lock on these mutex types shall acquire +the lock in a random order, though the specific behavior for a given platform may be different.

+ +

Header

+ +
+#include <boost/thread/mutex.hpp>
+
+ +

Public Interface

+ +
+ +
+    class mutex : private boost::noncopyable
+    {
+    public:
+        typedef boost::basic_lock<mutex> lock;
+        
+        mutex();
+        ~mutex();
+    };
+
+ +
+ +

Constructor

+ +
+    mutex();
+
+ +

Constructs a mutex.

+ +

Destructor

+ +
+    ~mutex();
+
+ +

Destructs a mutex.

+ +
+ +
+    class try_mutex : private boost::noncopyable
+    {
+    public:
+        typedef boost::basic_lock<try_mutex> lock;
+        typedef boost::basic_trylock<try_mutex> trylock;
+        
+        try_mutex();
+        ~try_mutex();
+    };
+
+ +
+ +

Constructor

+ +
+    try_mutex();
+
+ +

Constructs a try_mutex.

+ +

Destructor

+ +
+    ~try_mutex();
+
+ +

Destructs a try_mutex.

+ +
+ +
+    class timed_mutex : private boost::noncopyable
+    {
+    public:
+        typedef boost::basic_lock<timed_mutex> lock;
+        typedef boost::basic_trylock<timed_mutex> trylock;
+        typedef boost::basic_timedlock<timed_mutex> timedlock;
+        
+        timed_mutex();
+        ~timed_mutex();
+    };
+
+ +
+ +

Constructor

+ +
+    timed_mutex();
+
+ +

Constructs a timed_mutex.

+ +

Destructor

+ +
+    ~timed_mutex();
+
+ +

Destructs a mutex.

+ +

Example Usage

+ +
+#include <boost/thread/mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+
+boost::mutex io_mutex; // The iostreams are not gauranteed to be thread safe!
+
+class counter
+{
+private:
+    boost::mutex mutex;
+    int count;
+   
+public:
+    counter() : count(0) { }
+   
+    int increment() {
+        boost::mutex::lock lock(mutex);
+        return ++count;
+    }
+};
+
+counter c;
+
+void change_count(void*)
+{
+    int i = c.increment();
+    boost::mutex::lock lock(io_mutex);
+    std::cout << "count == " << i << std::endl;
+}
+
+int main(int, char*[])
+{
+    const int num_threads = 4;
+    for (int i=0; i < num_threads; ++i)
+        boost::thread::create(&change_count, 0);
+      
+    boost::thread::join_all();
+   
+    return 0;
+}
+
+ +

The output is:

+ +
+count == 1
+count == 2
+count == 3
+count == 4
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/mutex_concept.html b/doc/mutex_concept.html new file mode 100644 index 00000000..a91ce99d --- /dev/null +++ b/doc/mutex_concept.html @@ -0,0 +1,159 @@ + + + + + +Boost.Threads, Mutex Concept + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

Mutex Concept

+
+ +
+ +

The purpose of a mutex (short for mutual-exclusion) is to serialize access to +a resource shared between multiple threads. The mutex concept formalizes this +idea. A model that implements the mutex concept has two states: locked and +unlocked. Before using a resource that's shared, a thread would lock the mutex model, +insuring that it was the only thread accessing the shared resource at a given +time. When done with the shared resource the thread would unlock the mutex model +allowing another thread to acquire the lock and use the shared resource.

+ +

Traditional C thread APIs, such as pthreads or Windows thread APIs, expose methods to +lock and unlock a mutex model. This is a dangerous way to expose the needed functionality +since it's easy to forget to unlock a mutex model that's been locked. When the flow of +control is complex, with multiple return points, the likelihood that you'll forget to +unlock a mutex model becomes even greater. With exceptions it becomes nearly impossible +to insure that you unlock the mutex. Many C++ threading libraries have made use of a +pattern known as Scoped Locking1 to help insure +that a programmer did not forget to unlock the mutex model. With this pattern a +lock concept is employed where the lock model's constructor +locks the associated mutex model and the destructor unlocks the mutex model. The +Boost.Threads library takes this pattern to the extreme, where lock concepts are the +only way to lock and unlock a mutex model: lock and unlock methods are not exposed by any +mutex models in Boost.Threads. This helps to insure safe usage patterns, especially +with regard to code that may throw exceptions.

+ +

The Boost.Threads library currently defines six mutex models: +boost::mutex, boost::try_mutex, +boost::timed_mutex, boost::recursive_mutex, +boost::recursive_try_mutex and +boost::recursive_timed_mutex.

+ +

Locking Strategies

+ +

Every mutex model follows one of several locking strategies. These strategies +define the semantics for the locking operation when the calling thread already +owns a lock on the mutex model.

+ +

Recursive

+ +

With a recursive locking strategy when a thread attempts to acquire a lock on +the mutex model for which it already owns a lock the operation returns successfully. +Internally a lock count is maintained and the owning thread must unlock the +mutex model the same number of times that it's locked it before the mutex model's +state returns to unlocked. Since mutex model's in Boost.Threads expose +locking functionality only through lock concepts it can be proven that a thread +will always unlock a mutex model the same number of times that it's locked it. +This helps to eliminate a whole set of errors typically found in traditional +C style APIs.

+ +

The boost::recursive_mutex, +boost::recursive_try_mutex +and boost::recursive_timed_mutex use this locking strategy.

+ +

Checked

+ +

With a checked locking strategy when a thread attempts to acquire a lock on +the mutex model for which it already owns a lock the operation will fail with +some sort of error indication. Further, attempts by a thread to unlock a mutex +that was not locked by the thread will also return some sort of error indication. +In Boost.Threads an exception of type boost::lock_error +is thrown in these cases.

+ +

Unchecked

+ +

With an unchecked locking strategy when a thread attempts to acquire a lock +on the mutex model for which it already owns a lock the operation will deadlock. In +general this locking strategy is less safe than a checked or recursive strategy, +but it's also a faster strategy and so is employed by many libraries.

+ +

Boost.Threads does not provide a mutex model that explicitly uses this +strategy.

+ +

Unspecified

+ +

With an unspecified locking strategy when a thread attempts to acquire a lock +on a mutex model for which it already owns a lock the operation results in +undefined behavior. When a mutex model has an unspecified locking strategy the +programmer must assume that the mutex model instead uses an unchecked strategy.

+ +

In general a mutex model with an unspecified locking strategy is unsafe, and it +requires programmer discipline to use the mutex model properly. However, this strategy +allows an implementation to be as fast as possible with no restrictions on its implementation. +This is especially true for portable implementations that wrap the native threading support +of a platform. For this reason the boost::mutex, +boost::try_mutex and boost::timed_mutex use +this locking strategy despite the lack of safety.

+ +

Scheduling Policies

+ +

Every mutex model follows one of several scheduling policies. These policies +define the semantics for when a mutex model has more than one thread waiting to +acquire a lock when the mutex model is unlocked. In other words, the policy defines +which thread shall acquire the lock in this case.

+ +

FIFO

+ +

With a FIFO scheduling policy threads waiting for the lock will acquire it in +a first come first serve order (or First In First Out). This can help prevent a +high priority thread from starving lower priority threads that are also waiting +on the mutex lock.

+ +

Priority Driven

+ +

With a Priority Driven scheduling policy the thread with the highest priority +acquires the lock. Note that this means that low-priority threads may never acquire +the lock if the mutex model has high contention and there is always at least one high-priority +thread waiting. This is known as thread starvation. When multiple threads of the same +priority are waiting on the mutex lock one of the other scheduling priorities will +determine which thread shall acquire the lock.

+ +

Undefined

+ +

Threads acquire the lock in no particular order. Users should assume that +low-priority threads may wait indefinitely, and that threads of the same +priority acquire the lock in essentially random order.

+ +

Unspecified

+ +

The mutex model does not specify which scheduling policy is used. The programmer +must assume that an undefined scheduling policy is used. In order to insure +portability, all Boost.Threads mutex models use an unspecified scheduling policy.

+ +
+ +

Foot Notes

+ +

1 Douglas Schmidt, Michael Stal, Hans Rohnert, Frank Buschmann: +Pattern-Oriented Software Architecture - Volume 2 (Patterns for Concurrent +and Networked Objects), Wiley, 2000.

+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/recursive_mutex.html b/doc/recursive_mutex.html new file mode 100644 index 00000000..712a4728 --- /dev/null +++ b/doc/recursive_mutex.html @@ -0,0 +1,217 @@ + + + + + +Boost.Threads, recursive_mutex + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

recursive_mutex

+
+ +
+ +

recursive_mutex

+ +

The recursive_mutex, recursive_try_mutex and recursive_timed_mutex +classes define full featured mutex models +with recursive locking semantics. These models should be used to synchronize access to shared resources +when recursive locking by a single thread is likely to occur. A good example for this is when a class +supplies "internal synchronization" to insure thread safety and a method on the class may have to call +other methods on the class which would also attempt to lock the mutex model. + +

Each type adds another lock concept, including the +basic_lock, basic_trylock and +basic_timedlock types. For the best possible performance you should use +the mutex type that supports only the lock types that you need.

+ +

The recursive_mutex, recursive_try_mutex and recursive_timed_mutex employ a +Recursive locking strategy, so attempts to +recursively lock them succeed and an internal "lock count" is maintained. Attempts to unlock them +by a thread that does not own a lock on them will result in a lock_error +exception being thrown.

+ +

Like all the Boost.Threads mutex models, the +recursive_mutex, recursive_try_mutex and recursive_timed_mutex leave the +scheduling policy as Unspecified. Programmers +should assume that threads waiting for a lock on them shall acquire the lock in a random order, though the +specific behavior for a given platform may be different.

+ +

Header

+ +
+#include <boost/thread/recursive_mutex.hpp>
+
+ +

Public Interface

+ +
+ +
+    class recursive_mutex : private boost::noncopyable
+    {
+    public:
+       typedef boost::basic_lock<recursive_mutex> lock;
+      
+       recursive_mutex();
+       ~recursive_mutex();
+    };
+
+ +
+ +

Constructor

+ +
+    recursive_mutex();
+
+ +

Constructs a recursive_mutex.

+ +

Destructor

+ +
+    ~recursive_mutex();
+
+ +

Destructs a recursive_mutex.

+ +
+ +
+    class recursive_try_mutex : private boost::noncopyable
+    {
+    public:
+       typedef boost::basic_lock<recursive_try_mutex> lock;
+       typedef boost::basic_trylock<recursive_try_mutex> try_lock;
+      
+       recursive_try_mutex();
+       ~recursive_try_mutex();
+    };
+
+ +
+ +

Constructor

+ +
+    recursive_try_mutex();
+
+ +

Constructs a recursive_try_mutex.

+ +

Destructor

+ +
+    ~recursive_try_mutex();
+
+ +

Destructs a recursive_try_mutex.

+ +
+ +
+    class recursive_timed_mutex : private boost::noncopyable
+    {
+    public:
+       typedef boost::basic_lock<recursive_timed_mutex> lock;
+       typedef boost::basic_trylock<recursive_timed_mutex> try_lock;
+       typedef boost::basic_timedlock<recursive_timed_mutex> timed_lock;
+      
+       recursive_timed_mutex();
+       ~recursive_timed_mutex();
+    };
+
+ +
+ +

Constructor

+ +
+    recursive_timed_mutex();
+
+ +

Constructs a recursive_timed_mutex.

+ +

Destructor

+ +
+    ~recursive_timed_mutex();
+
+ +

Destructs a recursive_timed_mutex.

+ +

Example Usage

+ +
+#include <boost/thread/recursive_mutex.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+
+class counter
+{
+private:
+    boost::recursive_mutex mutex;
+    int count;
+   
+public:
+    counter() : count(0) { }
+
+    int add(int val) {
+        boost::recursive_mutex::lock lock(mutex);
+        count += val;
+        return count;
+    }   
+    int increment() {
+        boost::recursive_mutex::lock lock(mutex);
+        return add(1);
+    }
+};
+
+counter c;
+
+void change_count(void*)
+{
+    std::cout << "count == " << c.increment() << std::endl;
+}
+
+int main(int, char*[])
+{
+    const int num_threads=4;
+   
+    for (int i=0; i < num_threads; ++i)
+        boost::thread::create(&change_count, 0);
+      
+    boost::thread::join_all();
+   
+    return 0;
+}
+
+ +

The output is:

+ +
+count == 1
+count == 2
+count == 3
+count == 4
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/removed_atomic_t.html b/doc/removed_atomic_t.html new file mode 100644 index 00000000..2c22e8e7 --- /dev/null +++ b/doc/removed_atomic_t.html @@ -0,0 +1,131 @@ + + + + + +Boost.Threads, atomic_t + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

atomic_t

+
+ +
+ +

Header

+ +

The atomic_t class defines an "atomic integer" type. This class should be used +to perform thread safe operations on an integral type with out the overhead of locks. Only +a limited set of integer operations are available with an atomic_t instance.

+ +
+#include <boost/thread/atomic.hpp>
+
+ +

Public Interface

+ +
+    class atomic_t
+    {
+    public:
+       typedef implementation defined value_type;
+
+       explicit atomic_t(value_type val=0);
+    };
+
+    atomic_t::value_type read(const atomic_t& x);
+    atomic_t::value_type increment(atomic_t& x);
+    atomic_t::value_type decrement(atomic_t& x);
+    atomic_t::value_type swap(atomic_t& x, atomic_t::value_type y);
+    atomic_t::value_type compare_swap(atomic_t& x, atomic_t::value_type y, atomic_t::value_type z);
+
+ +

Constructor

+ +
+    atomic_t(atomic_t::value_type val=0);
+
+ +

Constructs an atomic_t and sets its value to val.

+ +

read

+ +
+     atomic_t::value_type read(const atomic_t& x);
+
+ +

Gets the current value of x.

+ +

increment

+ +
+    atomic_t::value_type increment(atomic_t& x);
+
+ +

Increments x and returns a value < 0 if the result is less than 0, +> 0 if the result is greater than 0 and == 0 if the result is equal to +0.

+ +

decrement

+ +
+    atomic_t::value_type decrement(atomic_t& x);
+
+ +

Decrements x and returns a value < 0 if the result is less than 0, +> 0 if the result is greater than 0 and == 0 if the result is equal to +0.

+ +

swap

+ +
+    atomic_t::value_type swap(atomic_t& x, atomic_t::value_type y);
+
+ +

Assigns the value of y to x and returns the value of x prior +to the swap.

+ +

compare_swap

+ +
+    atomic_t::value_type compare_swap(atomic_t& x, atomic_t::value_type y, atomic_t::value_type z);
+
+ +

Compares the value of z to the value of x and if equal sets the value of +x to the value of y and returns the value of x prior to the swap.

+ +

Example Usage

+ +
+#include <boost/thread/atomic.hpp>
+#include <boost/test/test_tools.hpp>
+
+int test_main(int, char*[])
+{
+    boost::atomic_t a;
+    BOOST_TEST_VERIFY(boost::read(a) == 0);
+    BOOST_TEST_VERIFY(boost::increment(a) > 0);
+    BOOST_TEST_VERIFY(boost::decrement(a) == 0);
+    BOOST_TEST_VERIFY(boost::swap(a, 1) == 0);
+    BOOST_TEST_VERIFY(boost::swap(a, 2, 0) == 1);
+    BOOST_TEST_VERIFY(boost::read(a) == 1);
+}
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/semaphore.html b/doc/semaphore.html new file mode 100644 index 00000000..c2ce466b --- /dev/null +++ b/doc/semaphore.html @@ -0,0 +1,152 @@ + + + + + +Boost.Threads, semaphore + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

semaphore

+
+ +
+ +

The semaphore class defines a classic synchronization primitive invented by the +Dutch computer scientist Edsger W. Dijkstra. A semaphore manages an internal counter. This +counter may never go below zero, or above a specified maximum value. When calling +semaphore::down the calling thread will block until the value is non-zero and then +decrements the value in a single atomic operation. When calling semaphore::up the +calling thread will increment the value in a single atomic operation, failing if the value has +already reached the specified maximum.

+ +

The semaphore is the simplest synchronization primitive available and is generally the +primitive used to build other synchronization concepts at some level of implementation. For this +reason Boost.Threads defines the semaphore type in the classic form. This simplifies +usage and implementation, but it means that the interface is not as safe as other Boost.Threads +interfaces. Unlike the mutex models supplied by Boost.Threads +there is no lock_concept for the semaphore to help insure proper +usage. Care should be taken when using a semaphore object to insure deadlock and +race conditions do not occur.

+ +

Header

+ +
+#include <boost/thread/semaphore.hpp>
+
+ +

Public Interface

+ +
+    class semaphore : private boost::noncopyable
+    {
+    public:
+        explicit semaphore(unsigned count=0, unsigned max=0);
+        ~semaphore();
+        
+        bool up(unsigned count=1, unsigned* prev=0);
+        void down();
+        bool down(const xtime& xt);
+    };
+
+ +

Constructor

+ +
+    explicit semaphore(unsigned count=0, unsigned max=0);
+
+ +

Constructs a semaphore. The count parameter is used to set the initial +semaphore count and the max parameter is used to set the maximum value for the +semaphore. If max is 0 then the maximum value is set to the maximum +possible value for the implementation.

+ +

Destructor

+ +
+    ~semaphore();
+
+ +

Destructs a semaphore.

+ +

up

+ +
+    bool up(unsigned count=1, unsigned* prev=0);
+
+ +

Increments the semaphore by count and optionally returns the previous value of the semaphore +in prev. If the semaphore's value is already at the maximum value specified in the +constructor then this operation fails immediately and returns a false value.

+ +

down

+ +
+    void down();
+
+ +

Decrements the semaphore by one, blocking indefinately if the semaphore's value is currently zero.

+ +
+    bool down(const xtime& xt);
+
+ +

Decrements the semaphore by one, blocking until xt if the semaphore's value is currently zero. +If the operation times out a false value is returned.

+ +

Example Usage

+ +
+#include <boost/thread/semaphore.hpp>
+#include <boost/thread/thread.hpp>
+#include <iostream>
+
+int global_data = 0;
+boost::semaphore global_semaphore;
+
+void change_global_data(void*)
+{
+    global_semaphore.down();
+    ++global_data;
+    std::cout << "global_data == " << global_data  << std::endl;
+    global_semaphore.up();
+}
+
+int main(int, char*[])
+{
+    const int num_threads = 4;
+    for (int i=0; i <  num_threads; ++i)
+        boost::thread::create(&change_global_data, 0);
+      
+    boost::thread::join_all();
+   
+    return 0;
+}
+
+ +

The output is:

+ +
+global_data == 1
+global_data == 2
+global_data == 3
+global_data == 4
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/thread.html b/doc/thread.html new file mode 100644 index 00000000..76a5c163 --- /dev/null +++ b/doc/thread.html @@ -0,0 +1,212 @@ + + + + + +Boost.Threads, thread + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

thread

+
+ +
+ +

The thread class provides the functionality need to create and manage threads +within the Boost.Threads library.

+ +

Header

+ +
+#include <boost/thread/thread.hpp>
+
+ +

Public Interface

+ +
+ +
+    class thread
+    {
+    public:
+        thread();
+        thread(const thread& other);
+        ~thread();
+
+        thread& operator=(const thread& other);
+        thread& swap(thread& other);
+        bool operator==(const thread& other);
+        bool operator!=(const thread& other);
+        bool is_alive() const;
+        void join();
+
+        static thread create(void (*threadfunc)(void*), void* param);
+        static thread self();
+        static void join_all();
+        static void sleep(const xtime& xt);
+        static void yield();
+    };
+
+ +
+ +

Constructor

+ +
+    thread();
+
+ +

Constructs a thread object that's not associated with any running thread.

+ +
+    thread(const thread& other);
+
+ +

Copy constructs a thread object, adding a reference to the associated running thread.

+ +

Destructor

+ +
+    ~thread();
+
+ +

Destructs a thread, removing a reference to the associated running thread.

+ +

Assignment

+ +
+    thread& operator=(const thread& other);
+
+ +

Assigns the thread to reference the same running thread associated with other.

+ +

swap

+ +
+    thread& swap(thread& other);
+
+ +

Swaps the thread with other, exchanging the references to the respective +associated running threads.

+ +

Comparison Operators

+ +
+    bool operator==(const thread& other);
+    bool operator!=(const thread& other);
+
+ +

Compares the thread to other to see if they refer to the same associated +running threads.

+ +

is_alive

+ +
+    bool is_alive() const;
+
+ +

Determines if the associated running thread is still executing. This operation is only really +useful to perform "busy waits" on the thread since any other use is likely to result in a race +condition.

+ +

join

+ +
+   void join();
+
+
+

Causes the current thread to "join" the associated running thread. In other words, the current +thread will block until the associated running thread finishes its execution.

+ +

create

+ +
+    static thread create(void (*threadfunc)(void*), void* param);
+
+ +

Creates a running thread which calls threadfunc passing it a value of param.

+ +

self

+ +
+    static thread self();
+
+ +

Returns a thread referencing the currently running thread.

+ +

join_all

+ +
+    static void join_all();
+
+ +

Causes the current thread to "join" all the other currently running threads. In other words, the +current thread will block until all the other running threads finish their execution.

+ +

sleep

+ +
+    static void sleep(const xtime& xt);
+
+ +

Causes the current thread to block until xt.

+ +

yield

+ +
+    static void yield();
+
+ +

Causes the current thread to give up the rest of its time to any other thread that may be waiting +on CPU time.

+ +

Example Usage

+ +
+#include <boost/thread/thread.hpp>
+#include <iostram>
+
+void alarm(void* p)
+{
+    int* pn = static_cast(p);
+   
+    boost::xtime xt;
+    boost::xtime_get(&xt, boost::TIME_UTC);
+    xt.sec += *pn;
+   
+    boost::thread::sleep(xt);
+   
+    std::cout << "alarm sounded..." << std::endl;
+}
+
+int main(int argc, char* argv[])
+{
+    int secs = 5;
+    std::cout << "setting alarm for 5 seconds..." << std::endl;
+    boost::thread::create(&alarm, &secs);
+    boost::thread::join_all();
+}
+
+ +

The output is:

+ +
+setting alarm for 5 seconds...
+alarm sounded...
+
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/doc/tss.html b/doc/tss.html new file mode 100644 index 00000000..650ad230 --- /dev/null +++ b/doc/tss.html @@ -0,0 +1,125 @@ + + + + + +Boost.Threads, tss + + + + + + + + + +
+

C++ Boost

+
+

Boost.Threads

+

tss

+
+ +
+ +

The tss class defines an interface for using thread specific storage. Thread +specific storage is data associated with individual threads and is often used to make +operations that rely on global data thread safe.

+ +

Header

+ +
+#include <boost/thread/tss.hpp>
+
+ +

Public Interface

+ +
+ +
+    class tss
+    {
+    public:
+        tss();
+        ~tss();
+
+        void* get() const;
+        bool set(void* value);
+    };
+
+ +
+ +

Constructor

+ +
+    tss();
+
+ +

Constructs a tss object for accessing thread specific storage.

+ +

Destructor

+ +
+    ~tss();
+
+ +

Destructs a tss.

+ +

get

+ +
+    void* get() const;
+
+ +

Retrieves the thread specific storage for the current thread.

+ +

set

+ +
+    bool set(void* value);
+
+ +

Sets the thread specific storage for the current thread. Returns false on failure.

+ +

Example Usage

+ +
+#include <boost/thread/thread.hpp>
+#include <boost/thread/tss.hpp>
+#include <cassert>
+
+boost::tss value;
+
+void increment()
+{
+   int* p = static_cast(value.get());
+   ++*p;
+}
+
+void thread_proc(void*)
+{
+   value.set(new int(0)); // initialize the thread's storage
+   for (int i=0; i<10; ++i)
+   {
+       increment();
+       int* p = static_cast(value.get());
+       assert(*p == i+1);
+   }
+}
+
+int main(int argc, char* argv[])
+{
+   for (int i=0; i<5; ++i)
+      boost::thread::create(&thread_proc, 0);
+   boost::thread::join_all();
+}
+
+ +
+ +

Copyright William E. Kempf +2001 all rights reserved.

+ + + diff --git a/example/Jamfile b/example/Jamfile index e69de29b..acab3d97 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -0,0 +1,5 @@ +SubDir TOP example ; + +SubInclude TOP example monitor ; +SubInclude TOP example starvephil ; +SubInclude TOP example tennis ; \ No newline at end of file diff --git a/example/monitor/Jamfile b/example/monitor/Jamfile new file mode 100644 index 00000000..19827844 --- /dev/null +++ b/example/monitor/Jamfile @@ -0,0 +1,11 @@ +SubDir TOP example monitor ; + +exe monitor + : monitor.cpp + ../../src/threads + ## Requirements ## + : ../.. + multi + ## default-BUILD ## + : debug release + ; diff --git a/example/monitor/monitor.cpp b/example/monitor/monitor.cpp new file mode 100644 index 00000000..54450b60 --- /dev/null +++ b/example/monitor/monitor.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include + +namespace { + const int ITERS = 100; + boost::mutex io_mutex; +}; + +template +class buffer_t : public M, public boost::condition +{ +public: + typedef typename M::lock lock; + + buffer_t(int n) + : p(0), c(0), full(0), buf(n) + { + } + + void send(int m) + { + lock lk(*this); + while (full == buf.size()) + wait(lk); + buf[p] = m; + p = (p+1) % buf.size(); + ++full; + notify_all(); + } + int receive() + { + lock lk(*this); + while (full == 0) + wait(lk); + int i = buf[c]; + c = (c+1) % buf.size(); + --full; + notify_all(); + return i; + } + + static buffer_t& get_buffer() + { + static buffer_t buf(2); + return buf; + } + + static void do_sender_thread(void*) + { + for (int n = 0; n < ITERS; ++n) + { + get_buffer().send(n); + { + volatile boost::mutex::lock lock(io_mutex); + std::cout << "sent: " << n << std::endl; + } + } + } + + static void do_receiver_thread(void*) + { + int n; + do + { + n = get_buffer().receive(); + { + volatile boost::mutex::lock lock(io_mutex); + std::cout << "received: " << n << std::endl; + } + } while (n < ITERS - 1); + } + +private: + unsigned int p, c, full; + std::vector buf; +}; + +template +void do_test(M* dummy=0) +{ + typedef buffer_t buffer_type; + buffer_type::get_buffer(); + boost::thread::create(&buffer_type::do_sender_thread, 0); + boost::thread::create(&buffer_type::do_receiver_thread, 0); + boost::thread::join_all(); +} + +void test_buffer() +{ + do_test(); + do_test(); +} + +int main() +{ + test_buffer(); + return 0; +} diff --git a/example/starvephil/Jamfile b/example/starvephil/Jamfile new file mode 100644 index 00000000..236cbc1f --- /dev/null +++ b/example/starvephil/Jamfile @@ -0,0 +1,11 @@ +SubDir TOP example starvephil ; + +exe starvephil + : starvephil.cpp + ../../src/threads + ## Requirements ## + : ../.. + multi + ## default-BUILD ## + : debug release + ; diff --git a/example/starvephil/starvephil.cpp b/example/starvephil/starvephil.cpp new file mode 100644 index 00000000..ffb5b45c --- /dev/null +++ b/example/starvephil/starvephil.cpp @@ -0,0 +1,153 @@ +#include +#include +#include +#include +#include + +namespace +{ + boost::mutex iomx; +}; + +class canteen +{ +public: + canteen() : m_chickens(0) { } + + void get(int id) + { + boost::mutex::lock lock(m_mutex); + while (m_chickens == 0) + { + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Phil" << id << + ": wot, no chickens? I'll WAIT ..." << std::endl; + } + m_condition.wait(lock); + } + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Phil" << id << + ": those chickens look good ... one please ..." << std::endl; + } + m_chickens--; + } + void put(int value) + { + boost::mutex::lock lock(m_mutex); + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << + ") Chef: ouch ... make room ... this dish is very hot ..." << std::endl; + } + boost::xtime xt; + boost::xtime_get(&xt, boost::TIME_UTC); + xt.sec += 3; + boost::thread::sleep(xt); + m_chickens += value; + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << + ") Chef: more chickens ... " << m_chickens << + " now available ... NOTIFYING ..." << std::endl; + } + m_condition.notify_all(); + } + +private: + boost::mutex m_mutex; + boost::condition m_condition; + int m_chickens; +}; + +canteen g_canteen; + +void chef(void*) +{ + const int chickens = 4; + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Chef: starting ..." << std::endl; + } + for (;;) + { + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Chef: cooking ..." << std::endl; + } + boost::xtime xt; + boost::xtime_get(&xt, boost::TIME_UTC); + xt.sec += 2; + boost::thread::sleep(xt); + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Chef: " << chickens + << " chickens, ready-to-go ..." << std::endl; + } + g_canteen.put(chickens); + } +} + +struct phil +{ + phil(int id) : m_id(id) { } + void run() { + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Phil" << m_id << ": starting ..." << std::endl; + } + for (;;) + { + if (m_id > 0) + { + boost::xtime xt; + boost::xtime_get(&xt, boost::TIME_UTC); + xt.sec += 3; + boost::thread::sleep(xt); + } + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Phil" << m_id + << ": gotta eat ..." << std::endl; + } + g_canteen.get(m_id); + { + boost::mutex::lock lock(iomx); + std::cout << "(" << clock() << ") Phil" << m_id + << ": mmm ... that's good ..." << std::endl; + } + } + } + static void do_thread(void* param) { + static_cast(param)->run(); + } + + int m_id; +}; + +struct thread_adapt +{ + thread_adapt(void (*func)(void*), void* param) : _func(func), _param(param) { } + int operator()() const + { + _func(_param); + return 0; + } + + void (*_func)(void*); + void* _param; +}; + +int main(int argc, char* argv[]) +{ + boost::thread::create(&chef, 0); + phil p[] = { phil(0), phil(1), phil(2), phil(3), phil(4) }; + boost::thread::create(&phil::do_thread, &p[0]); + boost::thread::create(&phil::do_thread, &p[1]); + boost::thread::create(&phil::do_thread, &p[2]); + boost::thread::create(&phil::do_thread, &p[3]); + boost::thread::create(&phil::do_thread, &p[4]); + boost::thread::join_all(); + return 0; +} diff --git a/example/tennis/Jamfile b/example/tennis/Jamfile new file mode 100644 index 00000000..ca563d36 --- /dev/null +++ b/example/tennis/Jamfile @@ -0,0 +1,11 @@ +SubDir TOP example tennis ; + +exe tennis + : tennis.cpp + ../../src/threads + ## Requirements ## + : ../.. + multi + ## default-BUILD ## + : debug release + ; diff --git a/example/tennis/tennis.cpp b/example/tennis/tennis.cpp new file mode 100644 index 00000000..b1cbf279 --- /dev/null +++ b/example/tennis/tennis.cpp @@ -0,0 +1,105 @@ +#include +#include +#include +#include +#include +#include + +enum game_state +{ + START, + PLAYER_A, + PLAYER_B, + GAME_OVER, + ONE_PLAYER_GONE, + BOTH_PLAYERS_GONE +}; + +int state; +boost::mutex mutex; +boost::condition cond; + +char* player_name(int state) +{ + if (state == PLAYER_A) + return "PLAYER-A"; + if (state == PLAYER_B) + return "PLAYER-B"; + throw "bad player"; + return 0; +} + +void player(void* param) +{ + boost::mutex::lock lock(mutex); + + int active = (int)param; + int other = active == PLAYER_A ? PLAYER_B : PLAYER_A; + + while (state < GAME_OVER) + { + std::cout << player_name(active) << ": Play." << std::endl; + state = other; + cond.notify_all(); + do + { + cond.wait(lock); + if (state == other) + std::cout << "---" << player_name(active) << ": Spurious wakeup!" << std::endl; + } while (state == other); + } + + ++state; + std::cout << player_name(active) << ": Gone." << std::endl; + cond.notify_all(); +} + +struct thread_adapt +{ + thread_adapt(void (*func)(void*), void* param) : _func(func), _param(param) { } + int operator()() const + { + _func(_param); + return 0; + } + + void (*_func)(void*); + void* _param; +}; + +int main(int argc, char* argv[]) +{ + state = START; + + boost::thread::create(&player, (void*)PLAYER_A); + boost::thread::create(&player, (void*)PLAYER_B); + + boost::xtime xt; + boost::xtime_get(&xt, boost::TIME_UTC); + xt.sec += 1; + boost::thread::sleep(xt); + { + boost::mutex::lock lock(mutex); + std::cout << "---Noise ON..." << std::endl; + } + + for (int i = 0; i < 1000000; ++i) + cond.notify_all(); + + { + boost::mutex::lock lock(mutex); + std::cout << "---Noise OFF..." << std::endl; + state = GAME_OVER; + cond.notify_all(); + do + { + cond.wait(lock); + } while (state != BOTH_PLAYERS_GONE); + } + + std::cout << "GAME OVER" << std::endl; + + boost::thread::join_all(); + + return 0; +} \ No newline at end of file diff --git a/src/Jamfile b/src/Jamfile new file mode 100644 index 00000000..532dd546 --- /dev/null +++ b/src/Jamfile @@ -0,0 +1,17 @@ +SubDir TOP src ; + +lib threads + : condition.cpp + mutex.cpp + recursive_mutex.cpp + semaphore.cpp + thread.cpp + tss.cpp + xtime.cpp + ## Requirements ## + : .. + multi + ## default-BUILD ## + : debug release + ; + diff --git a/src/_atomic.cpp b/src/_atomic.cpp new file mode 100644 index 00000000..8a56faf1 --- /dev/null +++ b/src/_atomic.cpp @@ -0,0 +1,80 @@ +/* + * + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + */ + +#include + +#if defined(BOOST_HAS_WINTHREADS) +# include +#endif + +namespace boost { + atomic_t::value_type read(const atomic_t& x) + { + return x._value; + } + +#if defined(BOOST_HAS_WINTHREADS) + atomic_t::value_type increment(atomic_t& x) + { + return InterlockedIncrement(const_cast(&x._value)); + } + + atomic_t::value_type decrement(atomic_t& x) + { + return InterlockedDecrement(const_cast(&x._value)); + } + + atomic_t::value_type swap(atomic_t& x, atomic_t::value_type y) + { + return InterlockedExchange(const_cast(&x._value), y); + } + + atomic_t::value_type compare_swap(atomic_t& x, atomic_t::value_type y, atomic_t::value_type z) + { + return InterlockedCompareExchange(const_cast(&x._value), y, z); + } +#else + atomic_t::value_type increment(atomic_t& x) + { + mutex::lock lock(x._mutex); + return ++x._value; + } + + atomic_t::value_type decrement(atomic_t& x) + { + mutex::lock lock(x._mutex); + return --x._value; + } + + atomic_t::value_type swap(atomic_t& x, atomic_t::value_type y) + { + mutex::lock lock(x._mutex); + atomic_t::value_type temp = x._value; + x._value = y; + return temp; + } + + atomic_t::value_type compare_swap(atomic_t& x, atomic_t::value_type y, atomic_t::value_type z) + { + mutex::lock lock(x._mutex); + atomic_t::value_type temp = x._value; + if (temp == z) + x._value = y; + return temp; + } +#endif +} // namespace boost diff --git a/src/atomic.cpp b/src/atomic.cpp new file mode 100644 index 00000000..453e1e89 --- /dev/null +++ b/src/atomic.cpp @@ -0,0 +1,80 @@ +/* + * + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + */ + +#include + +#if defined(BOOST_HAS_WINTHREADS) +# include +#endif + +namespace boost { + atomic_t::value_type read(const atomic_t& x) + { + return x.value; + } + +#if defined(BOOST_HAS_WINTHREADS) + atomic_t::value_type increment(atomic_t& x) + { + return InterlockedIncrement(const_cast(&x.value)); + } + + atomic_t::value_type decrement(atomic_t& x) + { + return InterlockedDecrement(const_cast(&x.value)); + } + + atomic_t::value_type swap(atomic_t& x, atomic_t::value_type y) + { + return InterlockedExchange(const_cast(&x.value), y); + } + + atomic_t::value_type compare_swap(atomic_t& x, atomic_t::value_type y, atomic_t::value_type z) + { + return InterlockedCompareExchange(const_cast(&x.value), y, z); + } +#else + atomic_t::value_type increment(atomic_t& x) + { + mutex::lock lock(x._mutex); + return ++x.value; + } + + atomic_t::value_type decrement(atomic_t& x) + { + mutex::lock lock(x._mutex); + return --x.value; + } + + atomic_t::value_type swap(atomic_t& x, atomic_t::value_type y) + { + mutex::lock lock(x._mutex); + atomic_t::value_type temp = x.value; + x.value = y; + return temp; + } + + atomic_t::value_type compare_swap(atomic_t& x, atomic_t::value_type y, atomic_t::value_type z) + { + mutex::lock lock(x._mutex); + atomic_t::value_type temp = x.value; + if (temp == z) + x.value = y; + return temp; + } +#endif +} // namespace boost diff --git a/src/condition.cpp b/src/condition.cpp new file mode 100644 index 00000000..2b684ca5 --- /dev/null +++ b/src/condition.cpp @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + * 22 May 01 Modified to use xtime for time outs. + */ + +#include +#include +#include +#include +#include +#include "timeconv.inl" + +#if defined(BOOST_HAS_WINTHREADS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#endif + +namespace boost +{ +#if defined(BOOST_HAS_WINTHREADS) + condition::condition() + : _gone(0), _blocked(0), _waiting(0) + { + _gate = reinterpret_cast(CreateSemaphore(0, 1, 1, 0)); + _queue = reinterpret_cast(CreateSemaphore(0, 0, std::numeric_limits::max(), 0)); + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + + if (!_gate || !_queue || !_mutex) + { + int res = CloseHandle(reinterpret_cast(_gate)); + assert(res); + res = CloseHandle(reinterpret_cast(_queue)); + assert(res); + res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + + throw std::runtime_error("boost::condition : failure to construct"); + } + } + + condition::~condition() + { + int res = CloseHandle(reinterpret_cast(_gate)); + assert(res); + res = CloseHandle(reinterpret_cast(_queue)); + assert(res); + res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void condition::notify_one() + { + unsigned signals = 0; + + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + if (_waiting != 0) // the _gate is already closed + { + if (_blocked == 0) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + return; + } + + ++_waiting; + --_blocked = 0; + } + else + { + res = WaitForSingleObject(reinterpret_cast(_gate), INFINITE); + assert(res == WAIT_OBJECT_0); + if (_blocked > _gone) + { + if (_gone != 0) + { + _blocked -= _gone; + _gone = 0; + } + signals = _waiting = 1; + --_blocked; + } + else + { + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + } + + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + + if (signals) + { + res = ReleaseSemaphore(reinterpret_cast(_queue), signals, 0); + assert(res); + } + } + } + + void condition::notify_all() + { + unsigned signals = 0; + + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + if (_waiting != 0) // the _gate is already closed + { + if (_blocked == 0) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + return; + } + + _waiting += (signals = _blocked); + _blocked = 0; + } + else + { + res = WaitForSingleObject(reinterpret_cast(_gate), INFINITE); + assert(res == WAIT_OBJECT_0); + if (_blocked > _gone) + { + if (_gone != 0) + { + _blocked -= _gone; + _gone = 0; + } + signals = _waiting = _blocked; + _blocked = 0; + } + else + { + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + } + + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + + if (signals) + { + res = ReleaseSemaphore(reinterpret_cast(_queue), signals, 0); + assert(res); + } + } + } + + void condition::enter_wait() + { + int res = WaitForSingleObject(reinterpret_cast(_gate), INFINITE); + assert(res == WAIT_OBJECT_0); + ++_blocked; + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + } + + void condition::do_wait() + { + int res = WaitForSingleObject(reinterpret_cast(_queue), INFINITE); + assert(res == WAIT_OBJECT_0); + + unsigned was_waiting=0; + unsigned was_gone=0; + + res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + was_waiting = _waiting; + was_gone = _gone; + if (was_waiting != 0) + { + if (--_waiting == 0) + { + if (_blocked != 0) + { + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); // open _gate + assert(res); + was_waiting = 0; + } + else if (_gone != 0) + _gone = 0; + } + } + else if (++_gone == (std::numeric_limits::max() / 2)) + { + // timeout occured, normalize the _gone count + // this may occur if many calls to wait with a timeout are made and + // no call to notify_* is made + res = WaitForSingleObject(reinterpret_cast(_gate), INFINITE); + assert(res == WAIT_OBJECT_0); + _blocked -= _gone; + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + _gone = 0; + } + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + + if (was_waiting == 1) + { + for (/**/ ; was_gone; --was_gone) + { + // better now than spurious later + res = WaitForSingleObject(reinterpret_cast(_queue), INFINITE); + assert(res == WAIT_OBJECT_0); + } + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + } + } + + bool condition::do_timed_wait(const xtime& xt) + { + unsigned milliseconds; + to_duration(xt, milliseconds); + + int res = WaitForSingleObject(reinterpret_cast(_queue), milliseconds); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + + bool ret = (res == WAIT_OBJECT_0); + + unsigned was_waiting=0; + unsigned was_gone=0; + + res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + was_waiting = _waiting; + was_gone = _gone; + if (was_waiting != 0) + { + if (!ret) // timeout + { + if (_blocked != 0) + --_blocked; + else + ++_gone; // count spurious wakeups + } + if (--_waiting == 0) + { + if (_blocked != 0) + { + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); // open _gate + assert(res); + was_waiting = 0; + } + else if (_gone != 0) + _gone = 0; + } + } + else if (++_gone == (std::numeric_limits::max() / 2)) + { + // timeout occured, normalize the _gone count + // this may occur if many calls to wait with a timeout are made and + // no call to notify_* is made + res = WaitForSingleObject(reinterpret_cast(_gate), INFINITE); + assert(res == WAIT_OBJECT_0); + _blocked -= _gone; + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + _gone = 0; + } + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + + if (was_waiting == 1) + { + for (/**/ ; was_gone; --was_gone) + { + // better now than spurious later + res = WaitForSingleObject(reinterpret_cast(_queue), INFINITE); + assert(res == WAIT_OBJECT_0); + } + res = ReleaseSemaphore(reinterpret_cast(_gate), 1, 0); + assert(res); + } + + return ret; + } +#elif defined(BOOST_HAS_PTHREADS) + condition::condition() + { + int res = pthread_cond_init(&_cond, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::condition : failure to construct"); + } + + condition::~condition() + { + int res = pthread_cond_destroy(&_cond); + assert(res == 0); + } + + void condition::notify_one() + { + int res = pthread_cond_signal(&_cond); + assert(res == 0); + } + + void condition::notify_all() + { + int res = pthread_cond_broadcast(&_cond); + assert(res == 0); + } + + void condition::do_wait(pthread_mutex_t* pmutex) + { + int res = pthread_cond_wait(&_cond, pmutex); + assert(res == 0); + } + + bool condition::do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex) + { + timespec ts; + to_timespec(xt, ts); + + int res = pthread_cond_timedwait(&_cond, pmutex, &ts); + assert(res == 0 || res == ETIMEDOUT); + + return res != ETIMEDOUT; + } +#endif +} // namespace boost diff --git a/src/mutex.cpp b/src/mutex.cpp new file mode 100644 index 00000000..a8eca5f8 --- /dev/null +++ b/src/mutex.cpp @@ -0,0 +1,399 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include "timeconv.inl" + +#if defined(BOOST_HAS_WINTHREADS) +# include +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#endif + +/* + * Hack around various namespace challenged compilers + */ +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { + using ::clock_t; + using ::clock; +} // namespace std +#endif + +namespace boost +{ +#if defined(BOOST_HAS_WINTHREADS) + mutex::mutex() + { + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + assert(_mutex); + + if (!_mutex) + throw std::runtime_error("boost::mutex : failure to construct"); + } + + mutex::~mutex() + { + int res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void mutex::do_lock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + } + + void mutex::do_unlock() + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + + void mutex::do_lock(cv_state& state) + { + do_lock(); + } + + void mutex::do_unlock(cv_state& state) + { + do_unlock(); + } + + try_mutex::try_mutex() + { + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + assert(_mutex); + + if (!_mutex) + throw std::runtime_error("boost::try_mutex : failure to construct"); + } + + try_mutex::~try_mutex() + { + int res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void try_mutex::do_lock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + } + + bool try_mutex::do_trylock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), 0); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + return res == WAIT_OBJECT_0; + } + + void try_mutex::do_unlock() + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + + void try_mutex::do_lock(cv_state& state) + { + do_lock(); + } + + void try_mutex::do_unlock(cv_state& state) + { + do_unlock(); + } + + timed_mutex::timed_mutex() + { + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + assert(_mutex); + + if (!_mutex) + throw std::runtime_error("boost::timed_mutex : failure to construct"); + } + + timed_mutex::~timed_mutex() + { + int res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void timed_mutex::do_lock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + } + + bool timed_mutex::do_trylock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), 0); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + return res == WAIT_OBJECT_0; + } + + bool timed_mutex::do_timedlock(const xtime& xt) + { + unsigned milliseconds; + to_duration(xt, milliseconds); + + int res = WaitForSingleObject(reinterpret_cast(_mutex), milliseconds); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + return res == WAIT_OBJECT_0; + } + + void timed_mutex::do_unlock() + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + + void timed_mutex::do_lock(cv_state& state) + { + do_lock(); + } + + void timed_mutex::do_unlock(cv_state& state) + { + do_unlock(); + } +#elif defined(BOOST_HAS_PTHREADS) + mutex::mutex() + { + int res = pthread_mutex_init(&_mutex, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::mutex : failure to construct"); + } + + mutex::~mutex() + { + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + } + + void mutex::do_lock() + { + int res = pthread_mutex_lock(&_mutex); + if (res == EDEADLK) throw lock_error(); + assert(res == 0); + } + + void mutex::do_unlock() + { + int res = pthread_mutex_unlock(&_mutex); + if (res == EPERM) throw lock_error(); + assert(res == 0); + } + + void mutex::do_lock(cv_state& state) + { + } + + void mutex::do_unlock(cv_state& state) + { + state.pmutex = &_mutex; + } + + try_mutex::try_mutex() + { + int res = pthread_mutex_init(&_mutex, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::try_mutex : failure to construct"); + } + + try_mutex::~try_mutex() + { + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + } + + void try_mutex::do_lock() + { + int res = pthread_mutex_lock(&_mutex); + if (res == EDEADLK) throw lock_error(); + assert(res == 0); + } + + bool try_mutex::do_trylock() + { + int res = pthread_mutex_trylock(&_mutex); + if (res == EDEADLK) throw lock_error(); + assert(res == 0 || res == EBUSY); + return res == 0; + } + + void try_mutex::do_unlock() + { + int res = pthread_mutex_unlock(&_mutex); + if (res == EPERM) throw lock_error(); + assert(res == 0); + } + + void try_mutex::do_lock(cv_state& state) + { + } + + void try_mutex::do_unlock(cv_state& state) + { + state.pmutex = &_mutex; + } + + timed_mutex::timed_mutex() + : _locked(false) + { + int res = pthread_mutex_init(&_mutex, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::timed_mutex : failure to construct"); + + res = pthread_cond_init(&_cond, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::timed_mutex : failure to construct"); + } + + timed_mutex::~timed_mutex() + { + assert(!_locked); + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + + res = pthread_cond_destroy(&_cond); + assert(res == 0); + } + + void timed_mutex::do_lock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + while (_locked) + { + res = pthread_cond_wait(&_cond, &_mutex); + assert(res == 0); + } + + assert(!_locked); + _locked = true; + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + bool timed_mutex::do_trylock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + bool ret = false; + if (!_locked) + { + _locked = true; + ret = true; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return ret; + } + + bool timed_mutex::do_timedlock(const xtime& xt) + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + timespec ts; + to_timespec(xt, ts); + + while (_locked) + { + res = pthread_cond_timedwait(&_cond, &_mutex, &ts); + assert(res == 0 || res == ETIMEDOUT); + + if (res == ETIMEDOUT) + break; + } + + bool ret = false; + if (!_locked) + { + _locked = true; + ret = true; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return ret; + } + + void timed_mutex::do_unlock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + assert(_locked); + _locked = false; + + res = pthread_cond_signal(&_cond); + assert(res == 0); + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + void timed_mutex::do_lock(cv_state& state) + { + int res; + while (_locked) + { + res = pthread_cond_wait(&_cond, &_mutex); + assert(res == 0); + } + + assert(!_locked); + _locked = true; + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + void timed_mutex::do_unlock(cv_state& state) + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + assert(_locked); + _locked = false; + + res = pthread_cond_signal(&_cond); + assert(res == 0); + + state.pmutex = &_mutex; + } +#endif +} // namespace boost diff --git a/src/recursive_mutex.cpp b/src/recursive_mutex.cpp new file mode 100644 index 00000000..6e15f47d --- /dev/null +++ b/src/recursive_mutex.cpp @@ -0,0 +1,769 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + */ + +#include +#include +#include +#include +#include +#include +#include "timeconv.inl" + +#if defined(BOOST_HAS_WINTHREADS) +# include +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#endif + +/* + * Hack around various namespace challenged compilers + */ +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { + using ::clock_t; + using ::clock; +} // namespace std +#endif + +namespace boost { +#if defined(BOOST_HAS_WINTHREADS) + recursive_mutex::recursive_mutex() + : _count(0) + { + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + assert(_mutex); + + if (!_mutex) + throw std::runtime_error("boost::recursive_mutex : failure to construct"); + } + + recursive_mutex::~recursive_mutex() + { + int res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void recursive_mutex::do_lock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + if (++_count > 1) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + } + + void recursive_mutex::do_unlock() + { + if (--_count == 0) + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + } + + void recursive_mutex::do_lock(cv_state& state) + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + _count = state; + } + + void recursive_mutex::do_unlock(cv_state& state) + { + state = _count; + _count = 0; + + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + + recursive_try_mutex::recursive_try_mutex() + : _count(0) + { + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + assert(_mutex); + + if (!_mutex) + throw std::runtime_error("boost::recursive_try_mutex : failure to construct"); + } + + recursive_try_mutex::~recursive_try_mutex() + { + int res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void recursive_try_mutex::do_lock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + if (++_count > 1) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + } + + bool recursive_try_mutex::do_trylock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), 0); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + + if (res == WAIT_OBJECT_0) + { + if (+++_count > 1) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + return true; + } + return false; + } + + void recursive_try_mutex::do_unlock() + { + if (--_count == 0) + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + } + + void recursive_try_mutex::do_lock(cv_state& state) + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + _count = state; + } + + void recursive_try_mutex::do_unlock(cv_state& state) + { + state = _count; + _count = 0; + + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + + recursive_timed_mutex::recursive_timed_mutex() + : _count(0) + { + _mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + assert(_mutex); + + if (!_mutex) + throw std::runtime_error("boost::recursive_timed_mutex : failure to construct"); + } + + recursive_timed_mutex::~recursive_timed_mutex() + { + int res = CloseHandle(reinterpret_cast(_mutex)); + assert(res); + } + + void recursive_timed_mutex::do_lock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + if (++_count > 1) + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + } + + bool recursive_timed_mutex::do_trylock() + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), 0); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + + if (res == WAIT_OBJECT_0) + { + if (+++_count > 1) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + return true; + } + return false; + } + + bool recursive_timed_mutex::do_timedlock(const xtime& xt) + { + unsigned milliseconds; + to_duration(xt, milliseconds); + + int res = WaitForSingleObject(reinterpret_cast(_mutex), milliseconds); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + + if (res == WAIT_OBJECT_0) + { + if (+++_count > 1) + { + res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + return true; + } + return false; + } + + void recursive_timed_mutex::do_unlock() + { + if (--_count == 0) + { + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } + } + + void recursive_timed_mutex::do_lock(cv_state& state) + { + int res = WaitForSingleObject(reinterpret_cast(_mutex), INFINITE); + assert(res == WAIT_OBJECT_0); + + _count = state; + } + + void recursive_timed_mutex::do_unlock(cv_state& state) + { + state = _count; + _count = 0; + + int res = ReleaseMutex(reinterpret_cast(_mutex)); + assert(res); + } +#elif defined(BOOST_HAS_PTHREADS) + recursive_mutex::recursive_mutex() + : _count(0) +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + , _valid_id(false) +# endif + { + pthread_mutexattr_t attr; + int res = pthread_mutexattr_init(&attr); + assert(res == 0); + +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + assert(res == 0); +# endif + + res = pthread_mutex_init(&_mutex, &attr); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::recursive_mutex : failure to construct"); + +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + res = pthread_cond_init(&_unlocked, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::recursive_mutex : failure to construct"); +# endif + } + + recursive_mutex::~recursive_mutex() + { + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + res = pthread_cond_destroy(&_unlocked); + assert(res == 0); +# endif + } + + void recursive_mutex::do_lock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + if (++_count > 1) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } +# else + pthread_t tid = pthread_self(); + if (_valid_id && pthread_equal(_thread_id, tid)) + ++_count; + else + { + while (_valid_id) + { + res = pthread_cond_wait(&_unlocked, &_mutex); + assert(res == 0); + } + + _thread_id = tid; + _valid_id = true; + _count = 1; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); +# endif + } + + void recursive_mutex::do_unlock() + { +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + if (--_count == 0) + { + int res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } +# else + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + pthread_t tid = pthread_self(); + if (_valid_id && !pthread_equal(_thread_id, tid)) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + throw lock_error(); + } + + if (--_count == 0) + { + assert(_valid_id); + _valid_id = false; + + res = pthread_cond_signal(&_unlocked); + assert(res == 0); + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); +# endif + } + + void recursive_mutex::do_lock(cv_state& state) + { +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + _count = state.count; +# else + int res; + + while (_valid_id) + { + res = pthread_cond_wait(&_unlocked, &_mutex); + assert(res == 0); + } + + _thread_id = pthread_self(); + _valid_id = true; + _count = state.count; + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); +# endif + } + + void recursive_mutex::do_unlock(cv_state& state) + { +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + assert(_valid_id); + _valid_id = false; + + res = pthread_cond_signal(&_unlocked); + assert(res == 0); +# endif + + state.pmutex = &_mutex; + state.count = _count; + } + + recursive_try_mutex::recursive_try_mutex() + : _count(0) +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + , _valid_id(false) +# endif + { + pthread_mutexattr_t attr; + int res = pthread_mutexattr_init(&attr); + assert(res == 0); + +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + res = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); + assert(res == 0); +# endif + + res = pthread_mutex_init(&_mutex, &attr); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::recursive_try_mutex : failure to construct"); + +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + res = pthread_cond_init(&_unlocked, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::recursive_try_mutex : failure to construct"); +# endif + } + + recursive_try_mutex::~recursive_try_mutex() + { + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + res = pthread_cond_destroy(&_unlocked); + assert(res == 0); +# endif + } + + void recursive_try_mutex::do_lock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + if (++_count > 1) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } +# else + pthread_t tid = pthread_self(); + if (_valid_id && pthread_equal(_thread_id, tid)) + ++_count; + else + { + while (_valid_id) + { + res = pthread_cond_wait(&_unlocked, &_mutex); + assert(res == 0); + } + + _thread_id = tid; + _valid_id = true; + _count = 1; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); +# endif + } + + bool recursive_try_mutex::do_trylock() + { +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + int res = pthread_mutex_trylock(&_mutex); + assert(res == 0); + + if (res == 0) + { + if (++_count > 1) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + return true; + } + + return false; +# else + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + bool ret = false; + pthread_t tid = pthread_self(); + if (_valid_id && pthread_equal(_thread_id, tid)) + { + ++_count; + ret = true; + } + else if (!_valid_id) + { + _thread_id = tid; + _valid_id = true; + _count = 1; + ret = true; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return ret; +# endif + } + + void recursive_try_mutex::do_unlock() + { +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + if (--_count == 0) + { + int res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } +# else + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + pthread_t tid = pthread_self(); + if (_valid_id && !pthread_equal(_thread_id, tid)) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + throw lock_error(); + } + + if (--_count == 0) + { + assert(_valid_id); + _valid_id = false; + + res = pthread_cond_signal(&_unlocked); + assert(res == 0); + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); +# endif + } + + void recursive_try_mutex::do_lock(cv_state& state) + { +# if defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + _count = state.count; +# else + int res; + + while (_valid_id) + { + res = pthread_cond_wait(&_unlocked, &_mutex); + assert(res == 0); + } + + _thread_id = pthread_self(); + _valid_id = true; + _count = state.count; + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); +# endif + } + + void recursive_try_mutex::do_unlock(cv_state& state) + { +# if !defined(BOOST_HAS_PTHREAD_MUTEXATTR_SETTYPE) + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + assert(_valid_id); + _valid_id = false; + + res = pthread_cond_signal(&_unlocked); + assert(res == 0); +# endif + + state.pmutex = &_mutex; + state.count = _count; + } + + recursive_timed_mutex::recursive_timed_mutex() + : _valid_id(false), _count(0) + { + int res = pthread_mutex_init(&_mutex, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::recursive_timed_mutex : failure to construct"); + + res = pthread_cond_init(&_unlocked, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::recursive_timed_mutex : failure to construct"); + } + + recursive_timed_mutex::~recursive_timed_mutex() + { + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + + res = pthread_cond_destroy(&_unlocked); + assert(res == 0); + } + + void recursive_timed_mutex::do_lock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + pthread_t tid = pthread_self(); + if (_valid_id && pthread_equal(_thread_id, tid)) + ++_count; + else + { + while (_valid_id) + { + res = pthread_cond_wait(&_unlocked, &_mutex); + assert(res == 0); + } + + _thread_id = tid; + _valid_id = true; + _count = 1; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + bool recursive_timed_mutex::do_trylock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + bool ret = false; + pthread_t tid = pthread_self(); + if (_valid_id && pthread_equal(_thread_id, tid)) + { + ++_count; + ret = true; + } + else if (!_valid_id) + { + _thread_id = tid; + _valid_id = true; + _count = 1; + ret = true; + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return ret; + } + + bool recursive_timed_mutex::do_timedlock(const xtime& xt) + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + bool ret = false; + pthread_t tid = pthread_self(); + if (_valid_id && pthread_equal(_thread_id, tid)) + { + ++_count; + ret = true; + } + else + { + timespec ts; + to_timespec(xt, ts); + + while (_valid_id) + { + res = pthread_cond_timedwait(&_unlocked, &_mutex, &ts); + if (res == ETIMEDOUT) + break; + assert(res == 0); + } + + if (!_valid_id) + { + _thread_id = tid; + _valid_id = true; + _count = 1; + ret = true; + } + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return ret; + } + + void recursive_timed_mutex::do_unlock() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + pthread_t tid = pthread_self(); + if (_valid_id && !pthread_equal(_thread_id, tid)) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + throw lock_error(); + } + + if (--_count == 0) + { + assert(_valid_id); + _valid_id = false; + + res = pthread_cond_signal(&_unlocked); + assert(res == 0); + } + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + void recursive_timed_mutex::do_lock(cv_state& state) + { + int res; + + while (_valid_id) + { + res = pthread_cond_wait(&_unlocked, &_mutex); + assert(res == 0); + } + + _thread_id = pthread_self(); + _valid_id = true; + _count = state.count; + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + void recursive_timed_mutex::do_unlock(cv_state& state) + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + assert(_valid_id); + _valid_id = false; + + res = pthread_cond_signal(&_unlocked); + assert(res == 0); + + state.pmutex = &_mutex; + state.count = _count; + } +#endif +} // namespace boost diff --git a/src/semaphore.cpp b/src/semaphore.cpp new file mode 100644 index 00000000..3d9173e4 --- /dev/null +++ b/src/semaphore.cpp @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + * 22 May 01 Modified to use xtime for time outs. + */ + +#include +#include +#include +#include +#include +#include "timeconv.inl" + +#if defined(BOOST_HAS_WINTHREADS) +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +# include +# include +#endif + +/* + * Hack around various namespace challenged compilers + */ +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { + using ::clock_t; + using ::clock; +} // namespace std +#endif + +namespace boost { +#if defined(BOOST_HAS_WINTHREADS) + semaphore::semaphore(unsigned count, unsigned max) + { + if (static_cast(max) <= 0) + max = std::numeric_limits::max(); + + _sema = reinterpret_cast(CreateSemaphore(0, count, max, 0)); + assert(_sema != 0); + + if (!_sema) + throw std::runtime_error("boost::semaphore : failure to construct"); + } + + semaphore::~semaphore() + { + int res = CloseHandle(reinterpret_cast(_sema)); + assert(res); + } + + bool semaphore::up(unsigned count, unsigned* prev) + { + long p; + bool ret = !!ReleaseSemaphore(reinterpret_cast(_sema), count, &p); + assert(ret || GetLastError() == ERROR_TOO_MANY_POSTS); + + if (prev) + *prev = p; + + return ret; + } + + void semaphore::down() + { + int res = WaitForSingleObject(reinterpret_cast(_sema), INFINITE); + assert(res == WAIT_OBJECT_0); + } + + bool semaphore::down(const xtime& xt) + { + unsigned milliseconds; + to_duration(xt, milliseconds); + int res = WaitForSingleObject(reinterpret_cast(_sema), milliseconds); + assert(res != WAIT_FAILED && res != WAIT_ABANDONED); + return res == WAIT_OBJECT_0; + } +#elif defined(BOOST_HAS_PTHREADS) + semaphore::semaphore(unsigned count, unsigned max) + : _available(count), _max(max ? max : std::numeric_limits::max()) + { + int res = pthread_mutex_init(&_mutex, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::semaphore : failure to construct"); + + res = pthread_cond_init(&_cond, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::semaphore : failure to construct"); + } + + semaphore::~semaphore() + { + int res = pthread_mutex_destroy(&_mutex); + assert(res == 0); + + res = pthread_cond_destroy(&_cond); + assert(res == 0); + } + + bool semaphore::up(unsigned count, unsigned* prev) + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + if (prev) + *prev = _available; + + if (_available + count > _max) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return false; + } + + _available += count; + + res = pthread_cond_broadcast(&_cond); + assert(res == 0); + + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return true; + } + + void semaphore::down() + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + while (_available == 0) + { + res = pthread_cond_wait(&_cond, &_mutex); + assert(res == 0); + } + + _available--; + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + } + + bool semaphore::down(const xtime& xt) + { + int res = pthread_mutex_lock(&_mutex); + assert(res == 0); + + timespec ts; + to_timespec(xt, ts); + + while (_available == 0) + { + res = pthread_cond_timedwait(&_cond, &_mutex, &ts); + assert(res == 0 || res == ETIMEDOUT); + + if (res == ETIMEDOUT) + { + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return false; + } + } + + _available--; + res = pthread_mutex_unlock(&_mutex); + assert(res == 0); + return true; + } +#endif +} // namespace boost diff --git a/src/thread.cpp b/src/thread.cpp new file mode 100644 index 00000000..1042f439 --- /dev/null +++ b/src/thread.cpp @@ -0,0 +1,341 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + * 1 Jun 01 Added boost::thread initial implementation. + */ + +#include +#include +#include +#include +#include +#include + +#if defined(BOOST_HAS_WINTHREADS) +# include +# include +#elif defined(BOOST_HAS_PTHREADS) +# include +#endif + +#include "timeconv.inl" + +namespace +{ + class thread_counter + { + public: + thread_counter() : _threads(0) { } + + void start() + { + boost::mutex::lock lock(_mutex); + ++_threads; + } + + void stop() + { + boost::mutex::lock lock(_mutex); + if (--_threads == 0) + _cond.notify_all(); + } + + void wait() + { + boost::mutex::lock lock(_mutex); + while (_threads != 0) + _cond.wait(lock); + } + + private: + unsigned long _threads; + boost::mutex _mutex; + boost::condition _cond; + }; + + thread_counter threads; + boost::tss tss_state; +} + +namespace boost +{ + namespace detail + { + class thread_state + { + enum + { + creating, + created, + started, + finished + }; + + public: + thread_state(); + ~thread_state(); + + void add_ref(); + void release(); + bool is_alive(); + void join(); + +#if defined(BOOST_HAS_WINTHREADS) + static unsigned __stdcall thread_proc(void* param); +#elif defined(BOOST_HAS_PTHREADS) + static void* thread_proc(void* param); +#endif + + static thread_state* create(const boost::detail::threadfunc& func, void* param); + + private: + unsigned long _refs; + mutex _mutex; + condition _cond; + int _state; + threadfunc _func; + void* _param; +#if defined(BOOST_HAS_WINTHREADS) + HANDLE _thread; +#elif defined(BOOST_HAS_PTHREADS) + pthread_t _thread; +#endif + + // This line illustrate the internal compiler error encountered on MSVC +// boost::function _function; + }; + + thread_state::thread_state() + : _state(creating), _refs(2) // Both created thread and creating thread own at first + { + } + + thread_state::~thread_state() + { + if (_state == finished) + { +#if defined(BOOST_HAS_WINTHREADS) + int res = CloseHandle(_thread); + assert(res); +#elif defined(BOOST_HAS_PTHREADS) + int res = pthread_detach(_thread); + assert(res == 0); +#endif + } + } + + void thread_state::add_ref() + { + mutex::lock lock(_mutex); + ++_refs; + } + + void thread_state::release() + { + bool del = false; + { + mutex::lock lock(_mutex); + del = (--_refs == 0); + } + if (del) delete this; + } + + bool thread_state::is_alive() + { + mutex::lock lock(_mutex); + return _state == started; + } + + void thread_state::join() + { + mutex::lock lock(_mutex); + while (_state != finished) + _cond.wait(lock); + } + +#if defined(BOOST_HAS_WINTHREADS) + unsigned __stdcall thread_state::thread_proc(void* param) +#elif defined(BOOST_HAS_PTHREADS) + void* thread_state::thread_proc(void* param) +#endif + { + thread_state* state = static_cast(param); + assert(state); + + tss_state.set(state); + + { + mutex::lock lock(state->_mutex); + + while (state->_state != created) + state->_cond.wait(lock); + + state->_state = started; + state->_cond.notify_all(); + threads.start(); + } + + try + { + state->_func(state->_param); + } + catch (...) + { + } + + { + mutex::lock lock(state->_mutex); + state->_state = finished; + state->_cond.notify_all(); + } + + state->release(); + threads.stop(); + + return 0; + } + + thread_state* thread_state::create(const boost::detail::threadfunc& func, void* param) + { + thread_state* state = new thread_state(); + mutex::lock lock(state->_mutex); + + assert(func); + state->_func = func; + state->_param = param; + +#if defined(BOOST_HAS_WINTHREADS) + unsigned id; + state->_thread = (HANDLE)_beginthreadex(0, 0, &thread_proc, state, 0, &id); + assert(state->_thread); + + if (state->_thread == 0) + { + delete state; + return 0; + } +#elif defined(BOOST_HAS_PTHREADS) + int res = pthread_create(&state->_thread, 0, &thread_proc, state); + assert(res == 0); + + if (res != 0) + { + delete state; + return 0; + } +#endif + + state->_state = created; + state->_cond.notify_all(); + + while (state->_state != started) + state->_cond.wait(lock); + + return state; + } + } + + lock_error::lock_error() : std::runtime_error("thread lock error") + { + } + + thread::thread(const thread& other) + : _state(other._state) + { + if (_state) + _state->add_ref(); + } + + thread::~thread() + { + if (_state) + _state->release(); + } + + bool thread::is_alive() const + { + if (_state) + return _state->is_alive(); + return false; + } + + void thread::join() + { + if (_state) + _state->join(); + } + + thread thread::create(const detail::threadfunc& func, void* param) + { + thread temp; + temp._state = detail::thread_state::create(func, param); + return temp; + } + + thread thread::self() + { + thread temp; + temp._state = static_cast(tss_state.get()); + if (temp._state) + temp._state->add_ref(); + return temp; + } + + void thread::join_all() + { + threads.wait(); + } + + void thread::sleep(const xtime& xt) + { +#if defined(BOOST_HAS_WINTHREADS) + unsigned milliseconds; + to_duration(xt, milliseconds); + Sleep(milliseconds); +#elif defined(BOOST_HAS_PTHREADS) +# if defined(BOOST_HAS_PTHREAD_DELAY_NP) + timespec ts; + to_timespec(xt, ts); + int res = pthread_delay_np(&ts); + assert(res == 0); +# elif defined(BOOST_HAS_NANOSLEEP) + timespec ts; + to_timespec(xt, ts); + nanosleep(&ts, 0); +# else + boost::semaphore sema; + sema.down(xt); +# endif +#endif + } + + void thread::yield() + { +#if defined(BOOST_HAS_WINTHREADS) + Sleep(0); +#elif defined(BOOST_HAS_PTHREADS) +# if defined(BOOST_HAS_SCHED_YIELD) + int res = sched_yield(); + assert(res == 0); +# elif defined(BOOST_HAS_PTHREAD_YIELD) + int res = pthread_yield(); + assert(res == 0); +# else + xtime xt; + xtime_get(&xt, boost::TIME_UTC); + sleep(xt); +# endif +#endif + } +} \ No newline at end of file diff --git a/src/timeconv.inl b/src/timeconv.inl new file mode 100644 index 00000000..a5a9518d --- /dev/null +++ b/src/timeconv.inl @@ -0,0 +1,67 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 1 Jun 01 Initial creation. + */ + +namespace { + const unsigned MILLISECONDS_PER_SECOND = 1000; + const unsigned NANOSECONDS_PER_SECOND = 1000000000; + const unsigned NANOSECONDS_PER_MILLISECOND = 1000000; + + inline void to_time(unsigned milliseconds, boost::xtime& xt) + { + int res = boost::xtime_get(&xt, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + xt.sec += (milliseconds / MILLISECONDS_PER_SECOND); + xt.nsec += ((milliseconds % MILLISECONDS_PER_SECOND) * NANOSECONDS_PER_MILLISECOND); + + if (xt.nsec > NANOSECONDS_PER_SECOND) + { + ++xt.sec; + xt.nsec -= NANOSECONDS_PER_SECOND; + } + } + +#if defined(BOOST_HAS_PTHREADS) + inline void to_timespec(const boost::xtime& xt, timespec& ts) + { + ts.tv_sec = static_cast(xt.sec); + ts.tv_nsec = static_cast(xt.nsec); + } + + inline void to_time(unsigned milliseconds, timespec& ts) + { + boost::xtime xt; + to_time(milliseconds, xt); + to_timespec(xt, ts); + } +#endif + + inline void to_duration(const boost::xtime& xt, unsigned& milliseconds) + { + boost::xtime cur; + int res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (xt.sec < cur.sec || (xt.sec == cur.sec && xt.nsec < cur.nsec)) + milliseconds = 0; + else + { + milliseconds = ((xt.sec - cur.sec) * MILLISECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MILLISECOND/2)) + / NANOSECONDS_PER_MILLISECOND); + } + } +} \ No newline at end of file diff --git a/src/tss.cpp b/src/tss.cpp new file mode 100644 index 00000000..01f3cf37 --- /dev/null +++ b/src/tss.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 6 Jun 01 Initial version. + */ + +#include +#include + +#if defined(BOOST_HAS_WINTHREADS) +# include +#endif + +#include + +namespace boost +{ +#if defined(BOOST_HAS_WINTHREADS) + tss::tss() + { + _key = TlsAlloc(); + assert(_key != 0xFFFFFFFF); + + if (_key == 0xFFFFFFFF) + throw std::runtime_error("boost::tss : failure to construct"); + } + + tss::~tss() + { + int res = TlsFree(_key); + assert(res); + } + + void* tss::get() const + { + return TlsGetValue(_key); + } + + bool tss::set(void* value) + { + return TlsSetValue(_key, value); + } +#elif defined(BOOST_HAS_PTHREADS) + tss::tss() + { + int res = pthread_key_create(&_key, 0); + assert(res == 0); + + if (res != 0) + throw std::runtime_error("boost::tss : failure to construct"); + } + + tss::~tss() + { + int res = pthread_key_delete(_key); + assert(res == 0); + } + + void* tss::get() const + { + return pthread_getspecific(_key); + } + + bool tss::set(void* value) + { + return pthread_setspecific(_key, value) == 0; + } +#endif +} \ No newline at end of file diff --git a/src/xtime.cpp b/src/xtime.cpp new file mode 100644 index 00000000..23f31108 --- /dev/null +++ b/src/xtime.cpp @@ -0,0 +1,55 @@ +/* + * Copyright (C) 2001 + * William E. Kempf + * + * 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 and this permission notice appear + * in supporting documentation. William E. Kempf makes no representations + * about the suitability of this software for any purpose. + * It is provided "as is" without express or implied warranty. + * + * Revision History (excluding minor changes for specific compilers) + * 8 Feb 01 Initial version. + */ + +#include +#include + +#if defined(BOOST_HAS_FTIME) +# include +#endif + +namespace boost { + int xtime_get(struct xtime* xtp, int clock_type) + { + if (clock_type == TIME_UTC) + { +#if defined(BOOST_HAS_FTIME) + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + const __int64 TIMESPEC_TO_FILETIME_OFFSET = ((__int64)27111902 << 32) + (__int64)3577643008; + xtp->sec = (int)((*(__int64*)&ft - TIMESPEC_TO_FILETIME_OFFSET) / 10000000); + xtp->nsec = (int)((*(__int64*)&ft - TIMESPEC_TO_FILETIME_OFFSET - + ((__int64)xtp->sec * (__int64)10000000)) * 100); + return clock_type; +#elif defined(BOOST_HAS_GETTIMEOFDAY) + struct timeval tv; + gettimeofday(&tv, 0); + xtp->sec = tv.tv_sec; + xtp->nsec = tv.tv_usec * 1000; + return clock_type; +#elif defined(BOOST_HAS_CLOCK_GETTIME) + timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + xtp->sec = ts.tv_sec; + xtp->nsec = ts.tv_nsec; + return clock_type; +#else + return 0; +#endif + } + return 0; + } +} \ No newline at end of file diff --git a/test/Jamfile b/test/Jamfile index e69de29b..765fb691 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -0,0 +1,11 @@ +SubDir TOP test ; + +exe test_thread + : test_thread.cpp + ../src/threads + ## Requirements ## + : .. + multi + ## default-BUILD ## + : debug release + ; diff --git a/test/test_thread.cpp b/test/test_thread.cpp new file mode 100644 index 00000000..80b8a19c --- /dev/null +++ b/test/test_thread.cpp @@ -0,0 +1,426 @@ +#include +#include +#include +#include +#include +//#include +#include + +#define BOOST_INCLUDE_MAIN +#include + +#include + +template +void test_lock(M* dummy=0) +{ + typedef M mutex_type; + typedef typename M::lock lock_type; + + mutex_type mutex; + boost::condition condition; + + // Test the lock's constructors. + { + lock_type lock(mutex, false); + BOOST_TEST(!lock); + } + lock_type lock(mutex); + BOOST_TEST(lock); + + // Construct and initialize an xtime for a fast time out. + boost::xtime xt; + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + + // Test the lock and the mutex with condition variables. + // No one is going to notify this condition variable. We expect to + // time out. + BOOST_TEST(condition.timed_wait(lock, xt) == false); + BOOST_TEST(lock); + + // Test the lock and unlock methods. + lock.unlock(); + BOOST_TEST(!lock); + lock.lock(); + BOOST_TEST(lock); +} + +template +void test_trylock(M* dummy=0) +{ + typedef M mutex_type; + typedef typename M::trylock trylock_type; + + mutex_type mutex; + boost::condition condition; + + // Test the lock's constructors. + { + trylock_type lock(mutex); + BOOST_TEST(lock); + } + { + trylock_type lock(mutex, false); + BOOST_TEST(!lock); + } + trylock_type lock(mutex, true); + BOOST_TEST(lock); + + // Construct and initialize an xtime for a fast time out. + boost::xtime xt; + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + + // Test the lock and the mutex with condition variables. + // No one is going to notify this condition variable. We expect to + // time out. + BOOST_TEST(condition.timed_wait(lock, xt) == false); + BOOST_TEST(lock); + + // Test the lock, unlock and trylock methods. + lock.unlock(); + BOOST_TEST(!lock); + lock.lock(); + BOOST_TEST(lock); + lock.unlock(); + BOOST_TEST(!lock); + BOOST_TEST(lock.try_lock()); + BOOST_TEST(lock); +} + +template +void test_timedlock(M* dummy=0) +{ + typedef M mutex_type; + typedef typename M::timedlock timedlock_type; + + mutex_type mutex; + boost::condition condition; + + // Test the lock's constructors. + { + // Construct and initialize an xtime for a fast time out. + boost::xtime xt; + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + + timedlock_type lock(mutex, xt); + BOOST_TEST(lock); + } + { + timedlock_type lock(mutex, false); + BOOST_TEST(!lock); + } + timedlock_type lock(mutex, true); + BOOST_TEST(lock); + + // Construct and initialize an xtime for a fast time out. + boost::xtime xt; + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + + // Test the lock and the mutex with condition variables. + // No one is going to notify this condition variable. We expect to + // time out. + BOOST_TEST(condition.timed_wait(lock, xt) == false); + BOOST_TEST(lock); + + // Test the lock, unlock and timedlock methods. + lock.unlock(); + BOOST_TEST(!lock); + lock.lock(); + BOOST_TEST(lock); + lock.unlock(); + BOOST_TEST(!lock); + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + BOOST_TEST(lock.timed_lock(xt)); +} + +void test_mutex() +{ + typedef boost::mutex mutex; + test_lock(); +} + +void test_try_mutex() +{ + typedef boost::try_mutex mutex; + test_lock(); + test_trylock(); +} + +void test_timed_mutex() +{ + typedef boost::timed_mutex mutex; + test_lock(); + test_trylock(); + test_timedlock(); +} + +void test_recursive_mutex() +{ + typedef boost::recursive_mutex mutex; + test_lock(); + mutex mx; + mutex::lock lock1(mx); + mutex::lock lock2(mx); +} + +void test_recursive_try_mutex() +{ + typedef boost::recursive_try_mutex mutex; + test_lock(); + test_trylock(); + mutex mx; + mutex::lock lock1(mx); + mutex::lock lock2(mx); +} + +void test_recursive_timed_mutex() +{ + typedef boost::recursive_timed_mutex mutex; + test_lock(); + test_trylock(); + test_timedlock(); + mutex mx; + mutex::lock lock1(mx); + mutex::lock lock2(mx); +} + +struct condition_test_data +{ + condition_test_data() : notified(0), awoken(0) { } + + boost::mutex mutex; + boost::condition condition; + int notified; + int awoken; +}; + +void condition_test_thread(void* param) +{ + condition_test_data* data = static_cast(param); + boost::mutex::lock lock(data->mutex); + BOOST_TEST(lock); + while (!(data->notified > 0)) + data->condition.wait(lock); + BOOST_TEST(lock); + data->awoken++; +} + +void test_condition_notify_one() +{ + condition_test_data data; + + boost::thread::create(&condition_test_thread, &data); + + { + boost::mutex::lock lock(data.mutex); + BOOST_TEST(lock); + data.notified++; + data.condition.notify_one(); + } + + boost::thread::join_all(); + BOOST_TEST(data.awoken == 1); +} + +void test_condition_notify_all() +{ + const int NUMTHREADS = 5; + condition_test_data data; + + for (int i = 0; i < NUMTHREADS; ++i) + boost::thread::create(&condition_test_thread, &data); + + { + boost::mutex::lock lock(data.mutex); + BOOST_TEST(lock); + data.notified++; + data.condition.notify_all(); + } + + boost::thread::join_all(); + BOOST_TEST(data.awoken == NUMTHREADS); +} + +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(param); + + boost::mutex::lock lock(data->mutex); + BOOST_TEST(lock); + + // Test wait. + while (data->notified != 1) + data->condition.wait(lock); + BOOST_TEST(lock); + BOOST_TEST(data->notified == 1); + data->awoken++; + data->condition.notify_one(); + + // Test predicate wait. + data->condition.wait(lock, cond_predicate(data->notified, 2)); + BOOST_TEST(lock); + BOOST_TEST(data->notified == 2); + data->awoken++; + data->condition.notify_one(); + + // Test timed_wait. + boost::xtime xt; + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + while (data->notified != 3) + data->condition.timed_wait(lock, xt); + BOOST_TEST(lock); + BOOST_TEST(data->notified == 3); + data->awoken++; + data->condition.notify_one(); + + // Test predicate timed_wait. + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.sec += 2; + BOOST_TEST(data->condition.timed_wait(lock, xt, cond_predicate(data->notified, 4))); + BOOST_TEST(lock); + BOOST_TEST(data->notified == 4); + data->awoken++; +} + +void test_condition_waits() +{ + condition_test_data data; + + boost::thread::create(&condition_test_waits, &data); + + boost::xtime xt; + + { + boost::mutex::lock lock(data.mutex); + BOOST_TEST(lock); + + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.sec += 1; + boost::thread::sleep(xt); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 1) + data.condition.wait(lock); + BOOST_TEST(data.awoken == 1); + + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.sec += 1; + boost::thread::sleep(xt); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 2) + data.condition.wait(lock); + BOOST_TEST(data.awoken == 2); + + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.sec += 1; + boost::thread::sleep(xt); + data.notified++; + data.condition.notify_one(); + while (data.awoken != 3) + data.condition.wait(lock); + BOOST_TEST(data.awoken == 3); + } + + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.sec += 1; + boost::thread::sleep(xt); + data.notified++; + data.condition.notify_one(); + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.sec += 1; + boost::thread::sleep(xt); + boost::thread::join_all(); + BOOST_TEST(data.awoken == 4); +} + +void test_condition() +{ + test_condition_notify_one(); + test_condition_notify_all(); + test_condition_waits(); +} + +void test_semaphore() +{ + boost::xtime xt; + unsigned val; + boost::semaphore sema(0, 1); + + BOOST_TEST(sema.up(1, &val)); + BOOST_TEST(val == 0); + BOOST_TEST(!sema.up()); + + sema.down(); + BOOST_TEST(sema.up()); + + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + BOOST_TEST(sema.down(xt)); + BOOST_TEST(boost::xtime_get(&xt, boost::TIME_UTC) == boost::TIME_UTC); + xt.nsec += 100000000; + BOOST_TEST(!sema.down(xt)); +} + +/*void test_atomic_t() +{ + boost::atomic_t atomic; + BOOST_TEST(boost::increment(atomic) > 0); + BOOST_TEST(boost::decrement(atomic) == 0); + BOOST_TEST(boost::swap(atomic, 10) == 0); + BOOST_TEST(boost::swap(atomic, 0) == 10); + BOOST_TEST(boost::compare_swap(atomic, 20, 10) == 0); + BOOST_TEST(boost::compare_swap(atomic, 20, 0) == 0); + BOOST_TEST(boost::read(atomic) == 20); +}*/ + +void test_tss_thread(void*) +{ + static boost::tss value; + value.set(new int(0)); + + for (int i=0; i<1000; ++i) + { + int* pn = static_cast(value.get()); + BOOST_TEST(*pn == i); + ++*pn; + boost::thread::yield(); + } +} + +void test_tss() +{ + for (int i=0; i<5; ++i) + boost::thread::create(&test_tss_thread); + boost::thread::join_all(); +} + +int test_main(int, char*[]) +{ + test_mutex(); + test_try_mutex(); + test_timed_mutex(); + test_recursive_mutex(); + test_recursive_try_mutex(); + test_recursive_timed_mutex(); + test_condition(); + test_semaphore(); + test_tss(); + return 0; +} diff --git a/test/test_thread.dsp b/test/test_thread.dsp new file mode 100644 index 00000000..89652912 --- /dev/null +++ b/test/test_thread.dsp @@ -0,0 +1,166 @@ +# Microsoft Developer Studio Project File - Name="test_thread" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) Console Application" 0x0103 + +CFG=test_thread - Win32 Debug +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "test_thread.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "test_thread.mak" CFG="test_thread - Win32 Debug" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "test_thread - Win32 Release" (based on "Win32 (x86) Console Application") +!MESSAGE "test_thread - Win32 Debug" (based on "Win32 (x86) Console Application") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" +CPP=cl.exe +RSC=rc.exe + +!IF "$(CFG)" == "test_thread - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "Release" +# PROP BASE Intermediate_Dir "Release" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "test_thread_Release" +# PROP Intermediate_Dir "test_thread_Release" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD CPP /nologo /MT /W3 /GX /O2 /I ".." /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c +# ADD BASE RSC /l 0x409 /d "NDEBUG" +# ADD RSC /l 0x409 /d "NDEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /machine:I386 +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pthreadvce.lib /nologo /subsystem:console /machine:I386 + +!ELSEIF "$(CFG)" == "test_thread - Win32 Debug" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 1 +# PROP BASE Output_Dir "Debug" +# PROP BASE Intermediate_Dir "Debug" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 1 +# PROP Output_Dir "test_thread_Debug" +# PROP Intermediate_Dir "test_thread_Debug" +# PROP Ignore_Export_Lib 0 +# PROP Target_Dir "" +# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I ".." /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c +# ADD BASE RSC /l 0x409 /d "_DEBUG" +# ADD RSC /l 0x409 /d "_DEBUG" +BSC32=bscmake.exe +# ADD BASE BSC32 /nologo +# ADD BSC32 /nologo +LINK32=link.exe +# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept +# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib pthreadvce.lib /nologo /subsystem:console /debug /machine:I386 /pdbtype:sept + +!ENDIF + +# Begin Target + +# Name "test_thread - Win32 Release" +# Name "test_thread - Win32 Debug" +# Begin Group "Source Files" + +# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat" +# Begin Source File + +SOURCE=..\src\atomic.cpp +# End Source File +# Begin Source File + +SOURCE=..\src\condition.cpp +# End Source File +# Begin Source File + +SOURCE=..\src\mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\src\recursive_mutex.cpp +# End Source File +# Begin Source File + +SOURCE=..\src\semaphore.cpp +# End Source File +# Begin Source File + +SOURCE=.\test_thread.cpp +# End Source File +# Begin Source File + +SOURCE=..\src\thread.cpp +# End Source File +# Begin Source File + +SOURCE=..\src\xtime.cpp +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE=..\boost\thread\atomic.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\condition.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\config.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\mutex.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\recursive_mutex.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\semaphore.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\thread.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\xlock.hpp +# End Source File +# Begin Source File + +SOURCE=..\boost\thread\xtime.hpp +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe" +# End Group +# End Target +# End Project diff --git a/test/test_thread.dsw b/test/test_thread.dsw new file mode 100644 index 00000000..a1376e86 --- /dev/null +++ b/test/test_thread.dsw @@ -0,0 +1,29 @@ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "test_thread"=.\test_thread.dsp - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +