* xmalloc.c (xmalloc): Fix spelling error.
[official-gcc.git] / libobjc / thr.c
blobf1c957aaa15c79a784e7926483d32ce6d25790e6
1 /* GNU Objective C Runtime Thread Interface
2 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3 Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
5 This file is part of GNU CC.
7 GNU CC is free software; you can redistribute it and/or modify it under the
8 terms of the GNU General Public License as published by the Free Software
9 Foundation; either version 2, or (at your option) any later version.
11 GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
12 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
14 details.
16 You should have received a copy of the GNU General Public License along with
17 GNU CC; see the file COPYING. If not, write to the Free Software
18 Foundation, 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
21 /* As a special exception, if you link this library with files compiled with
22 GCC to produce an executable, this does not cause the resulting executable
23 to be covered by the GNU General Public License. This exception does not
24 however invalidate any other reasons why the executable file might be
25 covered by the GNU General Public License. */
27 #include <stdlib.h>
28 #include "runtime.h"
30 /* Global exit status. */
31 int __objc_thread_exit_status = 0;
33 /* Flag which lets us know if we ever became multi threaded */
34 int __objc_is_multi_threaded = 0;
36 /* The hook function called when the runtime becomes multi threaded */
37 objc_thread_callback _objc_became_multi_threaded = NULL;
40 Use this to set the hook function that will be called when the
41 runtime initially becomes multi threaded.
42 The hook function is only called once, meaning only when the
43 2nd thread is spawned, not for each and every thread.
45 It returns the previous hook function or NULL if there is none.
47 A program outside of the runtime could set this to some function so
48 it can be informed; for example, the GNUstep Base Library sets it
49 so it can implement the NSBecomingMultiThreaded notification.
51 objc_thread_callback objc_set_thread_callback(objc_thread_callback func)
53 objc_thread_callback temp = _objc_became_multi_threaded;
54 _objc_became_multi_threaded = func;
55 return temp;
59 Private functions
61 These functions are utilized by the frontend, but they are not
62 considered part of the public interface.
66 First function called in a thread, starts everything else.
68 This function is passed to the backend by objc_thread_detach
69 as the starting function for a new thread.
71 struct __objc_thread_start_state
73 SEL selector;
74 id object;
75 id argument;
78 static volatile void
79 __objc_thread_detach_function(struct __objc_thread_start_state *istate)
81 /* Valid state? */
82 if (istate) {
83 id (*imp)(id,SEL,id);
84 SEL selector = istate->selector;
85 id object = istate->object;
86 id argument = istate->argument;
88 /* Don't need anymore so free it */
89 objc_free(istate);
91 /* Clear out the thread local storage */
92 objc_thread_set_data(NULL);
94 /* Check to see if we just became multi threaded */
95 if (!__objc_is_multi_threaded)
97 __objc_is_multi_threaded = 1;
99 /* Call the hook function */
100 if (_objc_became_multi_threaded != NULL)
101 (*_objc_became_multi_threaded)();
104 /* Call the method */
105 if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector)))
106 (*imp)(object, selector, argument);
107 else
108 objc_error(object, OBJC_ERR_UNIMPLEMENTED,
109 "objc_thread_detach called with bad selector.\n");
111 else
112 objc_error(nil, OBJC_ERR_BAD_STATE,
113 "objc_thread_detach called with NULL state.\n");
115 /* Exit the thread */
116 objc_thread_exit();
120 Frontend functions
122 These functions constitute the public interface to the Objective-C thread
123 and mutex functionality.
126 /* Frontend thread functions */
129 Detach a new thread of execution and return its id. Returns NULL if fails.
130 Thread is started by sending message with selector to object. Message
131 takes a single argument.
133 objc_thread_t
134 objc_thread_detach(SEL selector, id object, id argument)
136 struct __objc_thread_start_state *istate;
137 objc_thread_t thread_id = NULL;
139 /* Allocate the state structure */
140 if (!(istate = (struct __objc_thread_start_state *)
141 objc_malloc(sizeof(*istate))))
142 return NULL;
144 /* Initialize the state structure */
145 istate->selector = selector;
146 istate->object = object;
147 istate->argument = argument;
149 /* lock access */
150 objc_mutex_lock(__objc_runtime_mutex);
152 /* Call the backend to spawn the thread */
153 if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function,
154 istate)) == NULL)
156 /* failed! */
157 objc_mutex_unlock(__objc_runtime_mutex);
158 objc_free(istate);
159 return NULL;
162 /* Increment our thread counter */
163 __objc_runtime_threads_alive++;
164 objc_mutex_unlock(__objc_runtime_mutex);
166 return thread_id;
169 /* Set the current thread's priority. */
171 objc_thread_set_priority(int priority)
173 /* Call the backend */
174 return __objc_thread_set_priority(priority);
177 /* Return the current thread's priority. */
179 objc_thread_get_priority(void)
181 /* Call the backend */
182 return __objc_thread_get_priority();
186 Yield our process time to another thread. Any BUSY waiting that is done
187 by a thread should use this function to make sure that other threads can
188 make progress even on a lazy uniprocessor system.
190 void
191 objc_thread_yield(void)
193 /* Call the backend */
194 __objc_thread_yield();
198 Terminate the current tread. Doesn't return.
199 Actually, if it failed returns -1.
202 objc_thread_exit(void)
204 /* Decrement our counter of the number of threads alive */
205 objc_mutex_lock(__objc_runtime_mutex);
206 __objc_runtime_threads_alive--;
207 objc_mutex_unlock(__objc_runtime_mutex);
209 /* Call the backend to terminate the thread */
210 return __objc_thread_exit();
214 Returns an integer value which uniquely describes a thread. Must not be
215 NULL which is reserved as a marker for "no thread".
217 objc_thread_t
218 objc_thread_id(void)
220 /* Call the backend */
221 return __objc_thread_id();
225 Sets the thread's local storage pointer.
226 Returns 0 if successful or -1 if failed.
229 objc_thread_set_data(void *value)
231 /* Call the backend */
232 return __objc_thread_set_data(value);
236 Returns the thread's local storage pointer. Returns NULL on failure.
238 void *
239 objc_thread_get_data(void)
241 /* Call the backend */
242 return __objc_thread_get_data();
245 /* Frontend mutex functions */
248 Allocate a mutex. Return the mutex pointer if successful or NULL if the
249 allocation failed for any reason.
251 objc_mutex_t
252 objc_mutex_allocate(void)
254 objc_mutex_t mutex;
256 /* Allocate the mutex structure */
257 if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
258 return NULL;
260 /* Call backend to create the mutex */
261 if (__objc_mutex_allocate(mutex))
263 /* failed! */
264 objc_free(mutex);
265 return NULL;
268 /* Initialize mutex */
269 mutex->owner = NULL;
270 mutex->depth = 0;
271 return mutex;
275 Deallocate a mutex. Note that this includes an implicit mutex_lock to
276 insure that no one else is using the lock. It is legal to deallocate
277 a lock if we have a lock on it, but illegal to deallocate a lock held
278 by anyone else.
279 Returns the number of locks on the thread. (1 for deallocate).
282 objc_mutex_deallocate(objc_mutex_t mutex)
284 int depth;
286 /* Valid mutex? */
287 if (!mutex)
288 return -1;
290 /* Acquire lock on mutex */
291 depth = objc_mutex_lock(mutex);
293 /* Call backend to destroy mutex */
294 if (__objc_mutex_deallocate(mutex))
295 return -1;
297 /* Free the mutex structure */
298 objc_free(mutex);
300 /* Return last depth */
301 return depth;
305 Grab a lock on a mutex. If this thread already has a lock on this mutex
306 then we increment the lock count. If another thread has a lock on the
307 mutex we block and wait for the thread to release the lock.
308 Returns the lock count on the mutex held by this thread.
311 objc_mutex_lock(objc_mutex_t mutex)
313 objc_thread_t thread_id;
314 int status;
316 /* Valid mutex? */
317 if (!mutex)
318 return -1;
320 /* If we already own the lock then increment depth */
321 thread_id = objc_thread_id();
322 if (mutex->owner == thread_id)
323 return ++mutex->depth;
325 /* Call the backend to lock the mutex */
326 status = __objc_mutex_lock(mutex);
328 /* Failed? */
329 if (status)
330 return status;
332 /* Successfully locked the thread */
333 mutex->owner = thread_id;
334 return mutex->depth = 1;
338 Try to grab a lock on a mutex. If this thread already has a lock on
339 this mutex then we increment the lock count and return it. If another
340 thread has a lock on the mutex returns -1.
343 objc_mutex_trylock(objc_mutex_t mutex)
345 objc_thread_t thread_id;
346 int status;
348 /* Valid mutex? */
349 if (!mutex)
350 return -1;
352 /* If we already own the lock then increment depth */
353 thread_id = objc_thread_id();
354 if (mutex->owner == thread_id)
355 return ++mutex->depth;
357 /* Call the backend to try to lock the mutex */
358 status = __objc_mutex_trylock(mutex);
360 /* Failed? */
361 if (status)
362 return status;
364 /* Successfully locked the thread */
365 mutex->owner = thread_id;
366 return mutex->depth = 1;
370 Unlocks the mutex by one level.
371 Decrements the lock count on this mutex by one.
372 If the lock count reaches zero, release the lock on the mutex.
373 Returns the lock count on the mutex.
374 It is an error to attempt to unlock a mutex which this thread
375 doesn't hold in which case return -1 and the mutex is unaffected.
378 objc_mutex_unlock(objc_mutex_t mutex)
380 objc_thread_t thread_id;
381 int status;
383 /* Valid mutex? */
384 if (!mutex)
385 return -1;
387 /* If another thread owns the lock then abort */
388 thread_id = objc_thread_id();
389 if (mutex->owner != thread_id)
390 return -1;
392 /* Decrement depth and return */
393 if (mutex->depth > 1)
394 return --mutex->depth;
396 /* Depth down to zero so we are no longer the owner */
397 mutex->depth = 0;
398 mutex->owner = NULL;
400 /* Have the backend unlock the mutex */
401 status = __objc_mutex_unlock(mutex);
403 /* Failed? */
404 if (status)
405 return status;
407 return 0;
410 /* Frontend condition mutex functions */
413 Allocate a condition. Return the condition pointer if successful or NULL
414 if the allocation failed for any reason.
416 objc_condition_t
417 objc_condition_allocate(void)
419 objc_condition_t condition;
421 /* Allocate the condition mutex structure */
422 if (!(condition =
423 (objc_condition_t)objc_malloc(sizeof(struct objc_condition))))
424 return NULL;
426 /* Call the backend to create the condition mutex */
427 if (__objc_condition_allocate(condition))
429 /* failed! */
430 objc_free(condition);
431 return NULL;
434 /* Success! */
435 return condition;
439 Deallocate a condition. Note that this includes an implicit
440 condition_broadcast to insure that waiting threads have the opportunity
441 to wake. It is legal to dealloc a condition only if no other
442 thread is/will be using it. Here we do NOT check for other threads
443 waiting but just wake them up.
446 objc_condition_deallocate(objc_condition_t condition)
448 /* Broadcast the condition */
449 if (objc_condition_broadcast(condition))
450 return -1;
452 /* Call the backend to destroy */
453 if (__objc_condition_deallocate(condition))
454 return -1;
456 /* Free the condition mutex structure */
457 objc_free(condition);
459 return 0;
463 Wait on the condition unlocking the mutex until objc_condition_signal()
464 or objc_condition_broadcast() are called for the same condition. The
465 given mutex *must* have the depth set to 1 so that it can be unlocked
466 here, so that someone else can lock it and signal/broadcast the condition.
467 The mutex is used to lock access to the shared data that make up the
468 "condition" predicate.
471 objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
473 objc_thread_t thread_id;
475 /* Valid arguments? */
476 if (!mutex || !condition)
477 return -1;
479 /* Make sure we are owner of mutex */
480 thread_id = objc_thread_id();
481 if (mutex->owner != thread_id)
482 return -1;
484 /* Cannot be locked more than once */
485 if (mutex->depth > 1)
486 return -1;
488 /* Virtually unlock the mutex */
489 mutex->depth = 0;
490 mutex->owner = (objc_thread_t)NULL;
492 /* Call the backend to wait */
493 __objc_condition_wait(condition, mutex);
495 /* Make ourselves owner of the mutex */
496 mutex->owner = thread_id;
497 mutex->depth = 1;
499 return 0;
503 Wake up all threads waiting on this condition. It is recommended that
504 the called would lock the same mutex as the threads in objc_condition_wait
505 before changing the "condition predicate" and make this call and unlock it
506 right away after this call.
509 objc_condition_broadcast(objc_condition_t condition)
511 /* Valid condition mutex? */
512 if (!condition)
513 return -1;
515 return __objc_condition_broadcast(condition);
519 Wake up one thread waiting on this condition. It is recommended that
520 the called would lock the same mutex as the threads in objc_condition_wait
521 before changing the "condition predicate" and make this call and unlock it
522 right away after this call.
525 objc_condition_signal(objc_condition_t condition)
527 /* Valid condition mutex? */
528 if (!condition)
529 return -1;
531 return __objc_condition_signal(condition);
534 /* End of File */