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