|
Boost-Commit : |
Subject: [Boost-commit] svn:boost r85719 - in trunk: boost/sync boost/sync/detail/generic boost/sync/detail/windows libs/sync/test
From: tim_at_[hidden]
Date: 2013-09-17 05:59:25
Author: timblechmann
Date: 2013-09-17 05:59:24 EDT (Tue, 17 Sep 2013)
New Revision: 85719
URL: http://svn.boost.org/trac/boost/changeset/85719
Log:
sync: introduce win32-style event class
Added:
trunk/boost/sync/detail/generic/event_cv_emulation.hpp (contents, props changed)
trunk/boost/sync/detail/windows/event.hpp (contents, props changed)
trunk/boost/sync/event.hpp (contents, props changed)
trunk/libs/sync/test/event_test.cpp (contents, props changed)
Added: trunk/boost/sync/detail/generic/event_cv_emulation.hpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/boost/sync/detail/generic/event_cv_emulation.hpp 2013-09-17 05:59:24 EDT (Tue, 17 Sep 2013) (r85719)
@@ -0,0 +1,115 @@
+// event.hpp, condition variable emulation
+//
+// Copyright (C) 2013 Tim Blechmann
+//
+// 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)
+
+#ifndef BOOST_SYNC_DETAIL_GENERIC_EVENT_CV_EMULATION_HPP
+#define BOOST_SYNC_DETAIL_GENERIC_EVENT_CV_EMULATION_HPP
+
+#include <boost/thread/condition_variable.hpp>
+#include <boost/thread/mutex.hpp>
+
+#define BOOST_SYNC_EVENT_EMULATED
+
+namespace boost {
+namespace sync {
+
+class event
+{
+ BOOST_DELETED_FUNCTION(event(event const&));
+ BOOST_DELETED_FUNCTION(event& operator=(event const&));
+
+public:
+ event(bool auto_reset = false):
+ auto_reset_(auto_reset), set_(false)
+ {}
+
+ ~event()
+ {}
+
+ void post()
+ {
+ unique_lock<timed_mutex> lock(mutex_);
+ set_ = true;
+ cv_.notify_all();
+ }
+
+ void wait()
+ {
+ unique_lock<timed_mutex> lock(mutex_);
+ if (set_) {
+ if (auto_reset_)
+ set_ = false;
+ return;
+ }
+
+ cv_.wait( lock, bind(&event::is_set, this) );
+
+ if (auto_reset_)
+ set_ = false;
+ }
+
+ void reset()
+ {
+ unique_lock<timed_mutex> lock(mutex_);
+ set_ = false;
+ }
+
+ bool try_wait()
+ {
+ unique_lock<timed_mutex> lock(mutex_);
+ return test_status();
+ }
+
+ template <class Rep, class Period>
+ bool try_wait_for(const chrono::duration<Rep, Period> & duration)
+ {
+ return try_wait_until( chrono::system_clock::now() + duration );
+ }
+
+ template <class Clock, class Duration>
+ bool try_wait_until(const chrono::time_point<Clock, Duration> & timeout)
+ {
+ unique_lock<timed_mutex> lock(mutex_, timeout);
+
+ if (!lock.owns_lock())
+ return false;
+
+ const bool success = cv_.wait_until(lock, timeout, bind(&event::is_set, this));
+ if (success)
+ return test_status();
+ else
+ return false;
+ }
+
+private:
+ inline bool is_set()
+ {
+ return set_;
+ }
+
+ bool test_status()
+ {
+ if (!set_)
+ return false;
+ else {
+ if (auto_reset_)
+ set_ = false;
+
+ return true;
+ }
+ }
+
+ timed_mutex mutex_;
+ condition_variable_any cv_;
+ const bool auto_reset_;
+ bool set_;
+};
+
+}
+}
+
+#endif // BOOST_SYNC_DETAIL_GENERIC_EVENT_CV_EMULATION_HPP
Added: trunk/boost/sync/detail/windows/event.hpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/boost/sync/detail/windows/event.hpp 2013-09-17 05:59:24 EDT (Tue, 17 Sep 2013) (r85719)
@@ -0,0 +1,123 @@
+// event.hpp, win32 events
+//
+// Copyright (C) 2013 Tim Blechmann
+//
+// 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)
+
+#ifndef BOOST_SYNC_EVENT_DETAIL_WINDOWS_EVENT_HPP
+#define BOOST_SYNC_EVENT_DETAIL_WINDOWS_EVENT_HPP
+
+#include <boost/detail/win/GetLastError.hpp>
+#include <boost/detail/win/synchronization.hpp>
+#include <boost/detail/win/handles.hpp>
+
+#include <boost/sync/exceptions/resource_error.hpp>
+
+#include <boost/typeof/typeof.hpp>
+
+namespace boost {
+namespace sync {
+
+class event
+{
+ BOOST_DELETED_FUNCTION(event(event const&))
+ BOOST_DELETED_FUNCTION(event& operator=(event const&))
+
+ typedef boost::detail::win32::HANDLE_ HANDLE_;
+ typedef boost::detail::win32::BOOL_ BOOL_;
+
+public:
+ event(bool auto_reset = false)
+ {
+ handle_ = boost::detail::win32::CreateEventA(NULL, !auto_reset, 0, NULL);
+ if (!handle_)
+ boost::throw_exception(thread_resource_error(boost::detail::win32::GetLastError(), "boost::sync::event constructor failed in CreateEvent"));
+ }
+
+ ~event()
+ {
+ BOOST_VERIFY( boost::detail::win32::CloseHandle(handle_) );
+ }
+
+ void post()
+ {
+ const BOOL_ status = boost::detail::win32::SetEvent(handle_);
+ if (status == 0)
+ boost::throw_exception(thread_resource_error(boost::detail::win32::GetLastError(), "boost::sync::event::post failed in ReleaseEvent"));
+ }
+
+ void reset()
+ {
+ const BOOL_ status = boost::detail::win32::ResetEvent(handle_);
+ if (status == 0)
+ boost::throw_exception(thread_resource_error(boost::detail::win32::GetLastError(), "boost::sync::event::reset failed in ResetEvent"));
+ }
+
+ bool wait()
+ {
+ using namespace boost::detail::win32;
+
+ switch ( WaitForSingleObject(handle_, infinite) )
+ {
+ case WAIT_OBJECT_0:
+ return true;
+
+ case WAIT_FAILED:
+ boost::throw_exception(thread_resource_error(boost::detail::win32::GetLastError(), "boost::sync::event::wait failed in WaitForSingleObject"));
+
+ default:
+ BOOST_ASSERT(false);
+ return false;
+ }
+ }
+
+ bool try_wait()
+ {
+ return do_try_wait_for( 0L );
+ }
+
+ template <class Rep, class Period>
+ bool try_wait_for(const chrono::duration<Rep, Period> & rel_time)
+ {
+ BOOST_AUTO ( milliseconds, (DWORD_)chrono::duration_cast<chrono::milliseconds>( rel_time ) );
+ return do_try_wait_for( milliseconds.count() );
+ }
+
+ template <class Clock, class Duration>
+ bool try_wait_until(const chrono::time_point<Clock, Duration> & timeout )
+ {
+ typename Clock::time_point c_now = Clock::now();
+ return try_wait_for( timeout - c_now );
+ }
+
+private:
+ bool do_try_wait_for( long milliseconds )
+ {
+ using namespace boost::detail::win32;
+
+ switch ( WaitForSingleObject(handle_, milliseconds) )
+ {
+ case WAIT_OBJECT_0:
+ return true;
+
+ case WAIT_TIMEOUT:
+ return false;
+
+ case WAIT_FAILED:
+ boost::throw_exception(thread_resource_error(boost::detail::win32::GetLastError(), "boost::sync::event::do_try_wait_for failed in WaitForSingleObject"));
+
+ default:
+ BOOST_ASSERT(false);
+ return false;
+ }
+ }
+
+ HANDLE_ handle_;
+};
+
+}
+}
+
+#endif // BOOST_SYNC_EVENT_DETAIL_WINDOWS_EVENT_HPP
Added: trunk/boost/sync/event.hpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/boost/sync/event.hpp 2013-09-17 05:59:24 EDT (Tue, 17 Sep 2013) (r85719)
@@ -0,0 +1,119 @@
+// event.hpp
+//
+// Copyright (C) 2013 Tim Blechmann
+//
+// 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)
+
+#ifndef BOOST_SYNC_EVENT_HPP
+#define BOOST_SYNC_EVENT_HPP
+
+#ifdef BOOST_SYNC_DETAIL_DOXYGEN
+
+class event
+{
+
+public:
+ /**
+ * \b Effects: Constructs an event object. If `auto_reset` is true, waiting for an even will automatically reset it.
+ *
+ * \b Throws: if an error occurs.
+ */
+ event(bool auto_reset = false);
+
+ /**
+ * \b Effects: Destroys the event object.
+ *
+ * \b Precondition: No thread waits on this event.
+ *
+ * \b Throws: if an error occurs.
+ */
+ ~event();
+
+ /**
+ * \b Effects: Signals the event object: the event is set and waiting threads will be woken up.
+ *
+ * \b Precondition: No thread waits on this event.
+ *
+ * \b Throws: if an error occurs.
+ */
+ void post();
+
+ /**
+ * \b Effects: Waits for the event to be signaled. If the event is created as `auto_reset`, it will unset the `signaled` state
+ *
+ * \b Precondition: No thread waits on this event.
+ *
+ * \b Throws: if an error occurs.
+ */
+ void wait();
+
+ /**
+ * \b Effects: Resets the event to the unsignaled state.
+ *
+ * \b Throws: if an error occurs.
+ */
+ void reset();
+
+ /**
+ * \b Effects: Waits for the event to be signaled. If the event is created as `auto_reset`, it will unset the `signaled` state
+ *
+ * \b Returns: True if the event had been signaled, False, if the call would be blocking or the event has not been signaled.
+ *
+ * \b Throws: if an error occurs.
+ */
+ bool try_wait();
+
+ /**
+ * \b Effects: Waits for the event to be signaled. If the event is created as `auto_reset`, it will unset the `signaled` state
+ *
+ * \b Returns: True if the event had been signaled, False, if the call would be blocking or the event has not been signaled.
+ *
+ * \b Throws: if an error occurs.
+ */
+ template <typename Duration>
+ bool try_wait_for(const Duration & duration);
+
+ /**
+ * \b Effects: Waits for the event to be signaled. If the event is created as `auto_reset`, it will unset the `signaled` state
+ *
+ * \b Returns: True if the event had been signaled, False, if the call would be blocking or the event has not been signaled.
+ *
+ * \b Throws: if an error occurs.
+ */
+ template <typename TimePoint>
+ bool try_wait_until(const TimePoint & timeout);
+};
+
+#else // BOOST_SYNC_DETAIL_DOXYGEN
+
+#include <boost/sync/detail/config.hpp>
+
+#ifdef BOOST_HAS_PRAGMA_ONCE
+#pragma once
+#endif
+
+#include <boost/sync/detail/header.hpp>
+
+
+#if defined(BOOST_SYNC_DETAIL_PLATFORM_WINAPI)
+#include <boost/sync/detail/windows/event.hpp>
+
+//#elif defined(BOOST_SYNC_POSIX_SEMAPHORES)
+//#include <boost/sync/semaphore/semaphore_posix.hpp>
+
+//#elif defined(BOOST_SYNC_DISPATCH_SEMAPHORES)
+//#include <boost/sync/semaphore/semaphore_dispatch.hpp>
+
+#else
+
+#include <boost/sync/detail/generic/event_cv_emulation.hpp>
+
+#endif
+
+#include <boost/sync/detail/footer.hpp>
+
+#endif // BOOST_SYNC_DETAIL_DOXYGEN
+
+#endif // BOOST_SYNC_EVENT_HPP
Added: trunk/libs/sync/test/event_test.cpp
==============================================================================
--- /dev/null 00:00:00 1970 (empty, because file is newly added)
+++ trunk/libs/sync/test/event_test.cpp 2013-09-17 05:59:24 EDT (Tue, 17 Sep 2013) (r85719)
@@ -0,0 +1,161 @@
+// Copyright (C) 2013 Tim Blechmann
+//
+// 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)
+
+#include <boost/test/unit_test.hpp>
+
+#include <boost/thread.hpp>
+#include <boost/sync/event.hpp>
+
+#include <boost/typeof/typeof.hpp>
+
+static void test_event_post_wait()
+{
+ boost::sync::event ev;
+
+ ev.post();
+ ev.wait();
+
+ BOOST_REQUIRE( ev.try_wait() == true );
+
+ ev.reset();
+
+ BOOST_REQUIRE( ev.try_wait() == false );
+}
+
+
+static void test_event_post_try_wait()
+{
+ boost::sync::event ev;
+
+ BOOST_REQUIRE( ev.try_wait() == false );
+
+ ev.post();
+
+ BOOST_REQUIRE( ev.try_wait() == true );
+}
+
+static void test_event_post_wait_autoreset()
+{
+ boost::sync::event ev(true);
+
+ ev.post();
+ ev.wait();
+ BOOST_REQUIRE( ev.try_wait() == false );
+}
+
+
+static void test_event_reset()
+{
+ boost::sync::event ev(false);
+
+ BOOST_REQUIRE( ev.try_wait() == false );
+ ev.post();
+ BOOST_REQUIRE( ev.try_wait() == true );
+ ev.reset();
+ BOOST_REQUIRE( ev.try_wait() == false );
+}
+
+struct event_wait_and_post_test
+{
+ void run()
+ {
+ boost::thread post_thread(boost::bind(&event_wait_and_post_test::wait_and_post, this));
+ ev_.wait();
+ }
+
+ void wait_and_post()
+ {
+ boost::this_thread::sleep_for(boost::chrono::seconds(1));
+ ev_.post();
+ }
+
+ static void run_test()
+ {
+ event_wait_and_post_test test;
+ test.run();
+ }
+
+ boost::sync::event ev_;
+ boost::thread thread_;
+};
+
+static void test_event_wait_for()
+{
+ using namespace boost;
+
+ sync::event ev;
+
+ BOOST_AUTO(start, chrono::system_clock::now());
+
+ BOOST_REQUIRE( ev.try_wait() == false );
+
+ BOOST_REQUIRE(!ev.try_wait_for(chrono::milliseconds(500)));
+
+ BOOST_REQUIRE( ev.try_wait() == false );
+
+
+ BOOST_AUTO(end, chrono::system_clock::now());
+ BOOST_AUTO(wait_time, end - start);
+
+ // guessing!
+ BOOST_REQUIRE( wait_time > chrono::milliseconds(450) );
+ BOOST_REQUIRE( wait_time < chrono::milliseconds(1000) );
+
+ ev.post();
+
+ BOOST_REQUIRE(ev.try_wait_for(chrono::milliseconds(500)));
+}
+
+static void test_event_wait_until()
+{
+ using namespace boost;
+
+ sync::event ev(0);
+ {
+ BOOST_AUTO(now, chrono::system_clock::now());
+ BOOST_AUTO(timeout, now + chrono::milliseconds(500));
+
+ BOOST_REQUIRE(!ev.try_wait_until(timeout));
+
+ BOOST_AUTO(end, chrono::system_clock::now());
+ BOOST_AUTO(timeout_delta, end - timeout);
+
+ // guessing!
+ BOOST_REQUIRE( timeout_delta > chrono::milliseconds(-400) );
+ BOOST_REQUIRE( timeout_delta < chrono::milliseconds(400) );
+ }
+
+ ev.post();
+
+ {
+ BOOST_AUTO(start, chrono::system_clock::now());
+ BOOST_AUTO(timeout, start + chrono::milliseconds(500));
+
+ BOOST_REQUIRE(ev.try_wait_until(timeout));
+
+ BOOST_AUTO(end, chrono::system_clock::now());
+
+ // guessing!
+ BOOST_REQUIRE( (end - start) < chrono::milliseconds(100) );
+ }
+}
+
+boost::unit_test::test_suite* init_unit_test_suite(int, char*[])
+{
+ boost::unit_test::test_suite* test = BOOST_TEST_SUITE("boost::sync::event test suite");
+
+ test->add(BOOST_TEST_CASE(test_event_post_wait));
+ test->add(BOOST_TEST_CASE(test_event_post_wait_autoreset));
+ test->add(BOOST_TEST_CASE(test_event_post_try_wait));
+ test->add(BOOST_TEST_CASE(test_event_reset));
+
+ test->add(BOOST_TEST_CASE(event_wait_and_post_test::run_test));
+
+ test->add(BOOST_TEST_CASE(test_event_wait_for));
+ test->add(BOOST_TEST_CASE(test_event_wait_until));
+
+ 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