From f3af804ddb0c85e30197f64055895fad1f845161 Mon Sep 17 00:00:00 2001 From: "William E. Kempf" Date: Thu, 1 Nov 2001 16:18:57 +0000 Subject: [PATCH] Removed semaphores. Fixed some reported bugs. Switched to CRITICAL_SECTION implementations. [SVN r11501] --- build/Jamfile | 2 +- doc/faq.html | 3 +- doc/index.html | 2 - doc/introduction.html | 6 +- doc/rationale.html | 21 ++- doc/semaphore.html | 242 ---------------------------- example/tennis/tennis.cpp | 1 - include/boost/thread/exceptions.hpp | 11 +- include/boost/thread/semaphore.hpp | 57 ------- src/mutex.cpp | 17 +- src/recursive_mutex.cpp | 33 ++-- src/semaphore.cpp | 181 --------------------- src/thread.cpp | 7 +- test/test_thread.cpp | 36 ----- 14 files changed, 36 insertions(+), 583 deletions(-) delete mode 100644 doc/semaphore.html delete mode 100644 include/boost/thread/semaphore.hpp delete mode 100644 src/semaphore.cpp diff --git a/build/Jamfile b/build/Jamfile index a9391ac6..0ad9a350 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -39,7 +39,7 @@ if $(NT) # Base names of the source files for libboost_thread CPP_SOURCES = - condition mutex recursive_mutex semaphore thread tss xtime once ; + condition mutex recursive_mutex thread tss xtime once exceptions ; lib libboost_thread : ../src/$(CPP_SOURCES).cpp # requirements diff --git a/doc/faq.html b/doc/faq.html index 8cf2754d..d4b71303 100644 --- a/doc/faq.html +++ b/doc/faq.html @@ -54,8 +54,7 @@ take advantage of prior art to reduce errors) or had excessive dependencies on library components unrelated to threading. Existing C libraries couldn't meet our C++ requirements, and were also missing - certain features. For instance, POSIX threads doesn't support a - maximum value for semaphores. The WIN32 thread API lacks condition + certain features. For instance, the WIN32 thread API lacks condition variables, even though these are critical for the important Monitor pattern [Schmidt 00].

diff --git a/doc/index.html b/doc/index.html index 788ff83d..2f59aa6e 100644 --- a/doc/index.html +++ b/doc/index.html @@ -30,8 +30,6 @@ @@ -158,8 +157,7 @@

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, + synchronization. Types provided in this phase include the mutex/try_mutex/timed_mutex, recursive_mutex/recursive_try_mutex/recursive_timed_mutex, scoped_lock, diff --git a/doc/rationale.html b/doc/rationale.html index 8e7d0f39..43f79090 100644 --- a/doc/rationale.html +++ b/doc/rationale.html @@ -70,17 +70,16 @@ Supported in Boost.Threads

The Boost.Threads library supplies a set of low level primitives for - writing multi-threaded programs, such as semaphores, mutexes and - condition variables. In fact, the first release of Boost.Threads - supports only these low level primitives. However, computer science - research has shown that use of these primitives is difficult since - there's no way to mathematically prove that a usage pattern is - correct, meaning it doesn't result in race conditions or deadlocks. - There are several algebras (such as CSP, CCS and Join calculus) that - have been developed to help write provably correct parallel processes. - In order to prove the correctness these processes must be coded using - higher level abstractions. So why does Boost.Threads support the lower - level concepts?

+ writing multi-threaded programs, such as mutexes and condition variables. + In fact, the first release of Boost.Threads supports only these low level + primitives. However, computer science research has shown that use of these + primitives is difficult since there's no way to mathematically prove + that a usage pattern is correct, meaning it doesn't result in race + conditions or deadlocks. There are several algebras (such as CSP, CCS and + Join calculus) that have been developed to help write provably correct + parallel processes. In order to prove the correctness these processes must + be coded using higher level abstractions. So why does Boost.Threads support + the lower level concepts?

The reason is simple: the higher level concepts need to be implemented using at least some of the lower level concepts. So having diff --git a/doc/semaphore.html b/doc/semaphore.html deleted file mode 100644 index b2399ad8..00000000 --- a/doc/semaphore.html +++ /dev/null @@ -1,242 +0,0 @@ - - - - - - - Boost.Threads, semaphore - - - - - - - - - -
-

C++ Boost

-
-

Boost.Threads

- -

semaphore

-
-


- -

Introduction
- Header
- Synopsis
- Members
- Example

- -

Introduction

- -

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 never goes 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 decrement 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.

- -

Rationale: 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.

- -

Danger: Unlike the mutex models supplied by Boost.Threads, - there is no lock_concept for the - semaphore to help ensure proper usage. Great care must be taken when - using a semaphore object to ensure deadlock or race conditions do not occur.

- -

The dangers are spelled out by [Andrews-83] (function names - updated, see historical note below):

- -
-

Although semaphores can be used to program almost any kind of - synchronization, down() and up() are rather - unstructured primitives, and so it is easy to err when using them. - Execution of each critical section must begin with a down() - and end with a up() (on the same semaphore). Omitting a - down() or up(), or accidentally coding a down() - on one semaphore and a up() on another can have disastrous - effects, since mutually exclusive execution would no longer be - ensured. Also, when using semaphores, a programmer can forget to - include in critical sections all statements that reference shared - objects. This, too, could destroy the mutual exclusion required - within critical sections. A second difficulty with using semaphores - is that both condition synchronization and mutual exclusion are - programmed using the same pair of primitives. This makes it - difficult to identify the purpose of a given down() or - up() operation without looking at the other operations on the - corresponding semaphore. Since mutual exclusion and condition - synchronization are distinct concepts, they should have distinct - notations.

-
- -

Historical note: Dijkstra's original name for - down() was P (short for the Dutch "passeren", - "to pass"), and for up() was V (short for the - Dutch "vrygeven", "to release").

- -

Header

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

Synopsis

-
-namespace boost
-{
-    class semaphore : private boost::noncopyable // Exposition only.
-        // Class semaphore meets the NonCopyable requirement.
-    {
-    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);
-    private:
-        unsigned m_count;  exposition only [ISO 17.3.2.3/2]
-        unsigned m_max;    exposition only [ISO 17.3.2.3/2]
-    };
-}
-
- -

Members

-
- -

Constructor

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

Effects: As if:

- -

     m_count = count;
-      m_max = (max == 0 ? - std::numeric_limits<unsigned>::max() ? max );

-
- -

Destructor

-
-    ~semaphore();
-
- -

Effects: Destroys *this.

-
- -

up

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

Effects: As if:

- -

    unsigned ct;
-     bool ret;
-     { // as a single atomic operation:
-          ct = m_count;
-          if (m_count == m_max) - ret = false;
-          else
-          {
-              - ret = true;
-              - ++m_count;
-          }
-     }
-     if (prev) *prev = m_count;
-     return ret;

-
- -

down

-
-    void down();
-
- -

Effects: If m_count == 0, places the current - thread in the blocked state until - m_count != 0. Finally, --m_count.

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

Effects: If m_count == 0, places the current - thread in the blocked state until - m_count != 0 or xt is reached. Finally, - --m_count.

- -

Returns: If xt was reached, true, else false.

-
- -

Example Usage

-
-#include <boost/thread/semaphore.hpp>
-#include <boost/thread/thread.hpp>
-#include <iostream>
-
-int global_data = 0;
-boost::semaphore global_semaphore(1);
-
-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;
-    boost::thread_group thrds;
-    for (int i=0; i <  num_threads; ++i)
-        thrds.create_thread(&change_global_data, 0);
-      
-    thrds.join_all();
-   
-    return 0;
-}
-
- -

The output is:

-
-global_data == 1
-global_data == 2
-global_data == 3
-global_data == 4
-
-
- -

Revised - 01 - October, 2001 -

- -

© Copyright - William E. Kempf 2001 all rights reserved.

- - - diff --git a/example/tennis/tennis.cpp b/example/tennis/tennis.cpp index 68e60c7e..6290329f 100644 --- a/example/tennis/tennis.cpp +++ b/example/tennis/tennis.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include diff --git a/include/boost/thread/exceptions.hpp b/include/boost/thread/exceptions.hpp index bc8e142a..6eb30bf2 100644 --- a/include/boost/thread/exceptions.hpp +++ b/include/boost/thread/exceptions.hpp @@ -9,13 +9,6 @@ // about the suitability of this software for any purpose. // It is provided "as is" without express or implied warranty. -// This file is used to configure Boost.Threads during development -// in order to decouple dependency on any Boost release. Once -// accepted into Boost these contents will be moved to -// or some other appropriate build configuration and all -// #include statements will be changed -// accordingly. - #ifndef BOOST_THREAD_EXCEPTIONS_PDM070801_H #define BOOST_THREAD_EXCEPTIONS_PDM070801_H @@ -32,13 +25,13 @@ namespace boost { class lock_error : public std::runtime_error { public: - lock_error() : std::runtime_error("thread lock error") { } + lock_error(); }; class thread_resource_error : public std::runtime_error { public: - thread_resource_error() : std::runtime_error("thread resource error") { } + thread_resource_error(); }; } // namespace boost diff --git a/include/boost/thread/semaphore.hpp b/include/boost/thread/semaphore.hpp deleted file mode 100644 index b4db26fc..00000000 --- a/include/boost/thread/semaphore.hpp +++ /dev/null @@ -1,57 +0,0 @@ -// 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. - -#ifndef BOOST_SEMAPHORE_WEK070601_HPP -#define BOOST_SEMAPHORE_WEK070601_HPP - -#include -#ifndef BOOST_HAS_THREADS -# error Thread support is unavailable! -#endif - -#include - -#if defined(BOOST_HAS_PTHREADS) -# include -#endif - -namespace boost { - -struct xtime; - -class semaphore : private 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); - -private: -#if defined(BOOST_HAS_WINTHREADS) - void* m_sema; -#elif defined(BOOST_HAS_PTHREADS) - pthread_mutex_t m_mutex; - pthread_cond_t m_condition; - unsigned m_available; - unsigned m_max; -#endif -}; - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 22 May 01 WEKEMPF Modified to use xtime for time outs. - -#endif // BOOST_SEMAPHORE_WEK070601_HPP diff --git a/src/mutex.cpp b/src/mutex.cpp index f9360052..3d2e6932 100644 --- a/src/mutex.cpp +++ b/src/mutex.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include "timeconv.inl" #if defined(BOOST_HAS_WINTHREADS) @@ -30,30 +31,26 @@ namespace boost { #if defined(BOOST_HAS_WINTHREADS) mutex::mutex() { - m_mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + m_mutex = reinterpret_cast(new(std::nothrow) CRITICAL_SECTION); if (!m_mutex) throw thread_resource_error(); + InitializeCriticalSection(reinterpret_cast(m_mutex)); } mutex::~mutex() { - int res = 0; - res = CloseHandle(reinterpret_cast(m_mutex)); - assert(res); + DeleteCriticalSection(reinterpret_cast(m_mutex)); + delete reinterpret_cast(m_mutex); } void mutex::do_lock() { - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); + EnterCriticalSection(reinterpret_cast(m_mutex)); } void mutex::do_unlock() { - int res = 0; - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); + LeaveCriticalSection(reinterpret_cast(m_mutex)); } void mutex::do_lock(cv_state&) diff --git a/src/recursive_mutex.cpp b/src/recursive_mutex.cpp index 0128f6e3..e5cffc71 100644 --- a/src/recursive_mutex.cpp +++ b/src/recursive_mutex.cpp @@ -30,47 +30,35 @@ namespace boost { recursive_mutex::recursive_mutex() : m_count(0) { - m_mutex = reinterpret_cast(CreateMutex(0, 0, 0)); + m_mutex = reinterpret_cast(new(std::nothrow) CRITICAL_SECTION); if (!m_mutex) throw thread_resource_error(); + InitializeCriticalSection(reinterpret_cast(m_mutex)); } recursive_mutex::~recursive_mutex() { - int res = 0; - res = CloseHandle(reinterpret_cast(m_mutex)); - assert(res); + DeleteCriticalSection(reinterpret_cast(m_mutex)); + delete reinterpret_cast(m_mutex); } void recursive_mutex::do_lock() { - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); + EnterCriticalSection(reinterpret_cast(m_mutex)); if (++m_count > 1) - { - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - } + LeaveCriticalSection(reinterpret_cast(m_mutex)); } void recursive_mutex::do_unlock() { if (--m_count == 0) - { - int res = 0; - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); - } + LeaveCriticalSection(reinterpret_cast(m_mutex)); } void recursive_mutex::do_lock(cv_state& state) { - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_mutex), INFINITE); - assert(res == WAIT_OBJECT_0); - + EnterCriticalSection(reinterpret_cast(m_mutex)); m_count = state; } @@ -78,10 +66,7 @@ void recursive_mutex::do_unlock(cv_state& state) { state = m_count; m_count = 0; - - int res = 0; - res = ReleaseMutex(reinterpret_cast(m_mutex)); - assert(res); + LeaveCriticalSection(reinterpret_cast(m_mutex)); } recursive_try_mutex::recursive_try_mutex() diff --git a/src/semaphore.cpp b/src/semaphore.cpp deleted file mode 100644 index 1f98f2bc..00000000 --- a/src/semaphore.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// 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. - -#define NOMINMAX - -#include -#include -#include -#include -#include -#include -#include "timeconv.inl" - -#if defined(BOOST_HAS_WINTHREADS) -# include -#elif defined(BOOST_HAS_PTHREADS) -# include -# include -# include -# include -#endif - -namespace boost { - -#if defined(BOOST_HAS_WINTHREADS) -semaphore::semaphore(unsigned count, unsigned max) -{ - if (static_cast(max) <= 0) - max = std::numeric_limits::max(); - - m_sema = reinterpret_cast(CreateSemaphore(0, count, max, 0)); - if (!m_sema) - throw thread_resource_error(); -} - -semaphore::~semaphore() -{ - int res = 0; - res = CloseHandle(reinterpret_cast(m_sema)); - assert(res); -} - -bool semaphore::up(unsigned count, unsigned* prev) -{ - long p; - bool ret = !!ReleaseSemaphore(reinterpret_cast(m_sema), count, &p); - - if (prev) - *prev = p; - - return ret; -} - -void semaphore::down() -{ - int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_sema), INFINITE); - assert(res == WAIT_OBJECT_0); -} - -bool semaphore::down(const xtime& xt) -{ - unsigned milliseconds; - to_duration(xt, milliseconds); - unsigned int res = 0; - res = WaitForSingleObject(reinterpret_cast(m_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) - : m_available(count), m_max(max ? max : std::numeric_limits::max()) -{ - int res = 0; - res = pthread_mutex_init(&m_mutex, 0); - if (res != 0) - throw thread_resource_error(); - - res = pthread_cond_init(&m_condition, 0); - if (res != 0) - { - pthread_mutex_destroy(&m_mutex); - throw thread_resource_error(); - } -} - -semaphore::~semaphore() -{ - int res = 0; - res = pthread_mutex_destroy(&m_mutex); - assert(res == 0); - - res = pthread_cond_destroy(&m_condition); - assert(res == 0); -} - -bool semaphore::up(unsigned count, unsigned* prev) -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - if (prev) - *prev = m_available; - - if (m_available + count > m_max) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return false; - } - - m_available += count; - - res = pthread_cond_broadcast(&m_condition); - assert(res == 0); - - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return true; -} - -void semaphore::down() -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - while (m_available == 0) - { - res = pthread_cond_wait(&m_condition, &m_mutex); - assert(res == 0); - } - - m_available--; - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); -} - -bool semaphore::down(const xtime& xt) -{ - int res = 0; - res = pthread_mutex_lock(&m_mutex); - assert(res == 0); - - timespec ts; - to_timespec(xt, ts); - - while (m_available == 0) - { - res = pthread_cond_timedwait(&m_condition, &m_mutex, &ts); - assert(res == 0 || res == ETIMEDOUT); - - if (res == ETIMEDOUT) - { - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return false; - } - } - - m_available--; - res = pthread_mutex_unlock(&m_mutex); - assert(res == 0); - return true; -} -#endif - -} // namespace boost - -// Change Log: -// 8 Feb 01 WEKEMPF Initial version. -// 22 May 01 WEKEMPF Modified to use xtime for time outs. diff --git a/src/thread.cpp b/src/thread.cpp index 0b221569..d953d1e9 100644 --- a/src/thread.cpp +++ b/src/thread.cpp @@ -10,7 +10,6 @@ // It is provided "as is" without express or implied warranty. #include -#include #include #include #include @@ -167,8 +166,10 @@ void thread::sleep(const xtime& xt) // an absolute time. nanosleep(&ts, 0); # else - semaphore sema; - sema.down(xt); + mutex mx; + mutex::scoped_lock lock(mx); + condition cond; + cond.timed_wait(lock, xt); # endif #endif } diff --git a/test/test_thread.cpp b/test/test_thread.cpp index bba902d9..24648511 100644 --- a/test/test_thread.cpp +++ b/test/test_thread.cpp @@ -2,8 +2,6 @@ #include #include #include -#include -//#include #include #include #include @@ -373,39 +371,6 @@ void test_condition() 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); -}*/ - boost::mutex tss_mutex; int tss_instances = 0; @@ -480,7 +445,6 @@ int test_main(int, char*[]) test_recursive_try_mutex(); test_recursive_timed_mutex(); test_condition(); - test_semaphore(); test_tss(); test_once(); return 0;