src/thread/generic/SDL_syscond.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 29 May 2006 04:04:35 +0000
branchSDL-1.3
changeset 1668 4da1ee79c9af
parent 1662 782fd950bd46
permissions -rw-r--r--
more tweaking indent options
slouken@0
     1
/*
slouken@0
     2
    SDL - Simple DirectMedia Layer
slouken@1312
     3
    Copyright (C) 1997-2006 Sam Lantinga
slouken@0
     4
slouken@0
     5
    This library is free software; you can redistribute it and/or
slouken@1312
     6
    modify it under the terms of the GNU Lesser General Public
slouken@0
     7
    License as published by the Free Software Foundation; either
slouken@1312
     8
    version 2.1 of the License, or (at your option) any later version.
slouken@0
     9
slouken@0
    10
    This library is distributed in the hope that it will be useful,
slouken@0
    11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
slouken@0
    12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
slouken@1312
    13
    Lesser General Public License for more details.
slouken@0
    14
slouken@1312
    15
    You should have received a copy of the GNU Lesser General Public
slouken@1312
    16
    License along with this library; if not, write to the Free Software
slouken@1312
    17
    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
slouken@0
    18
slouken@0
    19
    Sam Lantinga
slouken@252
    20
    slouken@libsdl.org
slouken@0
    21
*/
slouken@1402
    22
#include "SDL_config.h"
slouken@0
    23
slouken@0
    24
/* An implementation of condition variables using semaphores and mutexes */
slouken@0
    25
/*
slouken@0
    26
   This implementation borrows heavily from the BeOS condition variable
slouken@0
    27
   implementation, written by Christopher Tate and Owen Smith.  Thanks!
slouken@0
    28
 */
slouken@0
    29
slouken@0
    30
#include "SDL_thread.h"
slouken@0
    31
slouken@0
    32
struct SDL_cond
slouken@0
    33
{
slouken@1662
    34
    SDL_mutex *lock;
slouken@1662
    35
    int waiting;
slouken@1662
    36
    int signals;
slouken@1662
    37
    SDL_sem *wait_sem;
slouken@1662
    38
    SDL_sem *wait_done;
slouken@0
    39
};
slouken@0
    40
slouken@0
    41
/* Create a condition variable */
slouken@1662
    42
SDL_cond *
slouken@1668
    43
SDL_CreateCond(void)
slouken@0
    44
{
slouken@1662
    45
    SDL_cond *cond;
slouken@0
    46
slouken@1668
    47
    cond = (SDL_cond *) SDL_malloc(sizeof(SDL_cond));
slouken@1662
    48
    if (cond) {
slouken@1668
    49
        cond->lock = SDL_CreateMutex();
slouken@1668
    50
        cond->wait_sem = SDL_CreateSemaphore(0);
slouken@1668
    51
        cond->wait_done = SDL_CreateSemaphore(0);
slouken@1662
    52
        cond->waiting = cond->signals = 0;
slouken@1662
    53
        if (!cond->lock || !cond->wait_sem || !cond->wait_done) {
slouken@1668
    54
            SDL_DestroyCond(cond);
slouken@1662
    55
            cond = NULL;
slouken@1662
    56
        }
slouken@1662
    57
    } else {
slouken@1668
    58
        SDL_OutOfMemory();
slouken@1662
    59
    }
slouken@1662
    60
    return (cond);
slouken@0
    61
}
slouken@0
    62
slouken@0
    63
/* Destroy a condition variable */
slouken@1662
    64
void
slouken@1668
    65
SDL_DestroyCond(SDL_cond * cond)
slouken@0
    66
{
slouken@1662
    67
    if (cond) {
slouken@1662
    68
        if (cond->wait_sem) {
slouken@1668
    69
            SDL_DestroySemaphore(cond->wait_sem);
slouken@1662
    70
        }
slouken@1662
    71
        if (cond->wait_done) {
slouken@1668
    72
            SDL_DestroySemaphore(cond->wait_done);
slouken@1662
    73
        }
slouken@1662
    74
        if (cond->lock) {
slouken@1668
    75
            SDL_DestroyMutex(cond->lock);
slouken@1662
    76
        }
slouken@1668
    77
        SDL_free(cond);
slouken@1662
    78
    }
slouken@0
    79
}
slouken@0
    80
slouken@0
    81
/* Restart one of the threads that are waiting on the condition variable */
slouken@1662
    82
int
slouken@1668
    83
SDL_CondSignal(SDL_cond * cond)
slouken@0
    84
{
slouken@1662
    85
    if (!cond) {
slouken@1668
    86
        SDL_SetError("Passed a NULL condition variable");
slouken@1662
    87
        return -1;
slouken@1662
    88
    }
slouken@0
    89
slouken@1662
    90
    /* If there are waiting threads not already signalled, then
slouken@1662
    91
       signal the condition and wait for the thread to respond.
slouken@1662
    92
     */
slouken@1668
    93
    SDL_LockMutex(cond->lock);
slouken@1662
    94
    if (cond->waiting > cond->signals) {
slouken@1662
    95
        ++cond->signals;
slouken@1668
    96
        SDL_SemPost(cond->wait_sem);
slouken@1668
    97
        SDL_UnlockMutex(cond->lock);
slouken@1668
    98
        SDL_SemWait(cond->wait_done);
slouken@1662
    99
    } else {
slouken@1668
   100
        SDL_UnlockMutex(cond->lock);
slouken@1662
   101
    }
slouken@0
   102
slouken@1662
   103
    return 0;
slouken@0
   104
}
slouken@0
   105
slouken@0
   106
/* Restart all threads that are waiting on the condition variable */
slouken@1662
   107
int
slouken@1668
   108
SDL_CondBroadcast(SDL_cond * cond)
slouken@0
   109
{
slouken@1662
   110
    if (!cond) {
slouken@1668
   111
        SDL_SetError("Passed a NULL condition variable");
slouken@1662
   112
        return -1;
slouken@1662
   113
    }
slouken@0
   114
slouken@1662
   115
    /* If there are waiting threads not already signalled, then
slouken@1662
   116
       signal the condition and wait for the thread to respond.
slouken@1662
   117
     */
slouken@1668
   118
    SDL_LockMutex(cond->lock);
slouken@1662
   119
    if (cond->waiting > cond->signals) {
slouken@1662
   120
        int i, num_waiting;
slouken@0
   121
slouken@1662
   122
        num_waiting = (cond->waiting - cond->signals);
slouken@1662
   123
        cond->signals = cond->waiting;
slouken@1662
   124
        for (i = 0; i < num_waiting; ++i) {
slouken@1668
   125
            SDL_SemPost(cond->wait_sem);
slouken@1662
   126
        }
slouken@1662
   127
        /* Now all released threads are blocked here, waiting for us.
slouken@1662
   128
           Collect them all (and win fabulous prizes!) :-)
slouken@1662
   129
         */
slouken@1668
   130
        SDL_UnlockMutex(cond->lock);
slouken@1662
   131
        for (i = 0; i < num_waiting; ++i) {
slouken@1668
   132
            SDL_SemWait(cond->wait_done);
slouken@1662
   133
        }
slouken@1662
   134
    } else {
slouken@1668
   135
        SDL_UnlockMutex(cond->lock);
slouken@1662
   136
    }
slouken@0
   137
slouken@1662
   138
    return 0;
slouken@0
   139
}
slouken@0
   140
slouken@0
   141
/* Wait on the condition variable for at most 'ms' milliseconds.
slouken@0
   142
   The mutex must be locked before entering this function!
slouken@0
   143
   The mutex is unlocked during the wait, and locked again after the wait.
slouken@0
   144
slouken@0
   145
Typical use:
slouken@0
   146
slouken@0
   147
Thread A:
slouken@0
   148
	SDL_LockMutex(lock);
slouken@0
   149
	while ( ! condition ) {
slouken@0
   150
		SDL_CondWait(cond);
slouken@0
   151
	}
slouken@0
   152
	SDL_UnlockMutex(lock);
slouken@0
   153
slouken@0
   154
Thread B:
slouken@0
   155
	SDL_LockMutex(lock);
slouken@0
   156
	...
slouken@0
   157
	condition = true;
slouken@0
   158
	...
slouken@0
   159
	SDL_UnlockMutex(lock);
slouken@0
   160
 */
slouken@1662
   161
int
slouken@1668
   162
SDL_CondWaitTimeout(SDL_cond * cond, SDL_mutex * mutex, Uint32 ms)
slouken@0
   163
{
slouken@1662
   164
    int retval;
slouken@0
   165
slouken@1662
   166
    if (!cond) {
slouken@1668
   167
        SDL_SetError("Passed a NULL condition variable");
slouken@1662
   168
        return -1;
slouken@1662
   169
    }
slouken@0
   170
slouken@1662
   171
    /* Obtain the protection mutex, and increment the number of waiters.
slouken@1662
   172
       This allows the signal mechanism to only perform a signal if there
slouken@1662
   173
       are waiting threads.
slouken@1662
   174
     */
slouken@1668
   175
    SDL_LockMutex(cond->lock);
slouken@1662
   176
    ++cond->waiting;
slouken@1668
   177
    SDL_UnlockMutex(cond->lock);
slouken@0
   178
slouken@1662
   179
    /* Unlock the mutex, as is required by condition variable semantics */
slouken@1668
   180
    SDL_UnlockMutex(mutex);
slouken@0
   181
slouken@1662
   182
    /* Wait for a signal */
slouken@1662
   183
    if (ms == SDL_MUTEX_MAXWAIT) {
slouken@1668
   184
        retval = SDL_SemWait(cond->wait_sem);
slouken@1662
   185
    } else {
slouken@1668
   186
        retval = SDL_SemWaitTimeout(cond->wait_sem, ms);
slouken@1662
   187
    }
slouken@0
   188
slouken@1662
   189
    /* Let the signaler know we have completed the wait, otherwise
slouken@1662
   190
       the signaler can race ahead and get the condition semaphore
slouken@1662
   191
       if we are stopped between the mutex unlock and semaphore wait,
slouken@1662
   192
       giving a deadlock.  See the following URL for details:
slouken@1662
   193
       http://www-classic.be.com/aboutbe/benewsletter/volume_III/Issue40.html
slouken@1662
   194
     */
slouken@1668
   195
    SDL_LockMutex(cond->lock);
slouken@1662
   196
    if (cond->signals > 0) {
slouken@1662
   197
        /* If we timed out, we need to eat a condition signal */
slouken@1662
   198
        if (retval > 0) {
slouken@1668
   199
            SDL_SemWait(cond->wait_sem);
slouken@1662
   200
        }
slouken@1662
   201
        /* We always notify the signal thread that we are done */
slouken@1668
   202
        SDL_SemPost(cond->wait_done);
slouken@0
   203
slouken@1662
   204
        /* Signal handshake complete */
slouken@1662
   205
        --cond->signals;
slouken@1662
   206
    }
slouken@1662
   207
    --cond->waiting;
slouken@1668
   208
    SDL_UnlockMutex(cond->lock);
slouken@0
   209
slouken@1662
   210
    /* Lock the mutex, as is required by condition variable semantics */
slouken@1668
   211
    SDL_LockMutex(mutex);
slouken@0
   212
slouken@1662
   213
    return retval;
slouken@0
   214
}
slouken@0
   215
slouken@0
   216
/* Wait on the condition variable forever */
slouken@1662
   217
int
slouken@1668
   218
SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex)
slouken@0
   219
{
slouken@1668
   220
    return SDL_CondWaitTimeout(cond, mutex, SDL_MUTEX_MAXWAIT);
slouken@0
   221
}
slouken@1662
   222
slouken@1662
   223
/* vi: set ts=4 sw=4 expandtab: */