1 /* GNU Objective C Runtime Thread Implementation
2 Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3 Contributed by Galen C. Hunt (gchunt@cs.rochester.edu)
4 Modified for Mach threads by Bill Bumgarner <bbum@friday.com>
5 Condition functions added by Mircea Oancea <mircea@first.elcom.pub.ro>
7 This file is part of GNU CC.
9 GNU CC is free software; you can redistribute it and/or modify it under the
10 terms of the GNU General Public License as published by the Free Software
11 Foundation; either version 2, or (at your option) any later version.
13 GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
18 You should have received a copy of the GNU General Public License
19 along with GNU CC; see the file COPYING. If not, write to
20 the Free Software Foundation, 59 Temple Place - Suite 330,
21 Boston, MA 02111-1307, USA. */
23 /* As a special exception, if you link this library with files compiled with
24 GCC to produce an executable, this does not cause the resulting executable
25 to be covered by the GNU General Public License. This exception does not
26 however invalidate any other reasons why the executable file might be
27 covered by the GNU General Public License. */
29 #include <mach/mach.h>
30 #include <mach/cthreads.h>
35 * This structure represents a single mutual exclusion lock. Lock semantics
36 * are detailed with the subsequent functions. We use whatever lock is
37 * provided by the system. We augment it with depth and current owner id
38 * fields to implement and re-entrant lock.
42 volatile objc_thread_t owner; /* Id of thread that owns. */
43 volatile int depth; /* # of acquires. */
44 struct mutex lock; /* cthread mutex */
49 struct condition condition; /* cthread condition */
53 * obtain the maximum thread priority that can set for t. Under the
54 * mach threading model, it is possible for the developer to adjust the
55 * maximum priority downward only-- cannot be raised without superuser
56 * priviledges. Once lowered, it cannot be raised.
58 static int __mach_get_max_thread_priority(cthread_t t, int *base) {
61 struct thread_sched_info info;
62 unsigned int info_count=THREAD_SCHED_INFO_COUNT;
67 threadP = cthread_thread(t); /* get thread underlying */
69 error=thread_info(threadP, THREAD_SCHED_INFO,
70 (thread_info_t)&info, &info_count);
72 if (error != KERN_SUCCESS)
76 *base = info.base_priority;
78 return info.max_priority;
82 * Initialize the threads subsystem. Returns 0 if successful, or -1 if no
83 * thread support is available.
86 __objc_init_thread_system(void)
88 DEBUG_PRINTF("__objc_init_thread_system\n");
89 return 0; /* Succeeded. */
94 __objc_fini_thread_system(void)
100 * Create a new thread of execution and return its id. Return NULL if fails.
101 * The new thread starts in "func" with the given argument.
104 objc_thread_create(void (*func)(void *arg), void *arg)
106 objc_thread_t thread_id = NULL; /* Detached thread id. */
107 cthread_t new_thread_handle; /* cthread handle. */
109 objc_mutex_lock(__objc_runtime_mutex);
112 new_thread_handle = cthread_fork((cthread_fn_t)func, arg);
114 if(new_thread_handle) {
115 /* this is not terribly portable */
116 thread_id = *(objc_thread_t *)&new_thread_handle;
117 cthread_detach(new_thread_handle); /* fully detach thread */
118 __objc_runtime_threads_alive++; /* increment thread count */
121 objc_mutex_unlock(__objc_runtime_mutex);
126 * Set the current thread's priority.
129 objc_thread_set_priority(int priority)
131 objc_thread_t *t = objc_thread_id();
132 cthread_t cT = (cthread_t) t;
133 int maxPriority = __mach_get_max_thread_priority(cT, NULL);
134 int sys_priority = 0;
136 if (maxPriority == -1)
140 case OBJC_THREAD_INTERACTIVE_PRIORITY:
141 sys_priority = maxPriority;
143 case OBJC_THREAD_BACKGROUND_PRIORITY:
144 sys_priority = (maxPriority * 2) / 3;
146 case OBJC_THREAD_LOW_PRIORITY:
147 sys_priority = maxPriority / 3;
153 if (sys_priority == 0)
156 if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS)
157 return 0; /* Changed priority. End. */
159 return -1; /* Failed. */
163 * Return the current thread's priority [well, whatever it is closest to].
166 objc_thread_get_priority(void)
168 objc_thread_t *t = objc_thread_id();
169 cthread_t cT = (cthread_t) t; /* see objc_thread_id() */
172 int sys_priority = 0;
174 int interactiveT, backgroundT, lowT; /* threasholds */
176 maxPriority = __mach_get_max_thread_priority(cT, &basePriority);
178 if(maxPriority == -1)
181 if (basePriority > ( (maxPriority * 2) / 3))
182 return OBJC_THREAD_INTERACTIVE_PRIORITY; /* interactive priority
184 if (basePriority > ( maxPriority / 3))
185 return OBJC_THREAD_BACKGROUND_PRIORITY; /* background priority
187 return OBJC_THREAD_LOW_PRIORITY; /* everything else is low */
191 * Yield our process time to another thread. Any BUSY waiting that is done
192 * by a thread should use this function to make sure that other threads can
193 * make progress even on a lazy uniprocessor system.
196 objc_thread_yield(void)
198 cthread_yield(); /* Yield to equal thread. */
202 * Terminate the current tread. Doesn't return anything. Doesn't return.
203 * Actually, if it failed returns -1.
206 objc_thread_exit(void)
208 objc_mutex_lock(__objc_runtime_mutex);
209 __objc_runtime_threads_alive--;
210 objc_mutex_unlock(__objc_runtime_mutex);
212 cthread_exit(&__objc_thread_exit_status); /* Terminate thread. */
217 * Returns an integer value which uniquely describes a thread. Must not be
218 * NULL which is reserved as a marker for "no thread".
223 cthread_t self = cthread_self();
224 return (objc_thread_t)self;
228 * Sets the thread's local storage pointer. Returns 0 if successful or -1
233 objc_thread_set_data(void *value)
235 cthread_set_data(cthread_self(), (any_t) value);
240 * Returns the thread's local storage pointer. Returns NULL on failure.
243 objc_thread_get_data(void)
245 return (void *) cthread_data(cthread_self());
249 * Allocate a mutex. Return the mutex pointer if successful or NULL if the
250 * allocation failed for any reason.
253 objc_mutex_allocate(void)
258 if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
259 return NULL; /* Abort if malloc failed. */
261 err = mutex_init(&(mutex->lock));
263 if (err != 0) { /* System init failed? */
264 objc_free(mutex); /* Yes, free local memory. */
265 return NULL; /* Abort. */
267 mutex->owner = (objc_thread_t) -1; /* No owner. */
268 mutex->depth = 0; /* No locks. */
269 return mutex; /* Return mutex handle. */
273 * Deallocate a mutex. Note that this includes an implicit mutex_lock to
274 * insure that no one else is using the lock. It is legal to deallocate
275 * a lock if we have a lock on it, but illegal to deallocate a lock held
277 * Returns the number of locks on the thread. (1 for deallocate).
280 objc_mutex_deallocate(objc_mutex_t mutex)
282 int depth; /* # of locks on mutex. */
284 if (!mutex) /* Is argument bad? */
285 return -1; /* Yes, abort. */
286 depth = objc_mutex_lock(mutex); /* Must have lock. */
288 mutex_unlock(&(mutex->lock)); /* Must unlock system mutex.*/
289 mutex_clear(&(mutex->lock)); /* Free system mutex. */
291 objc_free(mutex); /* Free memory. */
292 return depth; /* Return last depth. */
296 * Grab a lock on a mutex. If this thread already has a lock on this mutex
297 * then we increment the lock count. If another thread has a lock on the
298 * mutex we block and wait for the thread to release the lock.
299 * Returns the lock count on the mutex held by this thread.
302 objc_mutex_lock(objc_mutex_t mutex)
304 objc_thread_t thread_id; /* Cache our thread id. */
306 if (!mutex) /* Is argument bad? */
307 return -1; /* Yes, abort. */
308 thread_id = objc_thread_id(); /* Get this thread's id. */
309 if (mutex->owner == thread_id) /* Already own lock? */
310 return ++mutex->depth; /* Yes, increment depth. */
312 mutex_lock(&(mutex->lock)); /* Lock cthread mutex. */
314 mutex->owner = thread_id; /* Mark thread as owner. */
315 return mutex->depth = 1; /* Increment depth to end. */
319 * Try to grab a lock on a mutex. If this thread already has a lock on
320 * this mutex then we increment the lock count and return it. If another
321 * thread has a lock on the mutex returns -1.
324 objc_mutex_trylock(objc_mutex_t mutex)
326 objc_thread_t thread_id; /* Cache our thread id. */
328 if (!mutex) /* Is argument bad? */
329 return -1; /* Yes, abort. */
330 thread_id = objc_thread_id(); /* Get this thread's id. */
331 if (mutex->owner == thread_id) /* Already own lock? */
332 return ++mutex->depth; /* Yes, increment depth. */
334 if (mutex_try_lock(&(mutex->lock)) == 0) /* Lock cthread mutex. */
335 return -1; /* Failed, abort. */
337 mutex->owner = thread_id; /* Mark thread as owner. */
338 return mutex->depth = 1; /* Increment depth to end. */
342 * Decrements the lock count on this mutex by one. If the lock count reaches
343 * zero, release the lock on the mutex. Returns the lock count on the mutex.
344 * It is an error to attempt to unlock a mutex which this thread doesn't hold
345 * in which case return -1 and the mutex is unaffected.
346 * Will also return -1 if the mutex free fails.
349 objc_mutex_unlock(objc_mutex_t mutex)
351 objc_thread_t thread_id; /* Cache our thread id. */
353 if (!mutex) /* Is argument bad? */
354 return -1; /* Yes, abort. */
355 thread_id = objc_thread_id(); /* Get this thread's id. */
356 if (mutex->owner != thread_id) /* Does some else own lock? */
357 return -1; /* Yes, abort. */
358 if (mutex->depth > 1) /* Released last lock? */
359 return --mutex->depth; /* No, Decrement depth, end.*/
360 mutex->depth = 0; /* Yes, reset depth to 0. */
361 mutex->owner = (objc_thread_t) -1; /* Set owner to "no thread".*/
363 mutex_unlock(&(mutex->lock)); /* unlock cthread mutex. */
365 return 0; /* No, return success. */
369 * Allocate a condition. Return the condition pointer if successful or NULL
370 * if the allocation failed for any reason.
373 objc_condition_allocate(void)
375 objc_condition_t condition;
377 if (!(condition = (objc_condition_t)objc_malloc(
378 sizeof(struct objc_condition))))
379 return NULL; /* Abort if malloc failed. */
381 condition_init(&(condition->condition));
383 return condition; /* Return mutex handle. */
387 * Deallocate a condition. Note that this includes an implicit
388 * condition_broadcast to insure that waiting threads have the opportunity
389 * to wake. It is legal to dealloc a condition only if no other
390 * thread is/will be using it. Here we do NOT check for other threads
391 * waiting but just wake them up.
394 objc_condition_deallocate(objc_condition_t condition)
396 condition_broadcast(&(condition->condition));
397 condition_clear(&(condition->condition));
398 objc_free(condition);
403 * Wait on the condition unlocking the mutex until objc_condition_signal()
404 * or objc_condition_broadcast() are called for the same condition. The
405 * given mutex *must* have the depth set to 1 so that it can be unlocked
406 * here, so that someone else can lock it and signal/broadcast the condition.
407 * The mutex is used to lock access to the shared data that make up the
408 * "condition" predicate.
411 objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
413 objc_thread_t thread_id; /* Cache our thread id. */
415 if (!mutex || !condition) /* Is argument bad? */
416 return -1; /* Yes, abort. */
418 thread_id = objc_thread_id(); /* Get this thread's id. */
419 if (mutex->owner != thread_id) /* Does some else own lock? */
420 return -1; /* Yes, abort. */
421 if (mutex->depth > 1) /* Locked more than once ? */
422 return -1; /* YES, return error */
423 /* mutex will be unlocked */
424 mutex->depth = 0; /* Yes, reset depth to 0. */
425 mutex->owner = (objc_thread_t) -1; /* Set owner to "no thread".*/
427 condition_wait(&(condition->condition),
428 &(mutex->lock)); /* unlock, wait ..., lock */
430 mutex->owner = thread_id; /* Mark thread as owner. */
431 mutex->depth = 1; /* Increment depth to end. */
432 return 0; /* Return success. */
436 * Wake up all threads waiting on this condition. It is recommended that
437 * the called would lock the same mutex as the threads in objc_condition_wait
438 * before changing the "condition predicate" and make this call and unlock it
439 * right away after this call.
442 objc_condition_broadcast(objc_condition_t condition)
446 condition_broadcast(&(condition->condition));
451 * Wake up one thread waiting on this condition. It is recommended that
452 * the called would lock the same mutex as the threads in objc_condition_wait
453 * before changing the "condition predicate" and make this call and unlock it
454 * right away after this call.
457 objc_condition_signal(objc_condition_t condition)
461 condition_signal(&(condition->condition));