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