]> git.mxchange.org Git - simgear.git/commitdiff
Reimplement SGThread and friends.
authorMathias Froehlich <Mathias.Froehlich@web.de>
Thu, 1 Sep 2011 16:19:09 +0000 (18:19 +0200)
committerMathias Froehlich <Mathias.Froehlich@web.de>
Wed, 7 Sep 2011 15:32:43 +0000 (17:32 +0200)
Reimplement the threading stuff using either pthreads
or win32 threads. These simple classes should help us
to stay osg independent for simgears core classes.

projects/VC90/SimGear.vcproj
simgear/threads/SGThread.cxx
simgear/threads/SGThread.hxx

index 34aa57522ebcc2283982519b60fec08555bce6fc..077af7ae5a0e089364f3ad3f4957bfc93e8818a7 100644 (file)
                                RelativePath="..\..\simgear\threads\SGQueue.hxx"\r
                                >\r
                        </File>\r
+                       <File\r
+                               RelativePath="..\..\simgear\threads\SGThread.hxx"\r
+                               >\r
+                       </File>\r
+                       <File\r
+                               RelativePath="..\..\simgear\threads\SGThread.cxx"\r
+                               >\r
+                       </File>\r
                </Filter>\r
                <Filter\r
                        Name="Lib_sgstructure"\r
index c7a841b02bb9d8b0ddb3dadbf3621baca0812ca5..b4afc65198ed83ffbbfe4a2a65e35ab086cbac15 100644 (file)
+// 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();
+}
index 477ca458f354b6f10807e766a00aa3f006386b1d..7f3a651a800b195b493c86d676f68a11a1ab7f1b 100644 (file)
@@ -3,6 +3,7 @@
 // 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
@@ -62,18 +40,9 @@ public:
 
     /**
      * 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
@@ -89,82 +58,31 @@ protected:
      */
     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.
@@ -186,86 +104,46 @@ public:
      * 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
@@ -274,9 +152,9 @@ public:
      * @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.
@@ -294,50 +172,11 @@ public:
 
 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 */