src/thread/SDL_thread.c
author Sam Lantinga
Sun, 24 Jan 2010 21:10:53 +0000
changeset 3697 f7b03b6838cb
parent 3578 0d1b16ee0bca
child 4468 82f357a91d43
permissions -rw-r--r--
Fixed bug #926

Updated copyright to LGPL version 2.1 and year 2010
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2010 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 0   /* There could be memory corruption if another thread is starting */
   144     if (SDL_Threads == NULL) {
   145         SDL_ThreadsQuit();
   146     }
   147 #endif
   148 }
   149 
   150 /* The default (non-thread-safe) global error variable */
   151 static SDL_error SDL_global_error;
   152 
   153 /* Routine to get the thread-specific error variable */
   154 SDL_error *
   155 SDL_GetErrBuf(void)
   156 {
   157     SDL_error *errbuf;
   158 
   159     errbuf = &SDL_global_error;
   160     if (SDL_Threads) {
   161         int i;
   162         SDL_threadID this_thread;
   163 
   164         this_thread = SDL_ThreadID();
   165         SDL_mutexP(thread_lock);
   166         for (i = 0; i < SDL_numthreads; ++i) {
   167             if (this_thread == SDL_Threads[i]->threadid) {
   168                 errbuf = &SDL_Threads[i]->errbuf;
   169                 break;
   170             }
   171         }
   172         SDL_mutexV(thread_lock);
   173     }
   174     return (errbuf);
   175 }
   176 
   177 
   178 /* Arguments and callback to setup and run the user thread function */
   179 typedef struct
   180 {
   181     int (SDLCALL * func) (void *);
   182     void *data;
   183     SDL_Thread *info;
   184     SDL_sem *wait;
   185 } thread_args;
   186 
   187 void
   188 SDL_RunThread(void *data)
   189 {
   190     thread_args *args;
   191     int (SDLCALL * userfunc) (void *);
   192     void *userdata;
   193     int *statusloc;
   194 
   195     /* Perform any system-dependent setup
   196        - this function cannot fail, and cannot use SDL_SetError()
   197      */
   198     SDL_SYS_SetupThread();
   199 
   200     /* Get the thread id */
   201     args = (thread_args *) data;
   202     args->info->threadid = SDL_ThreadID();
   203 
   204     /* Figure out what function to run */
   205     userfunc = args->func;
   206     userdata = args->data;
   207     statusloc = &args->info->status;
   208 
   209     /* Wake up the parent thread */
   210     SDL_SemPost(args->wait);
   211 
   212     /* Run the function */
   213     *statusloc = userfunc(userdata);
   214 }
   215 
   216 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   217 #undef SDL_CreateThread
   218 DECLSPEC SDL_Thread *SDLCALL
   219 SDL_CreateThread(int (SDLCALL * fn) (void *), void *data,
   220                  pfnSDL_CurrentBeginThread pfnBeginThread,
   221                  pfnSDL_CurrentEndThread pfnEndThread)
   222 #else
   223 DECLSPEC SDL_Thread *SDLCALL
   224 SDL_CreateThread(int (SDLCALL * fn) (void *), void *data)
   225 #endif
   226 {
   227     SDL_Thread *thread;
   228     thread_args *args;
   229     int ret;
   230 
   231     /* Allocate memory for the thread info structure */
   232     thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
   233     if (thread == NULL) {
   234         SDL_OutOfMemory();
   235         return (NULL);
   236     }
   237     SDL_memset(thread, 0, (sizeof *thread));
   238     thread->status = -1;
   239 
   240     /* Set up the arguments for the thread */
   241     args = (thread_args *) SDL_malloc(sizeof(*args));
   242     if (args == NULL) {
   243         SDL_OutOfMemory();
   244         SDL_free(thread);
   245         return (NULL);
   246     }
   247     args->func = fn;
   248     args->data = data;
   249     args->info = thread;
   250     args->wait = SDL_CreateSemaphore(0);
   251     if (args->wait == NULL) {
   252         SDL_free(thread);
   253         SDL_free(args);
   254         return (NULL);
   255     }
   256 
   257     /* Add the thread to the list of available threads */
   258     SDL_AddThread(thread);
   259 
   260     /* Create the thread and go! */
   261 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   262     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   263 #else
   264     ret = SDL_SYS_CreateThread(thread, args);
   265 #endif
   266     if (ret >= 0) {
   267         /* Wait for the thread function to use arguments */
   268         SDL_SemWait(args->wait);
   269     } else {
   270         /* Oops, failed.  Gotta free everything */
   271         SDL_DelThread(thread);
   272         SDL_free(thread);
   273         thread = NULL;
   274     }
   275     SDL_DestroySemaphore(args->wait);
   276     SDL_free(args);
   277 
   278     /* Everything is running now */
   279     return (thread);
   280 }
   281 
   282 void
   283 SDL_WaitThread(SDL_Thread * thread, int *status)
   284 {
   285     if (thread) {
   286         SDL_SYS_WaitThread(thread);
   287         if (status) {
   288             *status = thread->status;
   289         }
   290         SDL_DelThread(thread);
   291         SDL_free(thread);
   292     }
   293 }
   294 
   295 SDL_threadID
   296 SDL_GetThreadID(SDL_Thread * thread)
   297 {
   298     SDL_threadID id;
   299 
   300     if (thread) {
   301         id = thread->threadid;
   302     } else {
   303         id = SDL_ThreadID();
   304     }
   305     return id;
   306 }
   307 
   308 void
   309 SDL_KillThread(SDL_Thread * thread)
   310 {
   311     /* This is a no-op in SDL 1.3 and later. */
   312 }
   313 
   314 /* vi: set ts=4 sw=4 expandtab: */