src/thread/SDL_thread.c
author Sam Lantinga
Fri, 08 Apr 2011 13:03:26 -0700
changeset 5535 96594ac5fd1a
parent 5509 5b1b4d820d10
child 5969 3a041d215edc
permissions -rw-r--r--
SDL 1.3 is now under the zlib license.
     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;
   192     int (SDLCALL * userfunc) (void *);
   193     void *userdata;
   194     int *statusloc;
   195 
   196     /* Perform any system-dependent setup
   197        - this function cannot fail, and cannot use SDL_SetError()
   198      */
   199     SDL_SYS_SetupThread();
   200 
   201     /* Get the thread id */
   202     args = (thread_args *) data;
   203     args->info->threadid = SDL_ThreadID();
   204 
   205     /* Figure out what function to run */
   206     userfunc = args->func;
   207     userdata = args->data;
   208     statusloc = &args->info->status;
   209 
   210     /* Wake up the parent thread */
   211     SDL_SemPost(args->wait);
   212 
   213     /* Run the function */
   214     *statusloc = userfunc(userdata);
   215 }
   216 
   217 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   218 #undef SDL_CreateThread
   219 DECLSPEC SDL_Thread *SDLCALL
   220 SDL_CreateThread(int (SDLCALL * fn) (void *), void *data,
   221                  pfnSDL_CurrentBeginThread pfnBeginThread,
   222                  pfnSDL_CurrentEndThread pfnEndThread)
   223 #else
   224 DECLSPEC SDL_Thread *SDLCALL
   225 SDL_CreateThread(int (SDLCALL * fn) (void *), void *data)
   226 #endif
   227 {
   228     SDL_Thread *thread;
   229     thread_args *args;
   230     int ret;
   231 
   232     /* Allocate memory for the thread info structure */
   233     thread = (SDL_Thread *) SDL_malloc(sizeof(*thread));
   234     if (thread == NULL) {
   235         SDL_OutOfMemory();
   236         return (NULL);
   237     }
   238     SDL_memset(thread, 0, (sizeof *thread));
   239     thread->status = -1;
   240 
   241     /* Set up the arguments for the thread */
   242     args = (thread_args *) SDL_malloc(sizeof(*args));
   243     if (args == NULL) {
   244         SDL_OutOfMemory();
   245         SDL_free(thread);
   246         return (NULL);
   247     }
   248     args->func = fn;
   249     args->data = data;
   250     args->info = thread;
   251     args->wait = SDL_CreateSemaphore(0);
   252     if (args->wait == NULL) {
   253         SDL_free(thread);
   254         SDL_free(args);
   255         return (NULL);
   256     }
   257 
   258     /* Add the thread to the list of available threads */
   259     SDL_AddThread(thread);
   260 
   261     /* Create the thread and go! */
   262 #ifdef SDL_PASSED_BEGINTHREAD_ENDTHREAD
   263     ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   264 #else
   265     ret = SDL_SYS_CreateThread(thread, args);
   266 #endif
   267     if (ret >= 0) {
   268         /* Wait for the thread function to use arguments */
   269         SDL_SemWait(args->wait);
   270     } else {
   271         /* Oops, failed.  Gotta free everything */
   272         SDL_DelThread(thread);
   273         SDL_free(thread);
   274         thread = NULL;
   275     }
   276     SDL_DestroySemaphore(args->wait);
   277     SDL_free(args);
   278 
   279     /* Everything is running now */
   280     return (thread);
   281 }
   282 
   283 SDL_threadID
   284 SDL_GetThreadID(SDL_Thread * thread)
   285 {
   286     SDL_threadID id;
   287 
   288     if (thread) {
   289         id = thread->threadid;
   290     } else {
   291         id = SDL_ThreadID();
   292     }
   293     return id;
   294 }
   295 
   296 int
   297 SDL_SetThreadPriority(SDL_ThreadPriority priority)
   298 {
   299     return SDL_SYS_SetThreadPriority(priority);
   300 }
   301 
   302 void
   303 SDL_WaitThread(SDL_Thread * thread, int *status)
   304 {
   305     if (thread) {
   306         SDL_SYS_WaitThread(thread);
   307         if (status) {
   308             *status = thread->status;
   309         }
   310         SDL_DelThread(thread);
   311         SDL_free(thread);
   312     }
   313 }
   314 
   315 /* vi: set ts=4 sw=4 expandtab: */