src/thread/nds/SDL_syscond.c
author Sam Lantinga <slouken@libsdl.org>
Wed, 27 Aug 2008 15:10:03 +0000
changeset 2735 204be4fc2726
child 2859 99210400e8b9
permissions -rw-r--r--
Final merge of Google Summer of Code 2008 work...

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