src/thread/stdcpp/SDL_syscond.cpp
changeset 8360 7f1bc00e59fc
parent 8357 8d788bb003f2
child 8484 7258989352eb
     1.1 --- a/src/thread/stdcpp/SDL_syscond.cpp	Sat Nov 24 11:16:45 2012 -0500
     1.2 +++ b/src/thread/stdcpp/SDL_syscond.cpp	Sat Nov 24 11:17:23 2012 -0500
     1.3 @@ -20,21 +20,20 @@
     1.4  */
     1.5  #include "SDL_config.h"
     1.6  
     1.7 -/* An implementation of condition variables using semaphores and mutexes */
     1.8 -/*
     1.9 -   This implementation borrows heavily from the BeOS condition variable
    1.10 -   implementation, written by Christopher Tate and Owen Smith.  Thanks!
    1.11 - */
    1.12 +extern "C" {
    1.13 +#include "SDL_thread.h"
    1.14 +}
    1.15  
    1.16 -#include "SDL_thread.h"
    1.17 +#include <chrono>
    1.18 +#include <condition_variable>
    1.19 +#include <exception>
    1.20 +#include <ratio>
    1.21 +
    1.22 +#include "SDL_sysmutex_c.h"
    1.23  
    1.24  struct SDL_cond
    1.25  {
    1.26 -    SDL_mutex *lock;
    1.27 -    int waiting;
    1.28 -    int signals;
    1.29 -    SDL_sem *wait_sem;
    1.30 -    SDL_sem *wait_done;
    1.31 +    std::condition_variable_any cpp_cond;
    1.32  };
    1.33  
    1.34  /* Create a condition variable */
    1.35 @@ -42,22 +41,17 @@
    1.36  SDL_cond *
    1.37  SDL_CreateCond(void)
    1.38  {
    1.39 -    SDL_cond *cond;
    1.40 -
    1.41 -    cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
    1.42 -    if (cond) {
    1.43 -        cond->lock = SDL_CreateMutex();
    1.44 -        cond->wait_sem = SDL_CreateSemaphore(0);
    1.45 -        cond->wait_done = SDL_CreateSemaphore(0);
    1.46 -        cond->waiting = cond->signals = 0;
    1.47 -        if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
    1.48 -            SDL_DestroyCond(cond);
    1.49 -            cond = NULL;
    1.50 -        }
    1.51 -    } else {
    1.52 -        SDL_OutOfMemory();
    1.53 +    /* Allocate and initialize the condition variable */
    1.54 +    try {
    1.55 +        SDL_cond * cond = new SDL_cond;
    1.56 +        return cond;
    1.57 +    } catch (std::exception & ex) {
    1.58 +        SDL_SetError("unable to create C++ condition variable: %s", ex.what());
    1.59 +        return NULL;
    1.60 +    } catch (...) {
    1.61 +        SDL_SetError("unable to create C++ condition variable due to an unknown exception");
    1.62 +        return NULL;
    1.63      }
    1.64 -    return (cond);
    1.65  }
    1.66  
    1.67  /* Destroy a condition variable */
    1.68 @@ -66,16 +60,11 @@
    1.69  SDL_DestroyCond(SDL_cond * cond)
    1.70  {
    1.71      if (cond) {
    1.72 -        if (cond->wait_sem) {
    1.73 -            SDL_DestroySemaphore(cond->wait_sem);
    1.74 +        try {
    1.75 +            delete cond;
    1.76 +        } catch (...) {
    1.77 +            // catch any and all exceptions, just in case something happens
    1.78          }
    1.79 -        if (cond->wait_done) {
    1.80 -            SDL_DestroySemaphore(cond->wait_done);
    1.81 -        }
    1.82 -        if (cond->lock) {
    1.83 -            SDL_DestroyMutex(cond->lock);
    1.84 -        }
    1.85 -        SDL_free(cond);
    1.86      }
    1.87  }
    1.88  
    1.89 @@ -89,20 +78,14 @@
    1.90          return -1;
    1.91      }
    1.92  
    1.93 -    /* If there are waiting threads not already signalled, then
    1.94 -       signal the condition and wait for the thread to respond.
    1.95 -     */
    1.96 -    SDL_LockMutex(cond->lock);
    1.97 -    if (cond->waiting > cond->signals) {
    1.98 -        ++cond->signals;
    1.99 -        SDL_SemPost(cond->wait_sem);
   1.100 -        SDL_UnlockMutex(cond->lock);
   1.101 -        SDL_SemWait(cond->wait_done);
   1.102 -    } else {
   1.103 -        SDL_UnlockMutex(cond->lock);
   1.104 +    try {
   1.105 +        cond->cpp_cond.notify_one();
   1.106 +        return 0;
   1.107 +    } catch (...) {
   1.108 +        // catch any and all exceptions, just in case something happens
   1.109 +        SDL_SetError("unable to signal C++ condition variable due to an unknown exception");
   1.110 +        return -1;
   1.111      }
   1.112 -
   1.113 -    return 0;
   1.114  }
   1.115  
   1.116  /* Restart all threads that are waiting on the condition variable */
   1.117 @@ -115,30 +98,14 @@
   1.118          return -1;
   1.119      }
   1.120  
   1.121 -    /* If there are waiting threads not already signalled, then
   1.122 -       signal the condition and wait for the thread to respond.
   1.123 -     */
   1.124 -    SDL_LockMutex(cond->lock);
   1.125 -    if (cond->waiting > cond->signals) {
   1.126 -        int i, num_waiting;
   1.127 -
   1.128 -        num_waiting = (cond->waiting - cond->signals);
   1.129 -        cond->signals = cond->waiting;
   1.130 -        for (i = 0; i < num_waiting; ++i) {
   1.131 -            SDL_SemPost(cond->wait_sem);
   1.132 -        }
   1.133 -        /* Now all released threads are blocked here, waiting for us.
   1.134 -           Collect them all (and win fabulous prizes!) :-)
   1.135 -         */
   1.136 -        SDL_UnlockMutex(cond->lock);
   1.137 -        for (i = 0; i < num_waiting; ++i) {
   1.138 -            SDL_SemWait(cond->wait_done);
   1.139 -        }
   1.140 -    } else {
   1.141 -        SDL_UnlockMutex(cond->lock);
   1.142 +    try {
   1.143 +        cond->cpp_cond.notify_all();
   1.144 +        return 0;
   1.145 +    } catch (...) {
   1.146 +        // catch any and all exceptions, just in case something happens
   1.147 +        SDL_SetError("unable to broadcast C++ condition variable due to an unknown exception");
   1.148 +        return -1;
   1.149      }
   1.150 -
   1.151 -    return 0;
   1.152  }
   1.153  
   1.154  /* Wait on the condition variable for at most 'ms' milliseconds.
   1.155 @@ -166,56 +133,43 @@
   1.156  int
   1.157  SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
   1.158  {
   1.159 -    int retval;
   1.160 -
   1.161      if (!cond) {
   1.162          SDL_SetError("Passed a NULL condition variable");
   1.163          return -1;
   1.164      }
   1.165  
   1.166 -    /* Obtain the protection mutex, and increment the number of waiters.
   1.167 -       This allows the signal mechanism to only perform a signal if there
   1.168 -       are waiting threads.
   1.169 -     */
   1.170 -    SDL_LockMutex(cond->lock);
   1.171 -    ++cond->waiting;
   1.172 -    SDL_UnlockMutex(cond->lock);
   1.173 -
   1.174 -    /* Unlock the mutex, as is required by condition variable semantics */
   1.175 -    SDL_UnlockMutex(mutex);
   1.176 -
   1.177 -    /* Wait for a signal */
   1.178 -    if (ms == SDL_MUTEX_MAXWAIT) {
   1.179 -        retval = SDL_SemWait(cond->wait_sem);
   1.180 -    } else {
   1.181 -        retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
   1.182 +    if (!mutex) {
   1.183 +        SDL_SetError("Passed a NULL mutex variable");
   1.184 +        return -1;
   1.185      }
   1.186  
   1.187 -    /* Let the signaler know we have completed the wait, otherwise
   1.188 -       the signaler can race ahead and get the condition semaphore
   1.189 -       if we are stopped between the mutex unlock and semaphore wait,
   1.190 -       giving a deadlock.  See the following URL for details:
   1.191 -       http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
   1.192 -     */
   1.193 -    SDL_LockMutex(cond->lock);
   1.194 -    if (cond->signals > 0) {
   1.195 -        /* If we timed out, we need to eat a condition signal */
   1.196 -        if (retval > 0) {
   1.197 -            SDL_SemWait(cond->wait_sem);
   1.198 +    try {
   1.199 +        std::unique_lock<std::recursive_mutex> cpp_lock(mutex->cpp_mutex, std::defer_lock_t());
   1.200 +        if (ms == SDL_MUTEX_MAXWAIT) {
   1.201 +            cond->cpp_cond.wait(
   1.202 +                cpp_lock
   1.203 +                );
   1.204 +            cpp_lock.release();
   1.205 +            return 0;
   1.206 +        } else {
   1.207 +            auto wait_result = cond->cpp_cond.wait_for(
   1.208 +                cpp_lock,
   1.209 +                std::chrono::duration<Uint32, std::milli>(ms)
   1.210 +                );
   1.211 +            cpp_lock.release();
   1.212 +            if (wait_result == std::cv_status::timeout) {
   1.213 +                return SDL_MUTEX_TIMEDOUT;
   1.214 +            } else {
   1.215 +                return 0;
   1.216 +            }
   1.217          }
   1.218 -        /* We always notify the signal thread that we are done */
   1.219 -        SDL_SemPost(cond->wait_done);
   1.220 -
   1.221 -        /* Signal handshake complete */
   1.222 -        --cond->signals;
   1.223 +    } catch (std::exception & ex) {
   1.224 +        SDL_SetError("unable to wait on C++ condition variable: %s", ex.what());
   1.225 +        return -1;
   1.226 +    } catch (...) {
   1.227 +        SDL_SetError("unable to lock wait on C++ condition variable due to an unknown exception");
   1.228 +        return -1;
   1.229      }
   1.230 -    --cond->waiting;
   1.231 -    SDL_UnlockMutex(cond->lock);
   1.232 -
   1.233 -    /* Lock the mutex, as is required by condition variable semantics */
   1.234 -    SDL_LockMutex(mutex);
   1.235 -
   1.236 -    return retval;
   1.237  }
   1.238  
   1.239  /* Wait on the condition variable forever */