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