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