src/thread/SDL_thread.c
author Sam Lantinga
Fri, 25 Mar 2011 12:44:06 -0700
changeset 5509 5b1b4d820d10
parent 5506 82a09d5d0f07
child 5535 96594ac5fd1a
permissions -rw-r--r--
The API sets the priority for the current thread, not an arbitrary thread.

Implemented thread priority as the 'nice' value on Linux. High priority threads require root permissions (you shouldn't give your game root permissions though!)
     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_ThreadPriority priority)
   299 {
   300     return SDL_SYS_SetThreadPriority(priority);
   301 }
   302 
   303 void
   304 SDL_WaitThread(SDL_Thread * thread, int *status)
   305 {
   306     if (thread) {
   307         SDL_SYS_WaitThread(thread);
   308         if (status) {
   309             *status = thread->status;
   310         }
   311         SDL_DelThread(thread);
   312         SDL_free(thread);
   313     }
   314 }
   315 
   316 /* vi: set ts=4 sw=4 expandtab: */