+// SGThread - Simple pthread class wrappers.
+//
+// Written by Bernie Bright, started April 2001.
+//
+// Copyright (C) 2001 Bernard Bright - bbright@bigpond.net.au
+// Copyright (C) 2011 Mathias Froehlich
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License as
+// published by the Free Software Foundation; either version 2 of the
+// License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//
+
+#ifdef HAVE_CONFIG_H
+# include <simgear_config.h>
+#endif
+
#include <simgear/compiler.h>
-#if defined(_MSC_VER) || defined(__MINGW32__)
-# include <time.h>
+#include "SGThread.hxx"
+
+#ifdef _WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+/// win32 threads
+/////////////////////////////////////////////////////////////////////////////
+
+#include <list>
+#include <windows.h>
+
+struct SGThread::PrivateData {
+ PrivateData() :
+ _handle(INVALID_HANDLE_VALUE)
+ {
+ }
+ ~PrivateData()
+ {
+ if (_handle == INVALID_HANDLE_VALUE)
+ return;
+ CloseHandle(_handle);
+ _handle = INVALID_HANDLE_VALUE;
+ }
+
+ static DWORD WINAPI start_routine(LPVOID data)
+ {
+ SGThread* thread = reinterpret_cast<SGThread*>(data);
+ thread->run();
+ return 0;
+ }
+
+ bool start(SGThread& thread)
+ {
+ if (_handle != INVALID_HANDLE_VALUE)
+ return false;
+ _handle = CreateThread(0, 0, start_routine, &thread, 0, 0);
+ if (_handle == INVALID_HANDLE_VALUE)
+ return false;
+ return true;
+ }
+
+ void join()
+ {
+ if (_handle == INVALID_HANDLE_VALUE)
+ return;
+ DWORD ret = WaitForSingleObject(_handle, 0);
+ if (ret != WAIT_OBJECT_0)
+ return;
+ CloseHandle(_handle);
+ _handle = INVALID_HANDLE_VALUE;
+ }
+
+ HANDLE _handle;
+};
+
+struct SGMutex::PrivateData {
+ PrivateData()
+ {
+ InitializeCriticalSection((LPCRITICAL_SECTION)&_criticalSection);
+ }
+
+ ~PrivateData()
+ {
+ DeleteCriticalSection((LPCRITICAL_SECTION)&_criticalSection);
+ }
+
+ void lock(void)
+ {
+ EnterCriticalSection((LPCRITICAL_SECTION)&_criticalSection);
+ }
+
+ void unlock(void)
+ {
+ LeaveCriticalSection((LPCRITICAL_SECTION)&_criticalSection);
+ }
+
+ CRITICAL_SECTION _criticalSection;
+};
+
+struct SGWaitCondition::PrivateData {
+ ~PrivateData(void)
+ {
+ // The waiters list should be empty anyway
+ _mutex.lock();
+ while (!_pool.empty()) {
+ CloseHandle(_pool.front());
+ _pool.pop_front();
+ }
+ _mutex.unlock();
+ }
+
+ void signal(void)
+ {
+ _mutex.lock();
+ if (!_waiters.empty())
+ SetEvent(_waiters.back());
+ _mutex.unlock();
+ }
+
+ void broadcast(void)
+ {
+ _mutex.lock();
+ for (std::list<HANDLE>::iterator i = _waiters.begin(); i != _waiters.end(); ++i)
+ SetEvent(*i);
+ _mutex.unlock();
+ }
+
+ bool wait(SGMutex::PrivateData& externalMutex, DWORD msec)
+ {
+ _mutex.lock();
+ if (_pool.empty())
+ _waiters.push_front(CreateEvent(NULL, FALSE, FALSE, NULL));
+ else
+ _waiters.splice(_waiters.begin(), _pool, _pool.begin());
+ std::list<HANDLE>::iterator i = _waiters.begin();
+ _mutex.unlock();
+
+ externalMutex.unlock();
+
+ DWORD result = WaitForSingleObject(*i, msec);
+
+ externalMutex.lock();
+
+ _mutex.lock();
+ if (result != WAIT_OBJECT_0)
+ result = WaitForSingleObject(*i, 0);
+ _pool.splice(_pool.begin(), _waiters, i);
+ _mutex.unlock();
+
+ return result == WAIT_OBJECT_0;
+ }
+
+ void wait(SGMutex::PrivateData& externalMutex)
+ {
+ wait(externalMutex, INFINITE);
+ }
+
+ // Protect the list of waiters
+ SGMutex::PrivateData _mutex;
+
+ std::list<HANDLE> _waiters;
+ std::list<HANDLE> _pool;
+};
+
+#else
+/////////////////////////////////////////////////////////////////////////////
+/// posix threads
+/////////////////////////////////////////////////////////////////////////////
+
+#include <pthread.h>
+#include <cassert>
+#include <cerrno>
+#include <sys/time.h>
+
+struct SGThread::PrivateData {
+ PrivateData() :
+ _started(false)
+ {
+ }
+ ~PrivateData()
+ {
+ // If we are still having a started thread and nobody waited,
+ // now detach ...
+ if (!_started)
+ return;
+ pthread_detach(_thread);
+ }
+
+ static void *start_routine(void* data)
+ {
+ SGThread* thread = reinterpret_cast<SGThread*>(data);
+ thread->run();
+ return 0;
+ }
+
+ bool start(SGThread& thread)
+ {
+ if (_started)
+ return false;
+
+ int ret = pthread_create(&_thread, 0, start_routine, &thread);
+ if (0 != ret)
+ return false;
+
+ _started = true;
+ return true;
+ }
+
+ void join()
+ {
+ if (!_started)
+ return;
+
+ pthread_join(_thread, 0);
+ _started = false;
+ }
+
+ pthread_t _thread;
+ bool _started;
+};
+
+struct SGMutex::PrivateData {
+ PrivateData()
+ {
+ int err = pthread_mutex_init(&_mutex, 0);
+ assert(err == 0);
+ (void)err;
+ }
+
+ ~PrivateData()
+ {
+ int err = pthread_mutex_destroy(&_mutex);
+ assert(err == 0);
+ (void)err;
+ }
+
+ void lock(void)
+ {
+ int err = pthread_mutex_lock(&_mutex);
+ assert(err == 0);
+ (void)err;
+ }
+
+ void unlock(void)
+ {
+ int err = pthread_mutex_unlock(&_mutex);
+ assert(err == 0);
+ (void)err;
+ }
+
+ pthread_mutex_t _mutex;
+};
+
+struct SGWaitCondition::PrivateData {
+ PrivateData(void)
+ {
+ int err = pthread_cond_init(&_condition, NULL);
+ assert(err == 0);
+ (void)err;
+ }
+ ~PrivateData(void)
+ {
+ int err = pthread_cond_destroy(&_condition);
+ assert(err == 0);
+ (void)err;
+ }
+
+ void signal(void)
+ {
+ int err = pthread_cond_signal(&_condition);
+ assert(err == 0);
+ (void)err;
+ }
+
+ void broadcast(void)
+ {
+ int err = pthread_cond_broadcast(&_condition);
+ assert(err == 0);
+ (void)err;
+ }
+
+ void wait(SGMutex::PrivateData& mutex)
+ {
+ int err = pthread_cond_wait(&_condition, &mutex._mutex);
+ assert(err == 0);
+ (void)err;
+ }
+
+ bool wait(SGMutex::PrivateData& mutex, unsigned msec)
+ {
+ struct timespec ts;
+#ifdef HAVE_CLOCK_GETTIME
+ if (0 != clock_gettime(CLOCK_REALTIME, &ts))
+ return false;
#else
-# if defined ( sgi ) && !defined( __GNUC__ )
- // This works around a bug triggered when using MipsPro 7.4.1
- // and (at least) IRIX 6.5.20
-# include <iostream>
-# endif
-# include <sys/time.h>
+ struct timeval tv;
+ if (0 != gettimeofday(&tv, NULL))
+ return false;
+ ts.tv_sec = tv.tv_sec;
+ ts.tv_nsec = tv.tv_usec * 1000;
#endif
-#if _MSC_VER >= 1300
-# include <winsock2.h>
+
+ ts.tv_nsec += 1000000*(msec % 1000);
+ if (1000000000 <= ts.tv_nsec) {
+ ts.tv_nsec -= 1000000000;
+ ts.tv_sec += 1;
+ }
+ ts.tv_sec += msec / 1000;
+
+ int evalue = pthread_cond_timedwait(&_condition, &mutex._mutex, &ts);
+ if (evalue == 0)
+ return true;
+
+ assert(evalue == ETIMEDOUT);
+ return false;
+ }
+
+ pthread_cond_t _condition;
+};
+
#endif
-#include "SGThread.hxx"
+SGThread::SGThread() :
+ _privateData(new PrivateData)
+{
+}
+
+SGThread::~SGThread()
+{
+ delete _privateData;
+ _privateData = 0;
+}
-void*
-start_handler( void* arg )
+bool
+SGThread::start()
{
- SGThread* thr = static_cast<SGThread*>(arg);
- thr->run();
- return 0;
+ return _privateData->start(*this);
}
void
-SGThread::set_cancel( cancel_t mode )
+SGThread::join()
{
- switch (mode)
- {
- case CANCEL_DISABLE:
- pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, 0 );
- break;
- case CANCEL_DEFERRED:
- pthread_setcanceltype( PTHREAD_CANCEL_DEFERRED, 0 );
- pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, 0 );
- break;
- case CANCEL_IMMEDIATE:
- pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, 0 );
- pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, 0 );
- break;
- default:
- break;
- }
+ _privateData->join();
}
-bool
-SGMutex::trylock()
+SGMutex::SGMutex() :
+ _privateData(new PrivateData)
{
- int status = pthread_mutex_lock( &mutex );
- if (status == EBUSY)
- {
- return false;
- }
- assert( status == 0 );
- return true;
}
-#if defined(_MSC_VER) || defined(__MINGW32__)
-int gettimeofday(struct timeval* tp, void* tzp) {
- LARGE_INTEGER t;
-
- if(QueryPerformanceCounter(&t)) {
- /* hardware supports a performance counter */
- LARGE_INTEGER f;
- QueryPerformanceFrequency(&f);
- tp->tv_sec = t.QuadPart/f.QuadPart;
- tp->tv_usec = ((float)t.QuadPart/f.QuadPart*1000*1000)
- - (tp->tv_sec*1000*1000);
- } else {
- /* hardware doesn't support a performance counter, so get the
- time in a more traditional way. */
- DWORD t;
- t = timeGetTime();
- tp->tv_sec = t / 1000;
- tp->tv_usec = t % 1000;
- }
-
- /* 0 indicates that the call succeeded. */
- return 0;
+SGMutex::~SGMutex()
+{
+ delete _privateData;
+ _privateData = 0;
}
-#endif
-bool
-SGPthreadCond::wait( SGMutex& mutex, unsigned long ms )
+void
+SGMutex::lock()
{
- struct timeval now;
- ::gettimeofday( &now, 0 );
+ _privateData->lock();
+}
- // Wait time is now + ms milliseconds
- unsigned int sec = ms / 1000;
- unsigned int nsec = (ms % 1000) * 1000;
- struct timespec abstime;
- abstime.tv_sec = now.tv_sec + sec;
- abstime.tv_nsec = now.tv_usec*1000 + nsec;
+void
+SGMutex::unlock()
+{
+ _privateData->unlock();
+}
- int status = pthread_cond_timedwait( &cond, &mutex.mutex, &abstime );
- if (status == ETIMEDOUT)
- {
- return false;
- }
+SGWaitCondition::SGWaitCondition() :
+ _privateData(new PrivateData)
+{
+}
+
+SGWaitCondition::~SGWaitCondition()
+{
+ delete _privateData;
+ _privateData = 0;
+}
- assert( status == 0 );
- return true;
+void
+SGWaitCondition::wait(SGMutex& mutex)
+{
+ _privateData->wait(*mutex._privateData);
}
+bool
+SGWaitCondition::wait(SGMutex& mutex, unsigned msec)
+{
+ return _privateData->wait(*mutex._privateData, msec);
+}
+
+void
+SGWaitCondition::signal()
+{
+ _privateData->signal();
+}
+
+void
+SGWaitCondition::broadcast()
+{
+ _privateData->broadcast();
+}
// Written by Bernie Bright, started April 2001.
//
// Copyright (C) 2001 Bernard Bright - bbright@bigpond.net.au
+// Copyright (C) 2011 Mathias Froehlich
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
-// $Id$
#ifndef SGTHREAD_HXX_INCLUDED
#define SGTHREAD_HXX_INCLUDED 1
#include <simgear/compiler.h>
-#include <pthread.h>
-#include <cassert>
-#include <cerrno>
-
-class SGThread;
-
-extern "C" {
- void* start_handler( void* );
-};
-
/**
* Encapsulate generic threading methods.
* Users derive a class from SGThread and implement the run() member function.
*/
-class SGThread
-{
-public:
- /**
- * SGThread cancelation modes.
- */
- enum cancel_t
- {
- CANCEL_DISABLE = 0,
- CANCEL_DEFERRED,
- CANCEL_IMMEDIATE
- };
+class SGThread {
public:
-
/**
* Create a new thread object.
* When a SGThread object is created it does not begin execution
/**
* Start the underlying thread of execution.
- * @param cpu An optional parameter to specify on which CPU to run this
- * thread (only supported on IRIX at this time).
* @return Pthread error code if execution fails, otherwise returns 0.
*/
- int start( unsigned cpu = 0 );
-
- /**
- * Sends a cancellation request to the underlying thread. The target
- * thread will either ignore the request, honor it immediately or defer
- * it until it reaches a cancellation point.
- */
- void cancel();
+ bool start();
/**
* Suspends the exection of the calling thread until this thread
*/
virtual ~SGThread();
- /**
- * Set the threads cancellation mode.
- * @param mode The required cancellation mode.
- */
- void set_cancel( cancel_t mode );
-
/**
* All threads execute by deriving the run() method of SGThread.
* If this function terminates then the thread also terminates.
*/
virtual void run() = 0;
-private:
-
- /**
- * Pthread thread identifier.
- */
- pthread_t tid;
-
- friend void* start_handler( void* );
-
private:
// Disable copying.
- SGThread( const SGThread& );
- SGThread& operator=( const SGThread& );
-};
+ SGThread(const SGThread&);
+ SGThread& operator=(const SGThread&);
-inline
-SGThread::SGThread()
-{
-}
+ struct PrivateData;
+ PrivateData* _privateData;
-inline
-SGThread::~SGThread()
-{
-}
-
-inline int
-SGThread::start( unsigned cpu )
-{
- int status = pthread_create( &tid, 0, start_handler, this );
- assert( status == 0 );
- (void)status;
-#if defined( sgi )
- if ( !status && !cpu )
- pthread_setrunon_np( cpu );
-#endif
- return status;
-}
-
-inline void
-SGThread::join()
-{
- int status = pthread_join( tid, 0 );
- assert( status == 0 );
- (void)status;
-}
+ friend struct PrivateData;
+};
-inline void
-SGThread::cancel()
-{
- int status = pthread_cancel( tid );
- assert( status == 0 );
- (void)status;
-}
+class SGWaitCondition;
/**
* A mutex is used to protect a section of code such that at any time
* only a single thread can execute the code.
*/
-class SGMutex
-{
- friend class SGPthreadCond;
-
+class SGMutex {
public:
-
/**
* Create a new mutex.
* Under Linux this is a 'fast' mutex.
* mutex is already locked and owned by the calling thread, the calling
* thread is suspended until the mutex is unlocked, effectively causing
* the calling thread to deadlock.
- *
- * @see SGMutex::trylock
*/
void lock();
- /**
- * Try to lock the mutex for the current thread. Behaves like lock except
- * that it doesn't block the calling thread.
- * @return true if mutex was successfully locked, otherwise false.
- * @see SGMutex::lock
- */
- bool trylock();
-
/**
* Unlock this mutex.
* It is assumed that the mutex is locked and owned by the calling thread.
*/
void unlock();
-protected:
+private:
+ struct PrivateData;
+ PrivateData* _privateData;
- /**
- * Pthread mutex.
- */
- pthread_mutex_t mutex;
+ friend class SGWaitCondition;
};
-inline SGMutex::SGMutex()
-{
- int status = pthread_mutex_init( &mutex, 0 );
- assert( status == 0 );
- (void)status;
-}
-
-inline SGMutex::~SGMutex()
-{
- int status = pthread_mutex_destroy( &mutex );
- assert( status == 0 );
- (void)status;
-}
-
-inline void SGMutex::lock()
-{
- int status = pthread_mutex_lock( &mutex );
- assert( status == 0 );
- (void)status;
-}
-
-inline void SGMutex::unlock()
-{
- int status = pthread_mutex_unlock( &mutex );
- assert( status == 0 );
- (void)status;
-}
-
/**
- * A condition variable is a synchronization device that allows threads to
+ * A condition variable is a synchronization device that allows threads to
* suspend execution until some predicate on shared data is satisfied.
* A condition variable is always associated with a mutex to avoid race
- * conditions.
+ * conditions.
*/
-class SGPthreadCond
-{
+class SGWaitCondition {
public:
/**
* Create a new condition variable.
*/
- SGPthreadCond();
+ SGWaitCondition();
/**
* Destroy the condition object.
*/
- ~SGPthreadCond();
+ ~SGWaitCondition();
/**
* Wait for this condition variable to be signaled.
*
* @param SGMutex& reference to a locked mutex.
*/
- void wait( SGMutex& );
+ void wait(SGMutex&);
/**
* Wait for this condition variable to be signaled for at most
* @param mutex reference to a locked mutex.
* @param ms milliseconds to wait for a signal.
*
- * @return
+ * @return
*/
- bool wait( SGMutex& mutex, unsigned long ms );
+ bool wait(SGMutex& mutex, unsigned msec);
/**
* Wake one thread waiting on this condition variable.
private:
// Disable copying.
- SGPthreadCond(const SGPthreadCond& );
- SGPthreadCond& operator=(const SGPthreadCond& );
-
-private:
+ SGWaitCondition(const SGWaitCondition&);
+ SGWaitCondition& operator=(const SGWaitCondition&);
- /**
- * The Pthread conditon variable.
- */
- pthread_cond_t cond;
+ struct PrivateData;
+ PrivateData* _privateData;
};
-inline SGPthreadCond::SGPthreadCond()
-{
- int status = pthread_cond_init( &cond, 0 );
- assert( status == 0 );
- (void)status;
-}
-
-inline SGPthreadCond::~SGPthreadCond()
-{
- int status = pthread_cond_destroy( &cond );
- assert( status == 0 );
- (void)status;
-}
-
-inline void SGPthreadCond::signal()
-{
- int status = pthread_cond_signal( &cond );
- assert( status == 0 );
- (void)status;
-}
-
-inline void SGPthreadCond::broadcast()
-{
- int status = pthread_cond_broadcast( &cond );
- assert( status == 0 );
- (void)status;
-}
-
-inline void SGPthreadCond::wait( SGMutex& mutex )
-{
- int status = pthread_cond_wait( &cond, &mutex.mutex );
- assert( status == 0 );
- (void)status;
-}
-
#endif /* SGTHREAD_HXX_INCLUDED */