src/thread/generic/SDL_syscond.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 15 Feb 2013 08:47:44 -0800
changeset 6885 700f1b25f77f
parent 6138 4c64952a58fb
child 7037 3fedf1f25b94
permissions -rw-r--r--
Happy New Year!
slouken@0
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@6885
     3
  Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
slouken@0
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@0
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@0
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@0
    20
*/
slouken@1402
    21
#include "SDL_config.h"
slouken@0
    22
slouken@0
    23
/* An implementation of condition variables using semaphores and mutexes */
slouken@0
    24
/*
slouken@0
    25
   This implementation borrows heavily from the BeOS condition variable
slouken@0
    26
   implementation, written by Christopher Tate and Owen Smith.  Thanks!
slouken@0
    27
 */
slouken@0
    28
slouken@0
    29
#include "SDL_thread.h"
slouken@0
    30
slouken@0
    31
struct SDL_cond
slouken@0
    32
{
slouken@1895
    33
    SDL_mutex *lock;
slouken@1895
    34
    int waiting;
slouken@1895
    35
    int signals;
slouken@1895
    36
    SDL_sem *wait_sem;
slouken@1895
    37
    SDL_sem *wait_done;
slouken@0
    38
};
slouken@0
    39
slouken@0
    40
/* Create a condition variable */
slouken@1895
    41
SDL_cond *
slouken@1895
    42
SDL_CreateCond(void)
slouken@0
    43
{
slouken@1895
    44
    SDL_cond *cond;
slouken@0
    45
slouken@1895
    46
    cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
slouken@1895
    47
    if (cond) {
slouken@1895
    48
        cond->lock = SDL_CreateMutex();
slouken@1895
    49
        cond->wait_sem = SDL_CreateSemaphore(0);
slouken@1895
    50
        cond->wait_done = SDL_CreateSemaphore(0);
slouken@1895
    51
        cond->waiting = cond->signals = 0;
slouken@1895
    52
        if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
slouken@1895
    53
            SDL_DestroyCond(cond);
slouken@1895
    54
            cond = NULL;
slouken@1895
    55
        }
slouken@1895
    56
    } else {
slouken@1895
    57
        SDL_OutOfMemory();
slouken@1895
    58
    }
slouken@1895
    59
    return (cond);
slouken@0
    60
}
slouken@0
    61
slouken@0
    62
/* Destroy a condition variable */
slouken@1895
    63
void
slouken@1895
    64
SDL_DestroyCond(SDL_cond * cond)
slouken@0
    65
{
slouken@1895
    66
    if (cond) {
slouken@1895
    67
        if (cond->wait_sem) {
slouken@1895
    68
            SDL_DestroySemaphore(cond->wait_sem);
slouken@1895
    69
        }
slouken@1895
    70
        if (cond->wait_done) {
slouken@1895
    71
            SDL_DestroySemaphore(cond->wait_done);
slouken@1895
    72
        }
slouken@1895
    73
        if (cond->lock) {
slouken@1895
    74
            SDL_DestroyMutex(cond->lock);
slouken@1895
    75
        }
slouken@1895
    76
        SDL_free(cond);
slouken@1895
    77
    }
slouken@0
    78
}
slouken@0
    79
slouken@0
    80
/* Restart one of the threads that are waiting on the condition variable */
slouken@1895
    81
int
slouken@1895
    82
SDL_CondSignal(SDL_cond * cond)
slouken@0
    83
{
slouken@1895
    84
    if (!cond) {
slouken@1895
    85
        SDL_SetError("Passed a NULL condition variable");
slouken@1895
    86
        return -1;
slouken@1895
    87
    }
slouken@0
    88
slouken@1895
    89
    /* If there are waiting threads not already signalled, then
slouken@1895
    90
       signal the condition and wait for the thread to respond.
slouken@1895
    91
     */
slouken@1895
    92
    SDL_LockMutex(cond->lock);
slouken@1895
    93
    if (cond->waiting > cond->signals) {
slouken@1895
    94
        ++cond->signals;
slouken@1895
    95
        SDL_SemPost(cond->wait_sem);
slouken@1895
    96
        SDL_UnlockMutex(cond->lock);
slouken@1895
    97
        SDL_SemWait(cond->wait_done);
slouken@1895
    98
    } else {
slouken@1895
    99
        SDL_UnlockMutex(cond->lock);
slouken@1895
   100
    }
slouken@0
   101
slouken@1895
   102
    return 0;
slouken@0
   103
}
slouken@0
   104
slouken@0
   105
/* Restart all threads that are waiting on the condition variable */
slouken@1895
   106
int
slouken@1895
   107
SDL_CondBroadcast(SDL_cond * cond)
slouken@0
   108
{
slouken@1895
   109
    if (!cond) {
slouken@1895
   110
        SDL_SetError("Passed a NULL condition variable");
slouken@1895
   111
        return -1;
slouken@1895
   112
    }
slouken@0
   113
slouken@1895
   114
    /* If there are waiting threads not already signalled, then
slouken@1895
   115
       signal the condition and wait for the thread to respond.
slouken@1895
   116
     */
slouken@1895
   117
    SDL_LockMutex(cond->lock);
slouken@1895
   118
    if (cond->waiting > cond->signals) {
slouken@1895
   119
        int i, num_waiting;
slouken@0
   120
slouken@1895
   121
        num_waiting = (cond->waiting - cond->signals);
slouken@1895
   122
        cond->signals = cond->waiting;
slouken@1895
   123
        for (i = 0; i < num_waiting; ++i) {
slouken@1895
   124
            SDL_SemPost(cond->wait_sem);
slouken@1895
   125
        }
slouken@1895
   126
        /* Now all released threads are blocked here, waiting for us.
slouken@1895
   127
           Collect them all (and win fabulous prizes!) :-)
slouken@1895
   128
         */
slouken@1895
   129
        SDL_UnlockMutex(cond->lock);
slouken@1895
   130
        for (i = 0; i < num_waiting; ++i) {
slouken@1895
   131
            SDL_SemWait(cond->wait_done);
slouken@1895
   132
        }
slouken@1895
   133
    } else {
slouken@1895
   134
        SDL_UnlockMutex(cond->lock);
slouken@1895
   135
    }
slouken@0
   136
slouken@1895
   137
    return 0;
slouken@0
   138
}
slouken@0
   139
slouken@0
   140
/* Wait on the condition variable for at most 'ms' milliseconds.
slouken@0
   141
   The mutex must be locked before entering this function!
slouken@0
   142
   The mutex is unlocked during the wait, and locked again after the wait.
slouken@0
   143
slouken@0
   144
Typical use:
slouken@0
   145
slouken@0
   146
Thread A:
slouken@5110
   147
    SDL_LockMutex(lock);
slouken@5110
   148
    while ( ! condition ) {
slouken@5110
   149
        SDL_CondWait(cond, lock);
slouken@5110
   150
    }
slouken@5110
   151
    SDL_UnlockMutex(lock);
slouken@0
   152
slouken@0
   153
Thread B:
slouken@5110
   154
    SDL_LockMutex(lock);
slouken@5110
   155
    ...
slouken@5110
   156
    condition = true;
slouken@5110
   157
    ...
slouken@5110
   158
    SDL_CondSignal(cond);
slouken@5110
   159
    SDL_UnlockMutex(lock);
slouken@0
   160
 */
slouken@1895
   161
int
slouken@1895
   162
SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
slouken@0
   163
{
slouken@1895
   164
    int retval;
slouken@0
   165
slouken@1895
   166
    if (!cond) {
slouken@1895
   167
        SDL_SetError("Passed a NULL condition variable");
slouken@1895
   168
        return -1;
slouken@1895
   169
    }
slouken@0
   170
slouken@1895
   171
    /* Obtain the protection mutex, and increment the number of waiters.
slouken@1895
   172
       This allows the signal mechanism to only perform a signal if there
slouken@1895
   173
       are waiting threads.
slouken@1895
   174
     */
slouken@1895
   175
    SDL_LockMutex(cond->lock);
slouken@1895
   176
    ++cond->waiting;
slouken@1895
   177
    SDL_UnlockMutex(cond->lock);
slouken@0
   178
slouken@1895
   179
    /* Unlock the mutex, as is required by condition variable semantics */
slouken@1895
   180
    SDL_UnlockMutex(mutex);
slouken@0
   181
slouken@1895
   182
    /* Wait for a signal */
slouken@1895
   183
    if (ms == SDL_MUTEX_MAXWAIT) {
slouken@1895
   184
        retval = SDL_SemWait(cond->wait_sem);
slouken@1895
   185
    } else {
slouken@1895
   186
        retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
slouken@1895
   187
    }
slouken@0
   188
slouken@1895
   189
    /* Let the signaler know we have completed the wait, otherwise
slouken@1895
   190
       the signaler can race ahead and get the condition semaphore
slouken@1895
   191
       if we are stopped between the mutex unlock and semaphore wait,
slouken@1895
   192
       giving a deadlock.  See the following URL for details:
slouken@1895
   193
       http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
slouken@1895
   194
     */
slouken@1895
   195
    SDL_LockMutex(cond->lock);
slouken@1895
   196
    if (cond->signals > 0) {
slouken@1895
   197
        /* If we timed out, we need to eat a condition signal */
slouken@1895
   198
        if (retval > 0) {
slouken@1895
   199
            SDL_SemWait(cond->wait_sem);
slouken@1895
   200
        }
slouken@1895
   201
        /* We always notify the signal thread that we are done */
slouken@1895
   202
        SDL_SemPost(cond->wait_done);
slouken@0
   203
slouken@1895
   204
        /* Signal handshake complete */
slouken@1895
   205
        --cond->signals;
slouken@1895
   206
    }
slouken@1895
   207
    --cond->waiting;
slouken@1895
   208
    SDL_UnlockMutex(cond->lock);
slouken@0
   209
slouken@1895
   210
    /* Lock the mutex, as is required by condition variable semantics */
slouken@1895
   211
    SDL_LockMutex(mutex);
slouken@0
   212
slouken@1895
   213
    return retval;
slouken@0
   214
}
slouken@0
   215
slouken@0
   216
/* Wait on the condition variable forever */
slouken@1895
   217
int
slouken@1895
   218
SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex)
slouken@0
   219
{
slouken@1895
   220
    return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
slouken@0
   221
}
slouken@1895
   222
slouken@1895
   223
/* vi: set ts=4 sw=4 expandtab: */