src/thread/SDL_thread.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
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Lesser General Public
     7     License as published by the Free Software Foundation; either
     8     version 2.1 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     Lesser General Public License for more details.
    14 
    15     You should have received a copy of the GNU Lesser General Public
    16     License along with this library; if not, write to the Free Software
    17     Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* System independent thread management routines for SDL */
    25 
    26 #include "SDL_mutex.h"
    27 #include "SDL_thread.h"
    28 #include "SDL_thread_c.h"
    29 #include "SDL_systhread.h"
    30 
    31 #define ARRAY_CHUNKSIZE	32
    32 /* The array of threads currently active in the application
    33    (except the main thread)
    34    The manipulation of an array here is safer than using a linked list.
    35 */
    36 static int SDL_maxthreads = 0;
    37 static int SDL_numthreads = 0;
    38 static SDL_Thread **SDL_Threads = NULL;
    39 static SDL_mutex *thread_lock = NULL;
    40 
    41 int
    42 SDL_ThreadsInit(void)
    43 {
    44     int retval;
    45 
    46     retval = 0;
    47     thread_lock = SDL_CreateMutex();
    48     if (thread_lock == NULL) {
    49         retval = -1;
    50     }
    51     return (retval);
    52 }
    53 
    54 /* This should never be called...
    55    If this is called by SDL_Quit(), we don't know whether or not we should
    56    clean up threads here.  If any threads are still running after this call,
    57    they will no longer have access to any per-thread data.
    58  */
    59 void
    60 SDL_ThreadsQuit(void)
    61 {
    62     SDL_mutex *mutex;
    63 
    64     mutex = thread_lock;
    65     thread_lock = NULL;
    66     if (mutex != NULL) {
    67         SDL_DestroyMutex(mutex);
    68     }
    69 }
    70 
    71 /* Routines for manipulating the thread list */
    72 static void
    73 SDL_AddThread(SDL_Thread * thread)
    74 {
    75     /* WARNING:
    76        If the very first threads are created simultaneously, then
    77        there could be a race condition causing memory corruption.
    78        In practice, this isn't a problem because by definition there
    79        is only one thread running the first time this is called.
    80      */
    81     if (!thread_lock) {
    82         if (SDL_ThreadsInit() < 0) {
    83             return;
    84         }
    85     }
    86     SDL_mutexP(thread_lock);
    87 
    88     /* Expand the list of threads, if necessary */
    89 #ifdef DEBUG_THREADS
    90     printf("Adding thread (%d already - %d max)\n",
    91            SDL_numthreads, SDL_maxthreads);
    92 #endif
    93     if (SDL_numthreads == SDL_maxthreads) {
    94         SDL_Thread **threads;
    95         threads = (SDL_Thread **) SDL_realloc(SDL_Threads,
    96                                               (SDL_maxthreads +
    97                                                ARRAY_CHUNKSIZE) *
    98                                               (sizeof *threads));
    99         if (threads == NULL) {
   100             SDL_OutOfMemory();
   101             goto done;
   102         }
   103         SDL_maxthreads += ARRAY_CHUNKSIZE;
   104         SDL_Threads = threads;
   105     }
   106     SDL_Threads[SDL_numthreads++] = thread;
   107   done:
   108     SDL_mutexV(thread_lock);
   109 }
   110 
   111 static void
   112 SDL_DelThread(SDL_Thread * thread)
   113 {
   114     int i;
   115 
   116     if (!thread_lock) {
   117         return;
   118     }
   119     SDL_mutexP(thread_lock);
   120     for (i = 0; i < SDL_numthreads; ++i) {
   121         if (thread == SDL_Threads[i]) {
   122             break;
   123         }
   124     }
   125     if (i < SDL_numthreads) {
   126         if (--SDL_numthreads > 0) {
   127             while (i < SDL_numthreads) {
   128                 SDL_Threads[i] = SDL_Threads[i + 1];
   129                 ++i;
   130             }
   131         } else {
   132             SDL_maxthreads = 0;
   133             SDL_free(SDL_Threads);
   134             SDL_Threads = NULL;
   135         }
   136 #ifdef DEBUG_THREADS
   137         printf("Deleting thread (%d left - %d max)\n",
   138                SDL_numthreads, SDL_maxthreads);
   139 #endif
   140     }
   141     SDL_mutexV(thread_lock);
   142 
   143     if (SDL_Threads == NULL) {
   144         SDL_ThreadsQuit();
   145     }
   146 }
   147 
   148 /* The default (non-thread-safe) global error variable */
   149 static SDL_error SDL_global_error;
   150 
   151 /* Routine to get the thread-specific error variable */
   152 SDL_error *
   153 SDL_GetErrBuf(void)
   154 {
   155     SDL_error *errbuf;
   156 
   157     errbuf = &SDL_global_error;
   158     if (SDL_Threads) {
   159         int i;
   160         Uint32 this_thread;
   161 
   162         this_thread = SDL_ThreadID();
   163         SDL_mutexP(thread_lock);
   164         for (i = 0; i < SDL_numthreads; ++i) {
   165             if (this_thread == SDL_Threads[i]->threadid) {
   166                 errbuf = &SDL_Threads[i]->errbuf;
   167                 break;
   168             }
   169         }
   170         SDL_mutexV(thread_lock);
   171     }
   172     return (errbuf);
   173 }
   174 
   175 
   176 /* Arguments and callback to setup and run the user thread function */
   177 typedef struct
   178 {
   179     int (SDLCALL * func) (void *);
   180     void *data;
   181     SDL_Thread *info;
   182     SDL_sem *wait;
   183 } thread_args;
   184 
   185 void
   186 SDL_RunThread(void *data)
   187 {
   188     thread_args *args;
   189     int (SDLCALL * userfunc) (void *);
   190     void *userdata;
   191     int *statusloc;
   192 
   193     /* Perform any system-dependent setup
   194        - this function cannot fail, and cannot use SDL_SetError()
   195      */
   196     SDL_SYS_SetupThread();
   197 
   198     /* Get the thread id */
   199     args = (thread_args *) data;
   200     args->info->threadid = SDL_ThreadID();
   201 
   202     /* Figure out what function to run */
   203     userfunc = args->func;
   204     userdata = args->data;
   205     statusloc = &args->info->status;
   206 
   207     /* Wake up the parent thread */
   208     SDL_SemPost(args->wait);
   209 
   210     /* Run the function */
   211     *statusloc = userfunc(userdata);
   212 }
   213 
   214 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   215 #undef SDL_CreateThread
   216 DECLSPEC SDL_Thread *SDLCALL
   217 SDL_CreateThread(int (SDLCALL * fn) (void *), void *data,
   218                  pfnSDL_CurrentBeginThread pfnBeginThread,
   219                  pfnSDL_CurrentEndThread pfnEndThread)
   220 #else
   221 DECLSPEC SDL_Thread *SDLCALL
   222 SDL_CreateThread(int (SDLCALL * fn) (void *), void *data)
   223 #endif
   224 {
   225     SDL_Thread *thread;
   226     thread_args *args;
   227     int ret;
   228 
   229     /* Allocate memory for the thread info structure */
   230     thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
   231     if (thread == NULL) {
   232         SDL_OutOfMemory();
   233         return (NULL);
   234     }
   235     SDL_memset(thread, 0, (sizeof *thread));
   236     thread->status = -1;
   237 
   238     /* Set up the arguments for the thread */
   239     args = (thread_args *) SDL_malloc(sizeof(*args));
   240     if (args == NULL) {
   241         SDL_OutOfMemory();
   242         SDL_free(thread);
   243         return (NULL);
   244     }
   245     args->func = fn;
   246     args->data = data;
   247     args->info = thread;
   248     args->wait = SDL_CreateSemaphore(0);
   249     if (args->wait == NULL) {
   250         SDL_free(thread);
   251         SDL_free(args);
   252         return (NULL);
   253     }
   254 
   255     /* Add the thread to the list of available threads */
   256     SDL_AddThread(thread);
   257 
   258     /* Create the thread and go! */
   259 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   260     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   261 #else
   262     ret = SDL_SYS_CreateThread(thread, args);
   263 #endif
   264     if (ret >= 0) {
   265         /* Wait for the thread function to use arguments */
   266         SDL_SemWait(args->wait);
   267     } else {
   268         /* Oops, failed.  Gotta free everything */
   269         SDL_DelThread(thread);
   270         SDL_free(thread);
   271         thread = NULL;
   272     }
   273     SDL_DestroySemaphore(args->wait);
   274     SDL_free(args);
   275 
   276     /* Everything is running now */
   277     return (thread);
   278 }
   279 
   280 void
   281 SDL_WaitThread(SDL_Thread * thread, int *status)
   282 {
   283     if (thread) {
   284         SDL_SYS_WaitThread(thread);
   285         if (status) {
   286             *status = thread->status;
   287         }
   288         SDL_DelThread(thread);
   289         SDL_free(thread);
   290     }
   291 }
   292 
   293 Uint32
   294 SDL_GetThreadID(SDL_Thread * thread)
   295 {
   296     Uint32 id;
   297 
   298     if (thread) {
   299         id = thread->threadid;
   300     } else {
   301         id = SDL_ThreadID();
   302     }
   303     return (id);
   304 }
   305 
   306 void
   307 SDL_KillThread(SDL_Thread * thread)
   308 {
   309     if (thread) {
   310         SDL_SYS_KillThread(thread);
   311         SDL_WaitThread(thread, NULL);
   312     }
   313 }
   314 
   315 /* vi: set ts=4 sw=4 expandtab: */