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 @@
Overview
- Class semaphore
-
Mutex Concepts
diff --git a/doc/introduction.html b/doc/introduction.html
index f7762c59..ffa66e15 100644
--- a/doc/introduction.html
+++ b/doc/introduction.html
@@ -87,8 +87,7 @@
Dangerous features, or features which
may be misused, are identified as such in the
documentation to make users aware of potential
- pitfalls. For example, see Semaphore .
+ pitfalls.
@@ -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
-
-
-
-
-
-
-
-
-
-
- Boost.Threads
-
- semaphore
-
-
-
-
-
- Introduction
- Header
- Synopsis
- Members
- Example
-
-
-
- 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").
-
-
-
-#include <boost/thread/semaphore.hpp>
-
-
-
-
-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]
- };
-}
-
-
-
-
-
- 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.
-
-
-
-
-#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;