src/thread/stdcpp/SDL_syscond.cpp
changeset 8357 8d788bb003f2
parent 8356 4d85eba58f0a
child 8360 7f1bc00e59fc
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/src/thread/stdcpp/SDL_syscond.cpp	Thu Nov 22 23:12:06 2012 -0500
     1.3 @@ -0,0 +1,229 @@
     1.4 +/*
     1.5 +  Simple DirectMedia Layer
     1.6 +  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
     1.7 +
     1.8 +  This software is provided 'as-is', without any express or implied
     1.9 +  warranty.  In no event will the authors be held liable for any damages
    1.10 +  arising from the use of this software.
    1.11 +
    1.12 +  Permission is granted to anyone to use this software for any purpose,
    1.13 +  including commercial applications, and to alter it and redistribute it
    1.14 +  freely, subject to the following restrictions:
    1.15 +
    1.16 +  1. The origin of this software must not be misrepresented; you must not
    1.17 +     claim that you wrote the original software. If you use this software
    1.18 +     in a product, an acknowledgment in the product documentation would be
    1.19 +     appreciated but is not required.
    1.20 +  2. Altered source versions must be plainly marked as such, and must not be
    1.21 +     misrepresented as being the original software.
    1.22 +  3. This notice may not be removed or altered from any source distribution.
    1.23 +*/
    1.24 +#include "SDL_config.h"
    1.25 +
    1.26 +/* An implementation of condition variables using semaphores and mutexes */
    1.27 +/*
    1.28 +   This implementation borrows heavily from the BeOS condition variable
    1.29 +   implementation, written by Christopher Tate and Owen Smith.  Thanks!
    1.30 + */
    1.31 +
    1.32 +#include "SDL_thread.h"
    1.33 +
    1.34 +struct SDL_cond
    1.35 +{
    1.36 +    SDL_mutex *lock;
    1.37 +    int waiting;
    1.38 +    int signals;
    1.39 +    SDL_sem *wait_sem;
    1.40 +    SDL_sem *wait_done;
    1.41 +};
    1.42 +
    1.43 +/* Create a condition variable */
    1.44 +extern "C"
    1.45 +SDL_cond *
    1.46 +SDL_CreateCond(void)
    1.47 +{
    1.48 +    SDL_cond *cond;
    1.49 +
    1.50 +    cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
    1.51 +    if (cond) {
    1.52 +        cond->lock = SDL_CreateMutex();
    1.53 +        cond->wait_sem = SDL_CreateSemaphore(0);
    1.54 +        cond->wait_done = SDL_CreateSemaphore(0);
    1.55 +        cond->waiting = cond->signals = 0;
    1.56 +        if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
    1.57 +            SDL_DestroyCond(cond);
    1.58 +            cond = NULL;
    1.59 +        }
    1.60 +    } else {
    1.61 +        SDL_OutOfMemory();
    1.62 +    }
    1.63 +    return (cond);
    1.64 +}
    1.65 +
    1.66 +/* Destroy a condition variable */
    1.67 +extern "C"
    1.68 +void
    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 +        }
    1.75 +        if (cond->wait_done) {
    1.76 +            SDL_DestroySemaphore(cond->wait_done);
    1.77 +        }
    1.78 +        if (cond->lock) {
    1.79 +            SDL_DestroyMutex(cond->lock);
    1.80 +        }
    1.81 +        SDL_free(cond);
    1.82 +    }
    1.83 +}
    1.84 +
    1.85 +/* Restart one of the threads that are waiting on the condition variable */
    1.86 +extern "C"
    1.87 +int
    1.88 +SDL_CondSignal(SDL_cond * cond)
    1.89 +{
    1.90 +    if (!cond) {
    1.91 +        SDL_SetError("Passed a NULL condition variable");
    1.92 +        return -1;
    1.93 +    }
    1.94 +
    1.95 +    /* If there are waiting threads not already signalled, then
    1.96 +       signal the condition and wait for the thread to respond.
    1.97 +     */
    1.98 +    SDL_LockMutex(cond->lock);
    1.99 +    if (cond->waiting > cond->signals) {
   1.100 +        ++cond->signals;
   1.101 +        SDL_SemPost(cond->wait_sem);
   1.102 +        SDL_UnlockMutex(cond->lock);
   1.103 +        SDL_SemWait(cond->wait_done);
   1.104 +    } else {
   1.105 +        SDL_UnlockMutex(cond->lock);
   1.106 +    }
   1.107 +
   1.108 +    return 0;
   1.109 +}
   1.110 +
   1.111 +/* Restart all threads that are waiting on the condition variable */
   1.112 +extern "C"
   1.113 +int
   1.114 +SDL_CondBroadcast(SDL_cond * cond)
   1.115 +{
   1.116 +    if (!cond) {
   1.117 +        SDL_SetError("Passed a NULL condition variable");
   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 +    }
   1.143 +
   1.144 +    return 0;
   1.145 +}
   1.146 +
   1.147 +/* Wait on the condition variable for at most 'ms' milliseconds.
   1.148 +   The mutex must be locked before entering this function!
   1.149 +   The mutex is unlocked during the wait, and locked again after the wait.
   1.150 +
   1.151 +Typical use:
   1.152 +
   1.153 +Thread A:
   1.154 +    SDL_LockMutex(lock);
   1.155 +    while ( ! condition ) {
   1.156 +        SDL_CondWait(cond, lock);
   1.157 +    }
   1.158 +    SDL_UnlockMutex(lock);
   1.159 +
   1.160 +Thread B:
   1.161 +    SDL_LockMutex(lock);
   1.162 +    ...
   1.163 +    condition = true;
   1.164 +    ...
   1.165 +    SDL_CondSignal(cond);
   1.166 +    SDL_UnlockMutex(lock);
   1.167 + */
   1.168 +extern "C"
   1.169 +int
   1.170 +SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
   1.171 +{
   1.172 +    int retval;
   1.173 +
   1.174 +    if (!cond) {
   1.175 +        SDL_SetError("Passed a NULL condition variable");
   1.176 +        return -1;
   1.177 +    }
   1.178 +
   1.179 +    /* Obtain the protection mutex, and increment the number of waiters.
   1.180 +       This allows the signal mechanism to only perform a signal if there
   1.181 +       are waiting threads.
   1.182 +     */
   1.183 +    SDL_LockMutex(cond->lock);
   1.184 +    ++cond->waiting;
   1.185 +    SDL_UnlockMutex(cond->lock);
   1.186 +
   1.187 +    /* Unlock the mutex, as is required by condition variable semantics */
   1.188 +    SDL_UnlockMutex(mutex);
   1.189 +
   1.190 +    /* Wait for a signal */
   1.191 +    if (ms == SDL_MUTEX_MAXWAIT) {
   1.192 +        retval = SDL_SemWait(cond->wait_sem);
   1.193 +    } else {
   1.194 +        retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
   1.195 +    }
   1.196 +
   1.197 +    /* Let the signaler know we have completed the wait, otherwise
   1.198 +       the signaler can race ahead and get the condition semaphore
   1.199 +       if we are stopped between the mutex unlock and semaphore wait,
   1.200 +       giving a deadlock.  See the following URL for details:
   1.201 +       http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
   1.202 +     */
   1.203 +    SDL_LockMutex(cond->lock);
   1.204 +    if (cond->signals > 0) {
   1.205 +        /* If we timed out, we need to eat a condition signal */
   1.206 +        if (retval > 0) {
   1.207 +            SDL_SemWait(cond->wait_sem);
   1.208 +        }
   1.209 +        /* We always notify the signal thread that we are done */
   1.210 +        SDL_SemPost(cond->wait_done);
   1.211 +
   1.212 +        /* Signal handshake complete */
   1.213 +        --cond->signals;
   1.214 +    }
   1.215 +    --cond->waiting;
   1.216 +    SDL_UnlockMutex(cond->lock);
   1.217 +
   1.218 +    /* Lock the mutex, as is required by condition variable semantics */
   1.219 +    SDL_LockMutex(mutex);
   1.220 +
   1.221 +    return retval;
   1.222 +}
   1.223 +
   1.224 +/* Wait on the condition variable forever */
   1.225 +extern "C"
   1.226 +int
   1.227 +SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex)
   1.228 +{
   1.229 +    return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
   1.230 +}
   1.231 +
   1.232 +/* vi: set ts=4 sw=4 expandtab: */