src/thread/SDL_thread.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 21 Feb 2006 08:46:50 +0000
changeset 1402 d910939febfa
parent 1358 c71e05b4dc2e
child 1471 9fb0eee04dd9
permissions -rw-r--r--
Use consistent identifiers for the various platforms we support.
Make sure every source file includes SDL_config.h, so the proper system
headers are chosen.
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2006 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 int _creating_thread_lock = 0;
    41 
    42 int SDL_ThreadsInit(void)
    43 {
    44 	int retval;
    45 
    46 	retval = 0;
    47 	/* Set the thread lock creation flag so that we can reuse an
    48 	   existing lock on the system - since this mutex never gets
    49 	   destroyed (see SDL_ThreadsQuit()), we want to reuse it.
    50 	*/
    51 	_creating_thread_lock = 1;
    52 	thread_lock = SDL_CreateMutex();
    53 	_creating_thread_lock = 0;
    54 	if ( thread_lock == NULL ) {
    55 		retval = -1;
    56 	}
    57 	return(retval);
    58 }
    59 
    60 /* This should never be called...
    61    If this is called by SDL_Quit(), we don't know whether or not we should
    62    clean up threads here.  If any threads are still running after this call,
    63    they will no longer have access to any per-thread data.
    64  */
    65 void SDL_ThreadsQuit()
    66 {
    67 	SDL_mutex *mutex;
    68 
    69 	mutex = thread_lock;
    70 	thread_lock = NULL;
    71 	if ( mutex != NULL ) {
    72 		SDL_DestroyMutex(mutex);
    73 	}
    74 }
    75 
    76 /* Routines for manipulating the thread list */
    77 static void SDL_AddThread(SDL_Thread *thread)
    78 {
    79 	SDL_Thread **threads;
    80 
    81 	/* WARNING:
    82 	   If the very first threads are created simultaneously, then
    83 	   there could be a race condition causing memory corruption.
    84 	   In practice, this isn't a problem because by definition there
    85 	   is only one thread running the first time this is called.
    86 	*/
    87 	if ( thread_lock == NULL ) {
    88 		if ( SDL_ThreadsInit() < 0 ) {
    89 			return;
    90 		}
    91 	}
    92 	SDL_mutexP(thread_lock);
    93 
    94 	/* Expand the list of threads, if necessary */
    95 #ifdef DEBUG_THREADS
    96 	printf("Adding thread (%d already - %d max)\n",
    97 			SDL_numthreads, SDL_maxthreads);
    98 #endif
    99 	if ( SDL_numthreads == SDL_maxthreads ) {
   100 		threads=(SDL_Thread **)SDL_malloc((SDL_maxthreads+ARRAY_CHUNKSIZE)*
   101 		                              (sizeof *threads));
   102 		if ( threads == NULL ) {
   103 			SDL_OutOfMemory();
   104 			goto done;
   105 		}
   106 		SDL_memcpy(threads, SDL_Threads, SDL_numthreads*(sizeof *threads));
   107 		SDL_maxthreads += ARRAY_CHUNKSIZE;
   108 		if ( SDL_Threads ) {
   109 			SDL_free(SDL_Threads);
   110 		}
   111 		SDL_Threads = threads;
   112 	}
   113 	SDL_Threads[SDL_numthreads++] = thread;
   114 done:
   115 	SDL_mutexV(thread_lock);
   116 }
   117 
   118 static void SDL_DelThread(SDL_Thread *thread)
   119 {
   120 	int i;
   121 
   122 	if ( thread_lock ) {
   123 		SDL_mutexP(thread_lock);
   124 		for ( i=0; i<SDL_numthreads; ++i ) {
   125 			if ( thread == SDL_Threads[i] ) {
   126 				break;
   127 			}
   128 		}
   129 		if ( i < SDL_numthreads ) {
   130 			if ( --SDL_numthreads > 0 ) {
   131 				while ( i < SDL_numthreads ) {
   132 					SDL_Threads[i] = SDL_Threads[i+1];
   133 					++i;
   134 				}
   135 			} else {
   136 				SDL_maxthreads = 0;
   137 				SDL_free(SDL_Threads);
   138 				SDL_Threads = NULL;
   139 			}
   140 #ifdef DEBUG_THREADS
   141 			printf("Deleting thread (%d left - %d max)\n",
   142 					SDL_numthreads, SDL_maxthreads);
   143 #endif
   144 		}
   145 		SDL_mutexV(thread_lock);
   146 	}
   147 }
   148 
   149 /* The default (non-thread-safe) global error variable */
   150 static SDL_error SDL_global_error;
   151 
   152 /* Routine to get the thread-specific error variable */
   153 SDL_error *SDL_GetErrBuf(void)
   154 {
   155 	SDL_error *errbuf;
   156 
   157 	errbuf = &SDL_global_error;
   158 	if ( SDL_Threads ) {
   159 		int i;
   160 		Uint32 this_thread;
   161 
   162 		this_thread = SDL_ThreadID();
   163 		SDL_mutexP(thread_lock);
   164 		for ( i=0; i<SDL_numthreads; ++i ) {
   165 			if ( this_thread == SDL_Threads[i]->threadid ) {
   166 				errbuf = &SDL_Threads[i]->errbuf;
   167 				break;
   168 			}
   169 		}
   170 		SDL_mutexV(thread_lock);
   171 	}
   172 	return(errbuf);
   173 }
   174 
   175 
   176 /* Arguments and callback to setup and run the user thread function */
   177 typedef struct {
   178 	int (*func)(void *);
   179 	void *data;
   180 	SDL_Thread *info;
   181 	SDL_sem *wait;
   182 } thread_args;
   183 
   184 void SDL_RunThread(void *data)
   185 {
   186 	thread_args *args;
   187 	int (*userfunc)(void *);
   188 	void *userdata;
   189 	int *statusloc;
   190 
   191 	/* Perform any system-dependent setup
   192 	   - this function cannot fail, and cannot use SDL_SetError()
   193 	 */
   194 	SDL_SYS_SetupThread();
   195 
   196 	/* Get the thread id */
   197 	args = (thread_args *)data;
   198 	args->info->threadid = SDL_ThreadID();
   199 
   200 	/* Figure out what function to run */
   201 	userfunc = args->func;
   202 	userdata = args->data;
   203 	statusloc = &args->info->status;
   204 
   205 	/* Wake up the parent thread */
   206 	SDL_SemPost(args->wait);
   207 
   208 	/* Run the function */
   209 	*statusloc = userfunc(userdata);
   210 }
   211 
   212 #if defined(__WIN32__) || defined(__OS2__)
   213 #undef SDL_CreateThread
   214 DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (*fn)(void *), void *data, pfnSDL_CurrentBeginThread pfnBeginThread, pfnSDL_CurrentEndThread pfnEndThread)
   215 #else
   216 DECLSPEC SDL_Thread * SDLCALL SDL_CreateThread(int (*fn)(void *), void *data)
   217 #endif
   218 {
   219 	SDL_Thread *thread;
   220 	thread_args *args;
   221 	int ret;
   222 
   223 	/* Allocate memory for the thread info structure */
   224 	thread = (SDL_Thread *)SDL_malloc(sizeof(*thread));
   225 	if ( thread == NULL ) {
   226 		SDL_OutOfMemory();
   227 		return(NULL);
   228 	}
   229 	SDL_memset(thread, 0, (sizeof *thread));
   230 	thread->status = -1;
   231 
   232 	/* Set up the arguments for the thread */
   233 	args = (thread_args *)SDL_malloc(sizeof(*args));
   234 	if ( args == NULL ) {
   235 		SDL_OutOfMemory();
   236 		SDL_free(thread);
   237 		return(NULL);
   238 	}
   239 	args->func = fn;
   240 	args->data = data;
   241 	args->info = thread;
   242 	args->wait = SDL_CreateSemaphore(0);
   243 	if ( args->wait == NULL ) {
   244 		SDL_free(thread);
   245 		SDL_free(args);
   246 		return(NULL);
   247 	}
   248 
   249 	/* Add the thread to the list of available threads */
   250 	SDL_AddThread(thread);
   251 
   252 	/* Create the thread and go! */
   253 #if defined(__WIN32__) || defined(__OS2__)
   254 	ret = SDL_SYS_CreateThread(thread, args, pfnBeginThread, pfnEndThread);
   255 #else
   256 	ret = SDL_SYS_CreateThread(thread, args);
   257 #endif
   258 	if ( ret >= 0 ) {
   259 		/* Wait for the thread function to use arguments */
   260 		SDL_SemWait(args->wait);
   261 	} else {
   262 		/* Oops, failed.  Gotta free everything */
   263 		SDL_DelThread(thread);
   264 		SDL_free(thread);
   265 		thread = NULL;
   266 	}
   267 	SDL_DestroySemaphore(args->wait);
   268 	SDL_free(args);
   269 
   270 	/* Everything is running now */
   271 	return(thread);
   272 }
   273 
   274 void SDL_WaitThread(SDL_Thread *thread, int *status)
   275 {
   276 	if ( thread ) {
   277 		SDL_SYS_WaitThread(thread);
   278 		if ( status ) {
   279 			*status = thread->status;
   280 		}
   281 		SDL_DelThread(thread);
   282 		SDL_free(thread);
   283 	}
   284 }
   285 
   286 Uint32 SDL_GetThreadID(SDL_Thread *thread)
   287 {
   288 	Uint32 id;
   289 
   290 	if ( thread ) {
   291 		id = thread->threadid;
   292 	} else {
   293 		id = SDL_ThreadID();
   294 	}
   295 	return(id);
   296 }
   297 
   298 void SDL_KillThread(SDL_Thread *thread)
   299 {
   300 	if ( thread ) {
   301 		SDL_SYS_KillThread(thread);
   302 		SDL_WaitThread(thread, NULL);
   303 	}
   304 }
   305