src/thread/SDL_thread.c
author Ryan C. Gordon
Sun, 02 Oct 2011 00:29:16 -0400
changeset 5969 3a041d215edc
parent 5535 96594ac5fd1a
child 6044 35448a5ea044
permissions -rw-r--r--
1.3 API CHANGE: Add support for naming threads.
     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 
    30 #define ARRAY_CHUNKSIZE	32
    31 /* The array of threads currently active in the application
    32    (except the main thread)
    33    The manipulation of an array here is safer than using a linked list.
    34 */
    35 static int SDL_maxthreads = 0;
    36 static int SDL_numthreads = 0;
    37 static SDL_Thread **SDL_Threads = NULL;
    38 static SDL_mutex *thread_lock = NULL;
    39 
    40 static int
    41 SDL_ThreadsInit(void)
    42 {
    43     int retval;
    44 
    45     retval = 0;
    46     thread_lock = SDL_CreateMutex();
    47     if (thread_lock == NULL) {
    48         retval = -1;
    49     }
    50     return (retval);
    51 }
    52 
    53 /* This should never be called...
    54    If this is called by SDL_Quit(), we don't know whether or not we should
    55    clean up threads here.  If any threads are still running after this call,
    56    they will no longer have access to any per-thread data.
    57  */
    58 #if 0
    59 static 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 #endif
    71 
    72 /* Routines for manipulating the thread list */
    73 static void
    74 SDL_AddThread(SDL_Thread * thread)
    75 {
    76     /* WARNING:
    77        If the very first threads are created simultaneously, then
    78        there could be a race condition causing memory corruption.
    79        In practice, this isn't a problem because by definition there
    80        is only one thread running the first time this is called.
    81      */
    82     if (!thread_lock) {
    83         if (SDL_ThreadsInit() < 0) {
    84             return;
    85         }
    86     }
    87     SDL_mutexP(thread_lock);
    88 
    89     /* Expand the list of threads, if necessary */
    90 #ifdef DEBUG_THREADS
    91     printf("Adding thread (%d already - %d max)\n",
    92            SDL_numthreads, SDL_maxthreads);
    93 #endif
    94     if (SDL_numthreads == SDL_maxthreads) {
    95         SDL_Thread **threads;
    96         threads = (SDL_Thread **) SDL_realloc(SDL_Threads,
    97                                               (SDL_maxthreads +
    98                                                ARRAY_CHUNKSIZE) *
    99                                               (sizeof *threads));
   100         if (threads == NULL) {
   101             SDL_OutOfMemory();
   102             goto done;
   103         }
   104         SDL_maxthreads += ARRAY_CHUNKSIZE;
   105         SDL_Threads = threads;
   106     }
   107     SDL_Threads[SDL_numthreads++] = thread;
   108   done:
   109     SDL_mutexV(thread_lock);
   110 }
   111 
   112 static void
   113 SDL_DelThread(SDL_Thread * thread)
   114 {
   115     int i;
   116 
   117     if (!thread_lock) {
   118         return;
   119     }
   120     SDL_mutexP(thread_lock);
   121     for (i = 0; i < SDL_numthreads; ++i) {
   122         if (thread == SDL_Threads[i]) {
   123             break;
   124         }
   125     }
   126     if (i < SDL_numthreads) {
   127         if (--SDL_numthreads > 0) {
   128             while (i < SDL_numthreads) {
   129                 SDL_Threads[i] = SDL_Threads[i + 1];
   130                 ++i;
   131             }
   132         } else {
   133             SDL_maxthreads = 0;
   134             SDL_free(SDL_Threads);
   135             SDL_Threads = NULL;
   136         }
   137 #ifdef DEBUG_THREADS
   138         printf("Deleting thread (%d left - %d max)\n",
   139                SDL_numthreads, SDL_maxthreads);
   140 #endif
   141     }
   142     SDL_mutexV(thread_lock);
   143 
   144 #if 0   /* There could be memory corruption if another thread is starting */
   145     if (SDL_Threads == NULL) {
   146         SDL_ThreadsQuit();
   147     }
   148 #endif
   149 }
   150 
   151 /* The default (non-thread-safe) global error variable */
   152 static SDL_error SDL_global_error;
   153 
   154 /* Routine to get the thread-specific error variable */
   155 SDL_error *
   156 SDL_GetErrBuf(void)
   157 {
   158     SDL_error *errbuf;
   159 
   160     errbuf = &SDL_global_error;
   161     if (SDL_Threads) {
   162         int i;
   163         SDL_threadID this_thread;
   164 
   165         this_thread = SDL_ThreadID();
   166         SDL_mutexP(thread_lock);
   167         for (i = 0; i < SDL_numthreads; ++i) {
   168             if (this_thread == SDL_Threads[i]->threadid) {
   169                 errbuf = &SDL_Threads[i]->errbuf;
   170                 break;
   171             }
   172         }
   173         SDL_mutexV(thread_lock);
   174     }
   175     return (errbuf);
   176 }
   177 
   178 
   179 /* Arguments and callback to setup and run the user thread function */
   180 typedef struct
   181 {
   182     int (SDLCALL * func) (void *);
   183     void *data;
   184     SDL_Thread *info;
   185     SDL_sem *wait;
   186 } thread_args;
   187 
   188 void
   189 SDL_RunThread(void *data)
   190 {
   191     thread_args *args = (thread_args *) data;
   192     int (SDLCALL * userfunc) (void *) = args->func;
   193     void *userdata = args->data;
   194     int *statusloc = &args->info->status;
   195 
   196     /* Perform any system-dependent setup
   197        - this function cannot fail, and cannot use SDL_SetError()
   198      */
   199     SDL_SYS_SetupThread(args->info->name);
   200 
   201     /* Get the thread id */
   202     args->info->threadid = SDL_ThreadID();
   203 
   204     /* Wake up the parent thread */
   205     SDL_SemPost(args->wait);
   206 
   207     /* Run the function */
   208     *statusloc = userfunc(userdata);
   209 }
   210 
   211 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   212 #undef SDL_CreateThread
   213 DECLSPEC SDL_Thread *SDLCALL
   214 SDL_CreateThread(int (SDLCALL * fn) (void *),
   215                  const char *name, void *data,
   216                  pfnSDL_CurrentBeginThread pfnBeginThread,
   217                  pfnSDL_CurrentEndThread pfnEndThread)
   218 #else
   219 DECLSPEC SDL_Thread *SDLCALL
   220 SDL_CreateThread(int (SDLCALL * fn) (void *),
   221                  const char *name, void *data)
   222 #endif
   223 {
   224     SDL_Thread *thread;
   225     thread_args *args;
   226     int ret;
   227 
   228     /* Allocate memory for the thread info structure */
   229     thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
   230     if (thread == NULL) {
   231         SDL_OutOfMemory();
   232         return (NULL);
   233     }
   234     SDL_memset(thread, 0, (sizeof *thread));
   235     thread->status = -1;
   236 
   237     /* Set up the arguments for the thread */
   238     if (name != NULL) {
   239         thread->name = SDL_strdup(name);
   240         if (thread->name == NULL) {
   241             SDL_OutOfMemory();
   242             SDL_free(thread);
   243             return (NULL);
   244         }
   245     }
   246 
   247     /* Set up the arguments for the thread */
   248     args = (thread_args *) SDL_malloc(sizeof(*args));
   249     if (args == NULL) {
   250         SDL_OutOfMemory();
   251         SDL_free(thread->name);
   252         SDL_free(thread);
   253         return (NULL);
   254     }
   255     args->func = fn;
   256     args->data = data;
   257     args->info = thread;
   258     args->wait = SDL_CreateSemaphore(0);
   259     if (args->wait == NULL) {
   260         SDL_free(thread->name);
   261         SDL_free(thread);
   262         SDL_free(args);
   263         return (NULL);
   264     }
   265 
   266     /* Add the thread to the list of available threads */
   267     SDL_AddThread(thread);
   268 
   269     /* Create the thread and go! */
   270 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   271     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   272 #else
   273     ret = SDL_SYS_CreateThread(thread, args);
   274 #endif
   275     if (ret >= 0) {
   276         /* Wait for the thread function to use arguments */
   277         SDL_SemWait(args->wait);
   278     } else {
   279         /* Oops, failed.  Gotta free everything */
   280         SDL_DelThread(thread);
   281         SDL_free(thread->name);
   282         SDL_free(thread);
   283         thread = NULL;
   284     }
   285     SDL_DestroySemaphore(args->wait);
   286     SDL_free(args);
   287 
   288     /* Everything is running now */
   289     return (thread);
   290 }
   291 
   292 SDL_threadID
   293 SDL_GetThreadID(SDL_Thread * thread)
   294 {
   295     SDL_threadID id;
   296 
   297     if (thread) {
   298         id = thread->threadid;
   299     } else {
   300         id = SDL_ThreadID();
   301     }
   302     return id;
   303 }
   304 
   305 const char *
   306 SDL_GetThreadName(SDL_Thread * thread)
   307 {
   308     return thread->name;
   309 }
   310 
   311 int
   312 SDL_SetThreadPriority(SDL_ThreadPriority priority)
   313 {
   314     return SDL_SYS_SetThreadPriority(priority);
   315 }
   316 
   317 void
   318 SDL_WaitThread(SDL_Thread * thread, int *status)
   319 {
   320     if (thread) {
   321         SDL_SYS_WaitThread(thread);
   322         if (status) {
   323             *status = thread->status;
   324         }
   325         SDL_DelThread(thread);
   326         SDL_free(thread->name);
   327         SDL_free(thread);
   328     }
   329 }
   330 
   331 /* vi: set ts=4 sw=4 expandtab: */