|
Boost-Commit : |
From: anthony_at_[hidden]
Date: 2007-11-01 13:07:48
Author: anthonyw
Date: 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
New Revision: 40647
URL: http://svn.boost.org/trac/boost/changeset/40647
Log:
condition wait and sleep are now cancellation points
Added:
trunk/boost/thread/pthread/condition_variable_fwd.hpp (contents, props changed)
trunk/boost/thread/pthread/thread_data.hpp (contents, props changed)
Text files modified:
trunk/boost/thread/pthread/condition_variable.hpp | 124 ++++++++++++++++--------------------
trunk/boost/thread/pthread/thread.hpp | 35 ----------
trunk/boost/thread/win32/condition_variable.hpp | 5 -
trunk/boost/thread/win32/thread.hpp | 6 +
trunk/libs/thread/src/pthread/thread.cpp | 134 ++++++++++++++++++++++-----------------
trunk/libs/thread/test/test_condition.cpp | 23 ++++++
6 files changed, 158 insertions(+), 169 deletions(-)
Modified: trunk/boost/thread/pthread/condition_variable.hpp
==============================================================================
--- trunk/boost/thread/pthread/condition_variable.hpp (original)
+++ trunk/boost/thread/pthread/condition_variable.hpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -13,78 +13,56 @@
#include <pthread.h>
#include "timespec.hpp"
#include "pthread_mutex_scoped_lock.hpp"
+#include "thread_data.hpp"
+#include "condition_variable_fwd.hpp"
namespace boost
{
- class condition_variable
+ inline condition_variable::condition_variable()
{
- private:
- pthread_cond_t cond;
-
- condition_variable(condition_variable&);
- condition_variable& operator=(condition_variable&);
- public:
- condition_variable()
- {
- int const res=pthread_cond_init(&cond,NULL);
- if(res)
- {
- throw thread_resource_error();
- }
- }
- ~condition_variable()
- {
- int const res=pthread_cond_destroy(&cond);
- BOOST_ASSERT(!res);
- }
-
- void wait(unique_lock<mutex>& m)
- {
- int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle());
- BOOST_ASSERT(!cond_res);
- }
-
- template<typename predicate_type>
- void wait(unique_lock<mutex>& m,predicate_type pred)
+ int const res=pthread_cond_init(&cond,NULL);
+ if(res)
{
- while(!pred()) wait(m);
+ throw thread_resource_error();
}
+ }
+ inline condition_variable::~condition_variable()
+ {
+ int const res=pthread_cond_destroy(&cond);
+ BOOST_ASSERT(!res);
+ }
- bool timed_wait(unique_lock<mutex>& m,boost::system_time const& wait_until)
- {
- struct timespec const timeout=detail::get_timespec(wait_until);
- int const cond_res=pthread_cond_timedwait(&cond,m.mutex()->native_handle(),&timeout);
- if(cond_res==ETIMEDOUT)
- {
- return false;
- }
- BOOST_ASSERT(!cond_res);
- return true;
- }
+ inline void condition_variable::wait(unique_lock<mutex>& m)
+ {
+ detail::cancel_wrapper allow_cancel(&cond);
+ int const cond_res=pthread_cond_wait(&cond,m.mutex()->native_handle());
+ BOOST_ASSERT(!cond_res);
+ }
- template<typename predicate_type>
- bool timed_wait(unique_lock<mutex>& m,boost::system_time const& wait_until,predicate_type pred)
- {
- while (!pred())
- {
- if(!timed_wait(m, wait_until))
- return false;
- }
- return true;
- }
+ inline bool condition_variable::timed_wait(unique_lock<mutex>& m,boost::system_time const& wait_until)
+ {
+ detail::cancel_wrapper allow_cancel(&cond);
+ struct timespec const timeout=detail::get_timespec(wait_until);
+ int const cond_res=pthread_cond_timedwait(&cond,m.mutex()->native_handle(),&timeout);
+ if(cond_res==ETIMEDOUT)
+ {
+ return false;
+ }
+ BOOST_ASSERT(!cond_res);
+ return true;
+ }
- void notify_one()
- {
- int const res=pthread_cond_signal(&cond);
- BOOST_ASSERT(!res);
- }
+ inline void condition_variable::notify_one()
+ {
+ int const res=pthread_cond_signal(&cond);
+ BOOST_ASSERT(!res);
+ }
- void notify_all()
- {
- int const res=pthread_cond_broadcast(&cond);
- BOOST_ASSERT(!res);
- }
- };
+ inline void condition_variable::notify_all()
+ {
+ int const res=pthread_cond_broadcast(&cond);
+ BOOST_ASSERT(!res);
+ }
class condition_variable_any
{
@@ -123,11 +101,14 @@
{
int res=0;
{
- boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
- m.unlock();
- res=pthread_cond_wait(&cond,&internal_mutex);
+ detail::cancel_wrapper allow_cancel(&cond);
+ {
+ boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
+ m.unlock();
+ res=pthread_cond_wait(&cond,&internal_mutex);
+ }
+ m.lock();
}
- m.lock();
if(res)
{
throw condition_error();
@@ -146,11 +127,14 @@
struct timespec const timeout=detail::get_timespec(wait_until);
int res=0;
{
- boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
- m.unlock();
- res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout);
+ detail::cancel_wrapper allow_cancel(&cond);
+ {
+ boost::pthread::pthread_mutex_scoped_lock internal_lock(&internal_mutex);
+ m.unlock();
+ res=pthread_cond_timedwait(&cond,&internal_mutex,&timeout);
+ }
+ m.lock();
}
- m.lock();
if(res==ETIMEDOUT)
{
return false;
Added: trunk/boost/thread/pthread/condition_variable_fwd.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/thread/pthread/condition_variable_fwd.hpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -0,0 +1,53 @@
+#ifndef BOOST_THREAD_PTHREAD_CONDITION_VARIABLE_FWD_HPP
+#define BOOST_THREAD_PTHREAD_CONDITION_VARIABLE_FWD_HPP
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+// (C) Copyright 2007 Anthony Williams
+
+#include <pthread.h>
+#include <boost/thread/locks.hpp>
+#include <boost/thread/thread_time.hpp>
+
+namespace boost
+{
+ class condition_variable
+ {
+ private:
+ pthread_cond_t cond;
+
+ condition_variable(condition_variable&);
+ condition_variable& operator=(condition_variable&);
+
+ struct cancel_wrapper;
+ public:
+ condition_variable();
+ ~condition_variable();
+
+ void wait(unique_lock<mutex>& m);
+
+ template<typename predicate_type>
+ void wait(unique_lock<mutex>& m,predicate_type pred)
+ {
+ while(!pred()) wait(m);
+ }
+
+ bool timed_wait(unique_lock<mutex>& m,boost::system_time const& wait_until);
+
+ template<typename predicate_type>
+ bool timed_wait(unique_lock<mutex>& m,boost::system_time const& wait_until,predicate_type pred)
+ {
+ while (!pred())
+ {
+ if(!timed_wait(m, wait_until))
+ return false;
+ }
+ return true;
+ }
+
+ void notify_one();
+ void notify_all();
+ };
+}
+
+#endif
Modified: trunk/boost/thread/pthread/thread.hpp
==============================================================================
--- trunk/boost/thread/pthread/thread.hpp (original)
+++ trunk/boost/thread/pthread/thread.hpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -20,10 +20,10 @@
#include <boost/optional.hpp>
#include <boost/thread/detail/move.hpp>
#include <boost/shared_ptr.hpp>
+#include "thread_data.hpp"
namespace boost
{
-
class thread;
namespace detail
@@ -81,39 +81,6 @@
};
}
- class thread_cancelled
- {};
-
- namespace detail
- {
- struct thread_exit_callback_node;
-
- struct thread_data_base
- {
- boost::shared_ptr<thread_data_base> self;
- pthread_t thread_handle;
- boost::mutex data_mutex;
- boost::condition_variable done_condition;
- bool done;
- bool join_started;
- bool joined;
- boost::detail::thread_exit_callback_node* thread_exit_callbacks;
- bool cancel_enabled;
- bool cancel_requested;
-
- thread_data_base():
- done(false),join_started(false),joined(false),
- thread_exit_callbacks(0),
- cancel_enabled(true),
- cancel_requested(false)
- {}
- virtual ~thread_data_base()
- {}
-
- virtual void run()=0;
- };
- }
-
struct xtime;
class BOOST_THREAD_DECL thread
{
Added: trunk/boost/thread/pthread/thread_data.hpp
==============================================================================
--- (empty file)
+++ trunk/boost/thread/pthread/thread_data.hpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -0,0 +1,93 @@
+#ifndef BOOST_THREAD_PTHREAD_THREAD_DATA_HPP
+#define BOOST_THREAD_PTHREAD_THREAD_DATA_HPP
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+// (C) Copyright 2007 Anthony Williams
+
+#include <boost/thread/detail/config.hpp>
+#include <boost/shared_ptr.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/optional.hpp>
+#include <pthread.h>
+#include "condition_variable_fwd.hpp"
+
+namespace boost
+{
+ class thread_cancelled
+ {};
+
+ namespace detail
+ {
+ struct thread_exit_callback_node;
+
+ struct thread_data_base
+ {
+ boost::shared_ptr<thread_data_base> self;
+ pthread_t thread_handle;
+ boost::mutex data_mutex;
+ boost::condition_variable done_condition;
+ boost::mutex sleep_mutex;
+ boost::condition_variable sleep_condition;
+ bool done;
+ bool join_started;
+ bool joined;
+ boost::detail::thread_exit_callback_node* thread_exit_callbacks;
+ bool cancel_enabled;
+ bool cancel_requested;
+ pthread_cond_t* current_cond;
+
+ thread_data_base():
+ done(false),join_started(false),joined(false),
+ thread_exit_callbacks(0),
+ cancel_enabled(true),
+ cancel_requested(false),
+ current_cond(0)
+ {}
+ virtual ~thread_data_base()
+ {}
+
+ virtual void run()=0;
+ };
+
+ BOOST_THREAD_DECL thread_data_base* get_current_thread_data();
+
+ class cancel_wrapper
+ {
+ thread_data_base* const thread_info;
+
+ void check_cancel()
+ {
+ if(thread_info->cancel_requested)
+ {
+ thread_info->cancel_requested=false;
+ throw thread_cancelled();
+ }
+ }
+
+ public:
+ explicit cancel_wrapper(pthread_cond_t* cond):
+ thread_info(detail::get_current_thread_data())
+ {
+ if(thread_info && thread_info->cancel_enabled)
+ {
+ lock_guard<mutex> guard(thread_info->data_mutex);
+ check_cancel();
+ thread_info->current_cond=cond;
+ }
+ }
+ ~cancel_wrapper()
+ {
+ if(thread_info && thread_info->cancel_enabled)
+ {
+ lock_guard<mutex> guard(thread_info->data_mutex);
+ thread_info->current_cond=NULL;
+ check_cancel();
+ }
+ }
+ };
+ }
+}
+
+
+#endif
Modified: trunk/boost/thread/win32/condition_variable.hpp
==============================================================================
--- trunk/boost/thread/win32/condition_variable.hpp (original)
+++ trunk/boost/thread/win32/condition_variable.hpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -156,13 +156,10 @@
++generations[0].count;
sem=detail::win32::duplicate_handle(generations[0].semaphore);
}
- unsigned long const wait_result=detail::win32::WaitForSingleObject(sem,::boost::detail::get_milliseconds_until(wait_until));
-
- if(wait_result==detail::win32::timeout)
+ if(!this_thread::cancellable_wait(sem,::boost::detail::get_milliseconds_until(wait_until)))
{
break;
}
- BOOST_ASSERT(!wait_result);
unsigned long const woken_result=detail::win32::WaitForSingleObject(local_wake_sem,0);
BOOST_ASSERT(woken_result==detail::win32::timeout || woken_result==0);
Modified: trunk/boost/thread/win32/thread.hpp
==============================================================================
--- trunk/boost/thread/win32/thread.hpp (original)
+++ trunk/boost/thread/win32/thread.hpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -197,6 +197,10 @@
thread::id BOOST_THREAD_DECL get_id();
bool BOOST_THREAD_DECL cancellable_wait(detail::win32::handle handle_to_wait_for,unsigned long milliseconds);
+ inline bool cancellable_wait(unsigned long milliseconds)
+ {
+ return cancellable_wait(detail::win32::invalid_handle_value,milliseconds);
+ }
void BOOST_THREAD_DECL cancellation_point();
bool BOOST_THREAD_DECL cancellation_enabled();
@@ -207,7 +211,7 @@
template<typename TimeDuration>
void sleep(TimeDuration const& rel_time)
{
- cancellable_wait(detail::win32::invalid_handle_value,static_cast<unsigned long>(rel_time.total_milliseconds()));
+ cancellable_wait(static_cast<unsigned long>(rel_time.total_milliseconds()));
}
}
Modified: trunk/libs/thread/src/pthread/thread.cpp
==============================================================================
--- trunk/libs/thread/src/pthread/thread.cpp (original)
+++ trunk/libs/thread/src/pthread/thread.cpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -24,41 +24,42 @@
boost::detail::thread_exit_function_base* func;
thread_exit_callback_node* next;
};
- }
- namespace
- {
- boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT;
- pthread_key_t current_thread_tls_key;
- extern "C"
+ namespace
{
- void tls_destructor(void* data)
+ boost::once_flag current_thread_tls_init_flag=BOOST_ONCE_INIT;
+ pthread_key_t current_thread_tls_key;
+
+ extern "C"
{
- boost::detail::thread_data_base* thread_info=static_cast<boost::detail::thread_data_base*>(data);
- if(thread_info)
+ void tls_destructor(void* data)
{
- while(thread_info->thread_exit_callbacks)
+ boost::detail::thread_data_base* thread_info=static_cast<boost::detail::thread_data_base*>(data);
+ if(thread_info)
{
- boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks;
- thread_info->thread_exit_callbacks=current_node->next;
- if(current_node->func)
+ while(thread_info->thread_exit_callbacks)
{
- (*current_node->func)();
- delete current_node->func;
+ boost::detail::thread_exit_callback_node* const current_node=thread_info->thread_exit_callbacks;
+ thread_info->thread_exit_callbacks=current_node->next;
+ if(current_node->func)
+ {
+ (*current_node->func)();
+ delete current_node->func;
+ }
+ delete current_node;
}
- delete current_node;
}
}
}
- }
- void create_current_thread_tls_key()
- {
- int const res=pthread_key_create(¤t_thread_tls_key,NULL);
- BOOST_ASSERT(!res);
+ void create_current_thread_tls_key()
+ {
+ int const res=pthread_key_create(¤t_thread_tls_key,NULL);
+ BOOST_ASSERT(!res);
+ }
}
-
+
boost::detail::thread_data_base* get_current_thread_data()
{
boost::call_once(current_thread_tls_init_flag,create_current_thread_tls_key);
@@ -71,15 +72,17 @@
int const res=pthread_setspecific(current_thread_tls_key,new_data);
BOOST_ASSERT(!res);
}
-
-
+ }
+
+ namespace
+ {
extern "C"
{
void* thread_proxy(void* param)
{
boost::shared_ptr<boost::detail::thread_data_base> thread_info = static_cast<boost::detail::thread_data_base*>(param)->self;
thread_info->self.reset();
- set_current_thread_data(thread_info.get());
+ detail::set_current_thread_data(thread_info.get());
try
{
thread_info->run();
@@ -92,8 +95,8 @@
std::terminate();
}
- tls_destructor(thread_info.get());
- set_current_thread_data(0);
+ detail::tls_destructor(thread_info.get());
+ detail::set_current_thread_data(0);
boost::lock_guard<boost::mutex> lock(thread_info->data_mutex);
thread_info->done=true;
thread_info->done_condition.notify_all();
@@ -213,33 +216,43 @@
void thread::sleep(const system_time& st)
{
- xtime const xt=get_xtime(st);
-
- for (int foo=0; foo < 5; ++foo)
+ detail::thread_data_base* const thread_info=detail::get_current_thread_data();
+
+ if(thread_info)
+ {
+ unique_lock<mutex> lk(thread_info->sleep_mutex);
+ while(thread_info->sleep_condition.timed_wait(lk,st));
+ }
+ else
{
+ xtime const xt=get_xtime(st);
+
+ for (int foo=0; foo < 5; ++foo)
+ {
# if defined(BOOST_HAS_PTHREAD_DELAY_NP)
- timespec ts;
- to_timespec_duration(xt, ts);
- int res = 0;
- res = pthread_delay_np(&ts);
- BOOST_ASSERT(res == 0);
+ timespec ts;
+ to_timespec_duration(xt, ts);
+ int res = 0;
+ res = pthread_delay_np(&ts);
+ BOOST_ASSERT(res == 0);
# elif defined(BOOST_HAS_NANOSLEEP)
- timespec ts;
- to_timespec_duration(xt, ts);
-
- // nanosleep takes a timespec that is an offset, not
- // an absolute time.
- nanosleep(&ts, 0);
+ timespec ts;
+ to_timespec_duration(xt, ts);
+
+ // nanosleep takes a timespec that is an offset, not
+ // an absolute time.
+ nanosleep(&ts, 0);
# else
- mutex mx;
- mutex::scoped_lock lock(mx);
- condition cond;
- cond.timed_wait(lock, xt);
+ mutex mx;
+ mutex::scoped_lock lock(mx);
+ condition cond;
+ cond.timed_wait(lock, xt);
# endif
- xtime cur;
- xtime_get(&cur, TIME_UTC);
- if (xtime_cmp(xt, cur) <= 0)
- return;
+ xtime cur;
+ xtime_get(&cur, TIME_UTC);
+ if (xtime_cmp(xt, cur) <= 0)
+ return;
+ }
}
}
@@ -285,6 +298,11 @@
{
lock_guard<mutex> lk(local_thread_info->data_mutex);
local_thread_info->cancel_requested=true;
+ if(local_thread_info->current_cond)
+ {
+ int const res=pthread_cond_broadcast(local_thread_info->current_cond);
+ BOOST_ASSERT(!res);
+ }
}
}
@@ -293,7 +311,7 @@
{
void cancellation_point()
{
- boost::detail::thread_data_base* const thread_info=get_current_thread_data();
+ boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data();
if(thread_info && thread_info->cancel_enabled)
{
lock_guard<mutex> lg(thread_info->data_mutex);
@@ -307,13 +325,13 @@
bool cancellation_enabled()
{
- boost::detail::thread_data_base* const thread_info=get_current_thread_data();
+ boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data();
return thread_info && thread_info->cancel_enabled;
}
bool cancellation_requested()
{
- boost::detail::thread_data_base* const thread_info=get_current_thread_data();
+ boost::detail::thread_data_base* const thread_info=detail::get_current_thread_data();
if(!thread_info)
{
return false;
@@ -330,15 +348,15 @@
{
if(cancel_was_enabled)
{
- get_current_thread_data()->cancel_enabled=false;
+ detail::get_current_thread_data()->cancel_enabled=false;
}
}
disable_cancellation::~disable_cancellation()
{
- if(get_current_thread_data())
+ if(detail::get_current_thread_data())
{
- get_current_thread_data()->cancel_enabled=cancel_was_enabled;
+ detail::get_current_thread_data()->cancel_enabled=cancel_was_enabled;
}
}
@@ -346,15 +364,15 @@
{
if(d.cancel_was_enabled)
{
- get_current_thread_data()->cancel_enabled=true;
+ detail::get_current_thread_data()->cancel_enabled=true;
}
}
restore_cancellation::~restore_cancellation()
{
- if(get_current_thread_data())
+ if(detail::get_current_thread_data())
{
- get_current_thread_data()->cancel_enabled=false;
+ detail::get_current_thread_data()->cancel_enabled=false;
}
}
}
Modified: trunk/libs/thread/test/test_condition.cpp
==============================================================================
--- trunk/libs/thread/test/test_condition.cpp (original)
+++ trunk/libs/thread/test/test_condition.cpp 2007-11-01 13:07:47 EDT (Thu, 01 Nov 2007)
@@ -103,7 +103,7 @@
void test_condition_notify_one()
{
- timed_test(&do_test_condition_notify_one, 2, execution_monitor::use_mutex);
+ timed_test(&do_test_condition_notify_one, 100, execution_monitor::use_mutex);
}
void do_test_condition_notify_all()
@@ -131,7 +131,7 @@
// We should have already tested notify_one here, so
// a timed test with the default execution_monitor::use_condition
// should be OK, and gives the fastest performance
- timed_test(&do_test_condition_notify_all, 3);
+ timed_test(&do_test_condition_notify_all, 100);
}
void do_test_condition_waits()
@@ -189,6 +189,24 @@
timed_test(&do_test_condition_waits, 12);
}
+void do_test_condition_wait_is_a_cancellation_point()
+{
+ condition_test_data data;
+
+ boost::thread thread(bind(&condition_test_thread, &data));
+
+ thread.cancel();
+ thread.join();
+ BOOST_CHECK_EQUAL(data.awoken,0);
+}
+
+
+void test_condition_wait_is_a_cancellation_point()
+{
+ timed_test(&do_test_condition_wait_is_a_cancellation_point, 1);
+}
+
+
boost::unit_test_framework::test_suite* init_unit_test_suite(int, char*[])
{
boost::unit_test_framework::test_suite* test =
@@ -197,6 +215,7 @@
test->add(BOOST_TEST_CASE(&test_condition_notify_one));
test->add(BOOST_TEST_CASE(&test_condition_notify_all));
test->add(BOOST_TEST_CASE(&test_condition_waits));
+ test->add(BOOST_TEST_CASE(&test_condition_wait_is_a_cancellation_point));
return test;
}
Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk