src/thread/SDL_thread.c
author Sam Lantinga <slouken@libsdl.org>
Mon, 31 Oct 2011 05:56:58 -0400
changeset 6044 35448a5ea044
parent 5969 3a041d215edc
child 6138 4c64952a58fb
permissions -rw-r--r--
Lots of fixes importing SDL source wholesale into a new iOS project
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2011 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 /* System independent thread management routines for SDL */
    24 
    25 #include "SDL_mutex.h"
    26 #include "SDL_thread.h"
    27 #include "SDL_thread_c.h"
    28 #include "SDL_systhread.h"
    29 #include "../SDL_error_c.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 static 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 #if 0
    60 static void
    61 SDL_ThreadsQuit(void)
    62 {
    63     SDL_mutex *mutex;
    64 
    65     mutex = thread_lock;
    66     thread_lock = NULL;
    67     if (mutex != NULL) {
    68         SDL_DestroyMutex(mutex);
    69     }
    70 }
    71 #endif
    72 
    73 /* Routines for manipulating the thread list */
    74 static void
    75 SDL_AddThread(SDL_Thread * thread)
    76 {
    77     /* WARNING:
    78        If the very first threads are created simultaneously, then
    79        there could be a race condition causing memory corruption.
    80        In practice, this isn't a problem because by definition there
    81        is only one thread running the first time this is called.
    82      */
    83     if (!thread_lock) {
    84         if (SDL_ThreadsInit() < 0) {
    85             return;
    86         }
    87     }
    88     SDL_mutexP(thread_lock);
    89 
    90     /* Expand the list of threads, if necessary */
    91 #ifdef DEBUG_THREADS
    92     printf("Adding thread (%d already - %d max)\n",
    93            SDL_numthreads, SDL_maxthreads);
    94 #endif
    95     if (SDL_numthreads == SDL_maxthreads) {
    96         SDL_Thread **threads;
    97         threads = (SDL_Thread **) SDL_realloc(SDL_Threads,
    98                                               (SDL_maxthreads +
    99                                                ARRAY_CHUNKSIZE) *
   100                                               (sizeof *threads));
   101         if (threads == NULL) {
   102             SDL_OutOfMemory();
   103             goto done;
   104         }
   105         SDL_maxthreads += ARRAY_CHUNKSIZE;
   106         SDL_Threads = threads;
   107     }
   108     SDL_Threads[SDL_numthreads++] = thread;
   109   done:
   110     SDL_mutexV(thread_lock);
   111 }
   112 
   113 static void
   114 SDL_DelThread(SDL_Thread * thread)
   115 {
   116     int i;
   117 
   118     if (!thread_lock) {
   119         return;
   120     }
   121     SDL_mutexP(thread_lock);
   122     for (i = 0; i < SDL_numthreads; ++i) {
   123         if (thread == SDL_Threads[i]) {
   124             break;
   125         }
   126     }
   127     if (i < SDL_numthreads) {
   128         if (--SDL_numthreads > 0) {
   129             while (i < SDL_numthreads) {
   130                 SDL_Threads[i] = SDL_Threads[i + 1];
   131                 ++i;
   132             }
   133         } else {
   134             SDL_maxthreads = 0;
   135             SDL_free(SDL_Threads);
   136             SDL_Threads = NULL;
   137         }
   138 #ifdef DEBUG_THREADS
   139         printf("Deleting thread (%d left - %d max)\n",
   140                SDL_numthreads, SDL_maxthreads);
   141 #endif
   142     }
   143     SDL_mutexV(thread_lock);
   144 
   145 #if 0   /* There could be memory corruption if another thread is starting */
   146     if (SDL_Threads == NULL) {
   147         SDL_ThreadsQuit();
   148     }
   149 #endif
   150 }
   151 
   152 /* The default (non-thread-safe) global error variable */
   153 static SDL_error SDL_global_error;
   154 
   155 /* Routine to get the thread-specific error variable */
   156 SDL_error *
   157 SDL_GetErrBuf(void)
   158 {
   159     SDL_error *errbuf;
   160 
   161     errbuf = &SDL_global_error;
   162     if (SDL_Threads) {
   163         int i;
   164         SDL_threadID this_thread;
   165 
   166         this_thread = SDL_ThreadID();
   167         SDL_mutexP(thread_lock);
   168         for (i = 0; i < SDL_numthreads; ++i) {
   169             if (this_thread == SDL_Threads[i]->threadid) {
   170                 errbuf = &SDL_Threads[i]->errbuf;
   171                 break;
   172             }
   173         }
   174         SDL_mutexV(thread_lock);
   175     }
   176     return (errbuf);
   177 }
   178 
   179 
   180 /* Arguments and callback to setup and run the user thread function */
   181 typedef struct
   182 {
   183     int (SDLCALL * func) (void *);
   184     void *data;
   185     SDL_Thread *info;
   186     SDL_sem *wait;
   187 } thread_args;
   188 
   189 void
   190 SDL_RunThread(void *data)
   191 {
   192     thread_args *args = (thread_args *) data;
   193     int (SDLCALL * userfunc) (void *) = args->func;
   194     void *userdata = args->data;
   195     int *statusloc = &args->info->status;
   196 
   197     /* Perform any system-dependent setup
   198        - this function cannot fail, and cannot use SDL_SetError()
   199      */
   200     SDL_SYS_SetupThread(args->info->name);
   201 
   202     /* Get the thread id */
   203     args->info->threadid = SDL_ThreadID();
   204 
   205     /* Wake up the parent thread */
   206     SDL_SemPost(args->wait);
   207 
   208     /* Run the function */
   209     *statusloc = userfunc(userdata);
   210 }
   211 
   212 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   213 #undef SDL_CreateThread
   214 DECLSPEC SDL_Thread *SDLCALL
   215 SDL_CreateThread(int (SDLCALL * fn) (void *),
   216                  const char *name, void *data,
   217                  pfnSDL_CurrentBeginThread pfnBeginThread,
   218                  pfnSDL_CurrentEndThread pfnEndThread)
   219 #else
   220 DECLSPEC SDL_Thread *SDLCALL
   221 SDL_CreateThread(int (SDLCALL * fn) (void *),
   222                  const char *name, 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     if (name != NULL) {
   240         thread->name = SDL_strdup(name);
   241         if (thread->name == NULL) {
   242             SDL_OutOfMemory();
   243             SDL_free(thread);
   244             return (NULL);
   245         }
   246     }
   247 
   248     /* Set up the arguments for the thread */
   249     args = (thread_args *) SDL_malloc(sizeof(*args));
   250     if (args == NULL) {
   251         SDL_OutOfMemory();
   252         SDL_free(thread->name);
   253         SDL_free(thread);
   254         return (NULL);
   255     }
   256     args->func = fn;
   257     args->data = data;
   258     args->info = thread;
   259     args->wait = SDL_CreateSemaphore(0);
   260     if (args->wait == NULL) {
   261         SDL_free(thread->name);
   262         SDL_free(thread);
   263         SDL_free(args);
   264         return (NULL);
   265     }
   266 
   267     /* Add the thread to the list of available threads */
   268     SDL_AddThread(thread);
   269 
   270     /* Create the thread and go! */
   271 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   272     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   273 #else
   274     ret = SDL_SYS_CreateThread(thread, args);
   275 #endif
   276     if (ret >= 0) {
   277         /* Wait for the thread function to use arguments */
   278         SDL_SemWait(args->wait);
   279     } else {
   280         /* Oops, failed.  Gotta free everything */
   281         SDL_DelThread(thread);
   282         SDL_free(thread->name);
   283         SDL_free(thread);
   284         thread = NULL;
   285     }
   286     SDL_DestroySemaphore(args->wait);
   287     SDL_free(args);
   288 
   289     /* Everything is running now */
   290     return (thread);
   291 }
   292 
   293 SDL_threadID
   294 SDL_GetThreadID(SDL_Thread * thread)
   295 {
   296     SDL_threadID id;
   297 
   298     if (thread) {
   299         id = thread->threadid;
   300     } else {
   301         id = SDL_ThreadID();
   302     }
   303     return id;
   304 }
   305 
   306 const char *
   307 SDL_GetThreadName(SDL_Thread * thread)
   308 {
   309     return thread->name;
   310 }
   311 
   312 int
   313 SDL_SetThreadPriority(SDL_ThreadPriority priority)
   314 {
   315     return SDL_SYS_SetThreadPriority(priority);
   316 }
   317 
   318 void
   319 SDL_WaitThread(SDL_Thread * thread, int *status)
   320 {
   321     if (thread) {
   322         SDL_SYS_WaitThread(thread);
   323         if (status) {
   324             *status = thread->status;
   325         }
   326         SDL_DelThread(thread);
   327         SDL_free(thread->name);
   328         SDL_free(thread);
   329     }
   330 }
   331 
   332 /* vi: set ts=4 sw=4 expandtab: */