diff --git a/src/pthread/condition.cpp b/src/pthread/condition.cpp new file mode 100644 index 00000000..0b01e3a6 --- /dev/null +++ b/src/pthread/condition.cpp @@ -0,0 +1,76 @@ +// Copyright 2006 Roland Schwarz. +// 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) +// +// This work is a reimplementation along the design and ideas +// of William E. Kempf. + +#include +#include + +#include +#include +#include + +#include +#include +#include "timeconv.inl" + +#include + +namespace boost { + +namespace detail { + +condition_impl::condition_impl() +{ + int res = 0; + res = pthread_cond_init(&m_condition, 0); + if (res != 0) + throw thread_resource_error(); +} + +condition_impl::~condition_impl() +{ + int res = 0; + res = pthread_cond_destroy(&m_condition); + assert(res == 0); +} + +void condition_impl::notify_one() +{ + int res = 0; + res = pthread_cond_signal(&m_condition); + assert(res == 0); +} + +void condition_impl::notify_all() +{ + int res = 0; + res = pthread_cond_broadcast(&m_condition); + assert(res == 0); +} + +void condition_impl::do_wait(pthread_mutex_t* pmutex) +{ + int res = 0; + res = pthread_cond_wait(&m_condition, pmutex); + assert(res == 0); +} + +bool condition_impl::do_timed_wait(const xtime& xt, pthread_mutex_t* pmutex) +{ + timespec ts; + to_timespec(xt, ts); + + int res = 0; + res = pthread_cond_timedwait(&m_condition, pmutex, &ts); + assert(res == 0 || res == ETIMEDOUT); + + return res != ETIMEDOUT; +} + +} // namespace detail + +} // namespace boost diff --git a/src/pthread/exceptions.cpp b/src/pthread/exceptions.cpp new file mode 100644 index 00000000..c940fce2 --- /dev/null +++ b/src/pthread/exceptions.cpp @@ -0,0 +1,150 @@ +// Copyright 2006 Roland Schwarz. +// 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) +// +// This work is a reimplementation along the design and ideas +// of William E. Kempf. + +#include +#include +#include +#include + +# ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::strerror; } +# endif + +#include // for POSIX error codes + +namespace +{ + +std::string system_message(int sys_err_code) +{ + std::string str; + str += std::strerror(errno); + return str; +} + +} // unnamed namespace + +namespace boost { + +thread_exception::thread_exception() + : m_sys_err(0) +{ +} + +thread_exception::thread_exception(int sys_err_code) + : m_sys_err(sys_err_code) +{ +} + +thread_exception::~thread_exception() throw() +{ +} + +int thread_exception::native_error() const +{ + return m_sys_err; +} + +const char* thread_exception::message() const +{ + if (m_sys_err != 0) + return system_message(m_sys_err).c_str(); + return what(); +} + +lock_error::lock_error() +{ +} + +lock_error::lock_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +lock_error::~lock_error() throw() +{ +} + +const char* lock_error::what() const throw() +{ + return "boost::lock_error"; +} + +thread_resource_error::thread_resource_error() +{ +} + +thread_resource_error::thread_resource_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +thread_resource_error::~thread_resource_error() throw() +{ +} + +const char* thread_resource_error::what() const throw() +{ + return "boost::thread_resource_error"; +} + +unsupported_thread_option::unsupported_thread_option() +{ +} + +unsupported_thread_option::unsupported_thread_option(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +unsupported_thread_option::~unsupported_thread_option() throw() +{ +} + +const char* unsupported_thread_option::what() const throw() +{ + return "boost::unsupported_thread_option"; +} + +invalid_thread_argument::invalid_thread_argument() +{ +} + +invalid_thread_argument::invalid_thread_argument(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +invalid_thread_argument::~invalid_thread_argument() throw() +{ +} + +const char* invalid_thread_argument::what() const throw() +{ + return "boost::invalid_thread_argument"; +} + +thread_permission_error::thread_permission_error() +{ +} + +thread_permission_error::thread_permission_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +thread_permission_error::~thread_permission_error() throw() +{ +} + +const char* thread_permission_error::what() const throw() +{ + return "boost::thread_permission_error"; +} + +} // namespace boost diff --git a/src/pthread/mutex.cpp b/src/pthread/mutex.cpp new file mode 100644 index 00000000..6f5a0c75 --- /dev/null +++ b/src/pthread/mutex.cpp @@ -0,0 +1,256 @@ +// Copyright 2006 Roland Schwarz. +// 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) +// +// This work is a reimplementation along the design and ideas +// of William E. Kempf. + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "timeconv.inl" + +#include + +namespace boost { + +mutex::mutex() +{ + int res = 0; + res = pthread_mutex_init(&m_mutex, 0); + if (res != 0) + throw thread_resource_error(); +} + +mutex::~mutex() +{ + int res = 0; + res = pthread_mutex_destroy(&m_mutex); + assert(res == 0); +} + +void mutex::do_lock() +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + if (res == EDEADLK) throw lock_error(); + assert(res == 0); +} + +void mutex::do_unlock() +{ + int res = 0; + res = pthread_mutex_unlock(&m_mutex); + if (res == EPERM) throw lock_error(); + assert(res == 0); +} + +void mutex::do_lock(cv_state&) +{ +} + +void mutex::do_unlock(cv_state& state) +{ + state.pmutex = &m_mutex; +} + +try_mutex::try_mutex() +{ + int res = 0; + res = pthread_mutex_init(&m_mutex, 0); + if (res != 0) + throw thread_resource_error(); +} + +try_mutex::~try_mutex() +{ + int res = 0; + res = pthread_mutex_destroy(&m_mutex); + assert(res == 0); +} + +void try_mutex::do_lock() +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + if (res == EDEADLK) throw lock_error(); + assert(res == 0); +} + +bool try_mutex::do_trylock() +{ + int res = 0; + res = pthread_mutex_trylock(&m_mutex); + if (res == EDEADLK) throw lock_error(); + assert(res == 0 || res == EBUSY); + return res == 0; +} + +void try_mutex::do_unlock() +{ + int res = 0; + res = pthread_mutex_unlock(&m_mutex); + if (res == EPERM) throw lock_error(); + assert(res == 0); +} + +void try_mutex::do_lock(cv_state&) +{ +} + +void try_mutex::do_unlock(cv_state& state) +{ + state.pmutex = &m_mutex; +} + +timed_mutex::timed_mutex() + : m_locked(false) +{ + 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(); + } +} + +timed_mutex::~timed_mutex() +{ + assert(!m_locked); + int res = 0; + res = pthread_mutex_destroy(&m_mutex); + assert(res == 0); + + res = pthread_cond_destroy(&m_condition); + assert(res == 0); +} + +void timed_mutex::do_lock() +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); + + while (m_locked) + { + res = pthread_cond_wait(&m_condition, &m_mutex); + assert(res == 0); + } + + assert(!m_locked); + m_locked = true; + + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); +} + +bool timed_mutex::do_trylock() +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); + + bool ret = false; + if (!m_locked) + { + m_locked = true; + ret = true; + } + + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); + return ret; +} + +bool timed_mutex::do_timedlock(const xtime& xt) +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); + + timespec ts; + to_timespec(xt, ts); + + while (m_locked) + { + res = pthread_cond_timedwait(&m_condition, &m_mutex, &ts); + assert(res == 0 || res == ETIMEDOUT); + + if (res == ETIMEDOUT) + break; + } + + bool ret = false; + if (!m_locked) + { + m_locked = true; + ret = true; + } + + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); + return ret; +} + +void timed_mutex::do_unlock() +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); + + assert(m_locked); + m_locked = false; + + res = pthread_cond_signal(&m_condition); + assert(res == 0); + + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); +} + +void timed_mutex::do_lock(cv_state&) +{ + int res = 0; + while (m_locked) + { + res = pthread_cond_wait(&m_condition, &m_mutex); + assert(res == 0); + } + + assert(!m_locked); + m_locked = true; + + res = pthread_mutex_unlock(&m_mutex); + assert(res == 0); +} + +void timed_mutex::do_unlock(cv_state& state) +{ + int res = 0; + res = pthread_mutex_lock(&m_mutex); + assert(res == 0); + + assert(m_locked); + m_locked = false; + + res = pthread_cond_signal(&m_condition); + assert(res == 0); + + state.pmutex = &m_mutex; +} + +} // namespace boost + diff --git a/src/pthread/thread.cpp b/src/pthread/thread.cpp new file mode 100644 index 00000000..c9523a13 --- /dev/null +++ b/src/pthread/thread.cpp @@ -0,0 +1,230 @@ +// Copyright 2006 Roland Schwarz. +// 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) +// +// This work is a reimplementation along the design and ideas +// of William E. Kempf. + +#include + +#include + +#include +#include +#include + +#include "timeconv.inl" + +namespace { + +class thread_param +{ +public: + thread_param(const boost::function0& threadfunc) + : m_threadfunc(threadfunc), m_started(false) + { + } + void wait() + { + boost::mutex::scoped_lock scoped_lock(m_mutex); + while (!m_started) + m_condition.wait(scoped_lock); + } + void started() + { + boost::mutex::scoped_lock scoped_lock(m_mutex); + m_started = true; + m_condition.notify_one(); + } + + boost::mutex m_mutex; + boost::condition m_condition; + const boost::function0& m_threadfunc; + bool m_started; +}; + +} // unnamed namespace + +extern "C" { +static void* thread_proxy(void* param) + { + try + { + thread_param* p = static_cast(param); + boost::function0 threadfunc = p->m_threadfunc; + p->started(); + threadfunc(); + } + catch (...) + { + } + return 0; + } +} + +namespace boost { + +thread::thread() + : m_joinable(false) +{ + m_thread = pthread_self(); +} + +thread::thread(const function0& threadfunc) + : m_joinable(true) +{ + thread_param param(threadfunc); + int res = 0; + res = pthread_create(&m_thread, 0, &thread_proxy, ¶m); + if (res != 0) + throw thread_resource_error(); + param.wait(); +} + +thread::~thread() +{ + if (m_joinable) + { + pthread_detach(m_thread); + } +} + +bool thread::operator==(const thread& other) const +{ + return pthread_equal(m_thread, other.m_thread) != 0; +} + +bool thread::operator!=(const thread& other) const +{ + return !operator==(other); +} + +void thread::join() +{ + assert(m_joinable); //See race condition comment below + int res = 0; + res = pthread_join(m_thread, 0); + assert(res == 0); + // This isn't a race condition since any race that could occur would + // have us in undefined behavior territory any way. + m_joinable = false; +} + +void thread::sleep(const xtime& xt) +{ + 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); + 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); +# else + 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; + } +} + +void thread::yield() +{ +# if defined(BOOST_HAS_SCHED_YIELD) + int res = 0; + res = sched_yield(); + assert(res == 0); +# elif defined(BOOST_HAS_PTHREAD_YIELD) + int res = 0; + res = pthread_yield(); + assert(res == 0); +# else + xtime xt; + xtime_get(&xt, TIME_UTC); + sleep(xt); +# endif +} + +thread_group::thread_group() +{ +} + +thread_group::~thread_group() +{ + // We shouldn't have to scoped_lock here, since referencing this object + // from another thread while we're deleting it in the current thread is + // going to lead to undefined behavior any way. + for (std::list::iterator it = m_threads.begin(); + it != m_threads.end(); ++it) + { + delete (*it); + } +} + +thread* thread_group::create_thread(const function0& threadfunc) +{ + // No scoped_lock required here since the only "shared data" that's + // modified here occurs inside add_thread which does scoped_lock. + std::auto_ptr thrd(new thread(threadfunc)); + add_thread(thrd.get()); + return thrd.release(); +} + +void thread_group::add_thread(thread* thrd) +{ + mutex::scoped_lock scoped_lock(m_mutex); + + // For now we'll simply ignore requests to add a thread object multiple + // times. Should we consider this an error and either throw or return an + // error value? + std::list::iterator it = std::find(m_threads.begin(), + m_threads.end(), thrd); + assert(it == m_threads.end()); + if (it == m_threads.end()) + m_threads.push_back(thrd); +} + +void thread_group::remove_thread(thread* thrd) +{ + mutex::scoped_lock scoped_lock(m_mutex); + + // For now we'll simply ignore requests to remove a thread object that's + // not in the group. Should we consider this an error and either throw or + // return an error value? + std::list::iterator it = std::find(m_threads.begin(), + m_threads.end(), thrd); + assert(it != m_threads.end()); + if (it != m_threads.end()) + m_threads.erase(it); +} + +void thread_group::join_all() +{ + mutex::scoped_lock scoped_lock(m_mutex); + for (std::list::iterator it = m_threads.begin(); + it != m_threads.end(); ++it) + { + (*it)->join(); + } +} + +int thread_group::size() +{ + return m_threads.size(); +} + +} // namespace boost + diff --git a/src/pthread/timeconv.inl b/src/pthread/timeconv.inl new file mode 100644 index 00000000..22bc5391 --- /dev/null +++ b/src/pthread/timeconv.inl @@ -0,0 +1,128 @@ +// Copyright (C) 2001-2003 +// 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. + +namespace { +const int MILLISECONDS_PER_SECOND = 1000; +const int NANOSECONDS_PER_SECOND = 1000000000; +const int NANOSECONDS_PER_MILLISECOND = 1000000; + +const int MICROSECONDS_PER_SECOND = 1000000; +const int NANOSECONDS_PER_MICROSECOND = 1000; + +inline void to_time(int milliseconds, boost::xtime& xt) +{ + int res = 0; + 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); + if(ts.tv_nsec >= NANOSECONDS_PER_SECOND) + { + ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND; + ts.tv_nsec %= NANOSECONDS_PER_SECOND; + } +} + +inline void to_time(int milliseconds, timespec& ts) +{ + boost::xtime xt; + to_time(milliseconds, xt); + to_timespec(xt, ts); +} + +inline void to_timespec_duration(const boost::xtime& xt, timespec& ts) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + else + { + ts.tv_sec = xt.sec - cur.sec; + ts.tv_nsec = xt.nsec - cur.nsec; + + if( ts.tv_nsec < 0 ) + { + ts.tv_sec -= 1; + ts.tv_nsec += NANOSECONDS_PER_SECOND; + } + if(ts.tv_nsec >= NANOSECONDS_PER_SECOND) + { + ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND; + ts.tv_nsec %= NANOSECONDS_PER_SECOND; + } + } +} +#endif + +inline void to_duration(boost::xtime xt, int& milliseconds) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + milliseconds = 0; + else + { + if (cur.nsec > xt.nsec) + { + xt.nsec += NANOSECONDS_PER_SECOND; + --xt.sec; + } + milliseconds = (int)((xt.sec - cur.sec) * MILLISECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MILLISECOND/2)) / + NANOSECONDS_PER_MILLISECOND); + } +} + +inline void to_microduration(const boost::xtime& xt, int& microseconds) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_get(&cur, boost::TIME_UTC) <= 0) + microseconds = 0; + else + { + microseconds = (int)((xt.sec - cur.sec) * MICROSECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MICROSECOND/2)) / + NANOSECONDS_PER_MICROSECOND); + } +} +} + +// Change Log: +// 1 Jun 01 Initial creation. diff --git a/src/win32/exceptions.cpp b/src/win32/exceptions.cpp new file mode 100644 index 00000000..3b7e2a34 --- /dev/null +++ b/src/win32/exceptions.cpp @@ -0,0 +1,166 @@ +// Copyright 2006 Roland Schwarz. +// 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) +// +// This work is a reimplementation along the design and ideas +// of William E. Kempf. + +#include +#include +#include +#include + +# ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::strerror; } +# endif + +#include "windows.h" + +namespace +{ + +std::string system_message(int sys_err_code) +{ + std::string str; + LPVOID lpMsgBuf; + ::FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + sys_err_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPSTR)&lpMsgBuf, + 0, + NULL); + str += static_cast(lpMsgBuf); + ::LocalFree(lpMsgBuf); // free the buffer + while (str.size() && (str[str.size()-1] == '\n' || + str[str.size()-1] == '\r')) + { + str.erase(str.size()-1); + } + return str; +} + +} // unnamed namespace + +namespace boost { + +thread_exception::thread_exception() + : m_sys_err(0) +{ +} + +thread_exception::thread_exception(int sys_err_code) + : m_sys_err(sys_err_code) +{ +} + +thread_exception::~thread_exception() throw() +{ +} + +int thread_exception::native_error() const +{ + return m_sys_err; +} + +const char* thread_exception::message() const +{ + if (m_sys_err != 0) + return system_message(m_sys_err).c_str(); + return what(); +} + +lock_error::lock_error() +{ +} + +lock_error::lock_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +lock_error::~lock_error() throw() +{ +} + +const char* lock_error::what() const throw() +{ + return "boost::lock_error"; +} + +thread_resource_error::thread_resource_error() +{ +} + +thread_resource_error::thread_resource_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +thread_resource_error::~thread_resource_error() throw() +{ +} + +const char* thread_resource_error::what() const throw() +{ + return "boost::thread_resource_error"; +} + +unsupported_thread_option::unsupported_thread_option() +{ +} + +unsupported_thread_option::unsupported_thread_option(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +unsupported_thread_option::~unsupported_thread_option() throw() +{ +} + +const char* unsupported_thread_option::what() const throw() +{ + return "boost::unsupported_thread_option"; +} + +invalid_thread_argument::invalid_thread_argument() +{ +} + +invalid_thread_argument::invalid_thread_argument(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +invalid_thread_argument::~invalid_thread_argument() throw() +{ +} + +const char* invalid_thread_argument::what() const throw() +{ + return "boost::invalid_thread_argument"; +} + +thread_permission_error::thread_permission_error() +{ +} + +thread_permission_error::thread_permission_error(int sys_err_code) + : thread_exception(sys_err_code) +{ +} + +thread_permission_error::~thread_permission_error() throw() +{ +} + +const char* thread_permission_error::what() const throw() +{ + return "boost::thread_permission_error"; +} + +} // namespace boost diff --git a/src/win32/thread.cpp b/src/win32/thread.cpp new file mode 100644 index 00000000..1bff027f --- /dev/null +++ b/src/win32/thread.cpp @@ -0,0 +1,247 @@ +// Copyright 2006 Roland Schwarz. +// 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) +// +// This work is a reimplementation along the design and ideas +// of William E. Kempf. + +#include + +#include +#include +#include +#include + + +#include +#if !defined(BOOST_NO_THREADEX) +# include +#endif + +#include "timeconv.inl" + +//#include "boost/thread/win32/tss_hooks.hpp" + +namespace { + +#if defined(BOOST_NO_THREADEX) +// Windows CE doesn't define _beginthreadex + +struct ThreadProxyData +{ + typedef unsigned (__stdcall* func)(void*); + func start_address_; + void* arglist_; + ThreadProxyData(func start_address,void* arglist) : start_address_(start_address), arglist_(arglist) {} +}; + +DWORD WINAPI ThreadProxy(LPVOID args) +{ + ThreadProxyData* data=reinterpret_cast(args); + DWORD ret=data->start_address_(data->arglist_); + delete data; + return ret; +} + +inline unsigned _beginthreadex(void* security, unsigned stack_size, unsigned (__stdcall* start_address)(void*), +void* arglist, unsigned initflag,unsigned* thrdaddr) +{ + DWORD threadID; + HANDLE hthread=CreateThread(static_cast(security),stack_size,ThreadProxy, + new ThreadProxyData(start_address,arglist),initflag,&threadID); + if (hthread!=0) + *thrdaddr=threadID; + return reinterpret_cast(hthread); +} +#endif + +class thread_param +{ +public: + thread_param(const boost::function0& threadfunc) + : m_threadfunc(threadfunc), m_started(false) + { + } + void wait() + { + boost::mutex::scoped_lock scoped_lock(m_mutex); + while (!m_started) + m_condition.wait(scoped_lock); + } + void started() + { + boost::mutex::scoped_lock scoped_lock(m_mutex); + m_started = true; + m_condition.notify_one(); + } + + boost::mutex m_mutex; + boost::condition m_condition; + const boost::function0& m_threadfunc; + bool m_started; +}; + +} // unnamed namespace + +extern "C" { + unsigned __stdcall thread_proxy(void* param) + { + try + { + thread_param* p = static_cast(param); + boost::function0 threadfunc = p->m_threadfunc; + p->started(); + threadfunc(); +// on_thread_exit(); + } + catch (...) + { +// on_thread_exit(); + } + return 0; + } + +} + +namespace boost { + +thread::thread() + : m_joinable(false) +{ + m_thread = reinterpret_cast(GetCurrentThread()); + m_id = GetCurrentThreadId(); +} + +thread::thread(const function0& threadfunc) + : m_joinable(true) +{ + thread_param param(threadfunc); + m_thread = reinterpret_cast(_beginthreadex(0, 0, &thread_proxy, + ¶m, 0, &m_id)); + if (!m_thread) + throw thread_resource_error(); + param.wait(); +} + +thread::~thread() +{ + if (m_joinable) + { + int res = 0; + res = CloseHandle(reinterpret_cast(m_thread)); + assert(res); + } +} + +bool thread::operator==(const thread& other) const +{ + return other.m_id == m_id; +} + +bool thread::operator!=(const thread& other) const +{ + return !operator==(other); +} + +void thread::join() +{ + assert(m_joinable); //See race condition comment below + int res = 0; + res = WaitForSingleObject(reinterpret_cast(m_thread), INFINITE); + assert(res == WAIT_OBJECT_0); + res = CloseHandle(reinterpret_cast(m_thread)); + assert(res); + // This isn't a race condition since any race that could occur would + // have us in undefined behavior territory any way. + m_joinable = false; +} + +void thread::sleep(const xtime& xt) +{ + for (int foo=0; foo < 5; ++foo) + { + int milliseconds; + to_duration(xt, milliseconds); + Sleep(milliseconds); + xtime cur; + xtime_get(&cur, TIME_UTC); + if (xtime_cmp(xt, cur) <= 0) + return; + } +} + +void thread::yield() +{ + Sleep(0); +} + +thread_group::thread_group() +{ +} + +thread_group::~thread_group() +{ + // We shouldn't have to scoped_lock here, since referencing this object + // from another thread while we're deleting it in the current thread is + // going to lead to undefined behavior any way. + for (std::list::iterator it = m_threads.begin(); + it != m_threads.end(); ++it) + { + delete (*it); + } +} + +thread* thread_group::create_thread(const function0& threadfunc) +{ + // No scoped_lock required here since the only "shared data" that's + // modified here occurs inside add_thread which does scoped_lock. + std::auto_ptr thrd(new thread(threadfunc)); + add_thread(thrd.get()); + return thrd.release(); +} + +void thread_group::add_thread(thread* thrd) +{ + mutex::scoped_lock scoped_lock(m_mutex); + + // For now we'll simply ignore requests to add a thread object multiple + // times. Should we consider this an error and either throw or return an + // error value? + std::list::iterator it = std::find(m_threads.begin(), + m_threads.end(), thrd); + assert(it == m_threads.end()); + if (it == m_threads.end()) + m_threads.push_back(thrd); +} + +void thread_group::remove_thread(thread* thrd) +{ + mutex::scoped_lock scoped_lock(m_mutex); + + // For now we'll simply ignore requests to remove a thread object that's + // not in the group. Should we consider this an error and either throw or + // return an error value? + std::list::iterator it = std::find(m_threads.begin(), + m_threads.end(), thrd); + assert(it != m_threads.end()); + if (it != m_threads.end()) + m_threads.erase(it); +} + +void thread_group::join_all() +{ + mutex::scoped_lock scoped_lock(m_mutex); + for (std::list::iterator it = m_threads.begin(); + it != m_threads.end(); ++it) + { + (*it)->join(); + } +} + +int thread_group::size() +{ + return m_threads.size(); +} + +} // namespace boost diff --git a/src/win32/timeconv.inl b/src/win32/timeconv.inl new file mode 100644 index 00000000..22bc5391 --- /dev/null +++ b/src/win32/timeconv.inl @@ -0,0 +1,128 @@ +// Copyright (C) 2001-2003 +// 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. + +namespace { +const int MILLISECONDS_PER_SECOND = 1000; +const int NANOSECONDS_PER_SECOND = 1000000000; +const int NANOSECONDS_PER_MILLISECOND = 1000000; + +const int MICROSECONDS_PER_SECOND = 1000000; +const int NANOSECONDS_PER_MICROSECOND = 1000; + +inline void to_time(int milliseconds, boost::xtime& xt) +{ + int res = 0; + 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); + if(ts.tv_nsec >= NANOSECONDS_PER_SECOND) + { + ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND; + ts.tv_nsec %= NANOSECONDS_PER_SECOND; + } +} + +inline void to_time(int milliseconds, timespec& ts) +{ + boost::xtime xt; + to_time(milliseconds, xt); + to_timespec(xt, ts); +} + +inline void to_timespec_duration(const boost::xtime& xt, timespec& ts) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + { + ts.tv_sec = 0; + ts.tv_nsec = 0; + } + else + { + ts.tv_sec = xt.sec - cur.sec; + ts.tv_nsec = xt.nsec - cur.nsec; + + if( ts.tv_nsec < 0 ) + { + ts.tv_sec -= 1; + ts.tv_nsec += NANOSECONDS_PER_SECOND; + } + if(ts.tv_nsec >= NANOSECONDS_PER_SECOND) + { + ts.tv_sec += ts.tv_nsec / NANOSECONDS_PER_SECOND; + ts.tv_nsec %= NANOSECONDS_PER_SECOND; + } + } +} +#endif + +inline void to_duration(boost::xtime xt, int& milliseconds) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_cmp(xt, cur) <= 0) + milliseconds = 0; + else + { + if (cur.nsec > xt.nsec) + { + xt.nsec += NANOSECONDS_PER_SECOND; + --xt.sec; + } + milliseconds = (int)((xt.sec - cur.sec) * MILLISECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MILLISECOND/2)) / + NANOSECONDS_PER_MILLISECOND); + } +} + +inline void to_microduration(const boost::xtime& xt, int& microseconds) +{ + boost::xtime cur; + int res = 0; + res = boost::xtime_get(&cur, boost::TIME_UTC); + assert(res == boost::TIME_UTC); + + if (boost::xtime_get(&cur, boost::TIME_UTC) <= 0) + microseconds = 0; + else + { + microseconds = (int)((xt.sec - cur.sec) * MICROSECONDS_PER_SECOND) + + (((xt.nsec - cur.nsec) + (NANOSECONDS_PER_MICROSECOND/2)) / + NANOSECONDS_PER_MICROSECOND); + } +} +} + +// Change Log: +// 1 Jun 01 Initial creation.