1 /* GNU Objective C Runtime Thread Implementation
2 Copyright (C) 1996 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>
6 This file is part of GNU CC.
8 GNU CC is free software; you can redistribute it and/or modify it under the
9 terms of the GNU General Public License as published by the Free Software
10 Foundation; either version 2, or (at your option) any later version.
12 GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
13 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
14 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
17 You should have received a copy of the GNU General Public License
18 along with GNU CC; see the file COPYING. If not, write to
19 the Free Software Foundation, 59 Temple Place - Suite 330,
20 Boston, MA 02111-1307, USA. */
22 /* As a special exception, if you link this library with files compiled with
23 GCC to produce an executable, this does not cause the resulting executable
24 to be covered by the GNU General Public License. This exception does not
25 however invalidate any other reasons why the executable file might be
26 covered by the GNU General Public License. */
28 #include <mach/mach.h>
29 #include <mach/cthreads.h>
34 * This structure represents a single mutual exclusion lock. Lock semantics
35 * are detailed with the subsequent functions. We use whatever lock is
36 * provided by the system. We augment it with depth and current owner id
37 * fields to implement and re-entrant lock.
41 volatile _objc_thread_t owner; /* Id of thread that owns. */
42 volatile int depth; /* # of acquires. */
43 struct mutex lock; /* cthread mutex */
48 * obtain the maximum thread priority that can set for t. Under the
49 * mach threading model, it is possible for the developer to adjust the
50 * maximum priority downward only-- cannot be raised without superuser
51 * priviledges. Once lowered, it cannot be raised.
53 static int __mach_get_max_thread_priority(cthread_t t, int *base) {
56 struct thread_sched_info info;
57 unsigned int info_count=THREAD_SCHED_INFO_COUNT;
62 threadP = cthread_thread(t); /* get thread underlying */
64 error=thread_info(threadP, THREAD_SCHED_INFO,
65 (thread_info_t)&info, &info_count);
67 if (error != KERN_SUCCESS)
71 *base = info.base_priority;
73 return info.max_priority;
77 * Initialize the threads subsystem. Returns 0 if successful, or -1 if no
78 * thread support is available.
81 __objc_init_thread_system(void)
83 DEBUG_PRINTF("__objc_init_thread_system\n");
84 return 0; /* Succeeded. */
89 __objc_fini_thread_system(void)
95 * Create a new thread of execution and return its id. Return NULL if fails.
96 * The new thread starts in "func" with the given argument.
99 objc_thread_create(void (*func)(void *arg), void *arg)
101 _objc_thread_t thread_id = NULL; /* Detached thread id. */
102 cthread_t new_thread_handle; /* cthread handle. */
104 objc_mutex_lock(__objc_runtime_mutex);
107 new_thread_handle = cthread_fork((cthread_fn_t)func, arg);
109 if(new_thread_handle) {
110 /* this is not terribly portable */
111 thread_id = *(_objc_thread_t *)&new_thread_handle;
112 cthread_detach(new_thread_handle); /* fully detach thread */
113 __objc_runtime_threads_alive++; /* increment thread count */
116 objc_mutex_unlock(__objc_runtime_mutex);
121 * Set the current thread's priority.
124 objc_thread_set_priority(int priority)
126 _objc_thread_t *t = objc_thread_id();
127 cthread_t cT = (cthread_t) t;
128 int maxPriority = __mach_get_max_thread_priority(cT, NULL);
129 int sys_priority = 0;
131 if (maxPriority == -1)
135 case OBJC_THREAD_INTERACTIVE_PRIORITY:
136 sys_priority = maxPriority;
138 case OBJC_THREAD_BACKGROUND_PRIORITY:
139 sys_priority = (maxPriority * 2) / 3;
141 case OBJC_THREAD_LOW_PRIORITY:
142 sys_priority = maxPriority / 3;
148 if (sys_priority == 0)
151 if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS)
152 return 0; /* Changed priority. End. */
154 return -1; /* Failed. */
158 * Return the current thread's priority [well, whatever it is closest to].
161 objc_thread_get_priority(void)
163 _objc_thread_t *t = objc_thread_id();
164 cthread_t cT = (cthread_t) t; /* see objc_thread_id() */
167 int sys_priority = 0;
169 int interactiveT, backgroundT, lowT; /* threasholds */
171 maxPriority = __mach_get_max_thread_priority(cT, &basePriority);
173 if(maxPriority == -1)
176 if (basePriority > ( (maxPriority * 2) / 3))
177 return OBJC_THREAD_INTERACTIVE_PRIORITY; /* interactive priority
179 if (basePriority > ( maxPriority / 3))
180 return OBJC_THREAD_BACKGROUND_PRIORITY; /* background priority
182 return OBJC_THREAD_LOW_PRIORITY; /* everything else is low */
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.
191 objc_thread_yield(void)
193 cthread_yield(); /* Yield to equal thread. */
197 * Terminate the current tread. Doesn't return anything. Doesn't return.
198 * Actually, if it failed returns -1.
201 objc_thread_exit(void)
203 objc_mutex_lock(__objc_runtime_mutex);
204 __objc_runtime_threads_alive--;
205 objc_mutex_unlock(__objc_runtime_mutex);
207 cthread_exit(&__objc_thread_exit_status); /* Terminate thread. */
212 * Returns an integer value which uniquely describes a thread. Must not be
213 * NULL which is reserved as a marker for "no thread".
218 cthread_t self = cthread_self();
219 return (_objc_thread_t)self;
223 * Sets the thread's local storage pointer. Returns 0 if successful or -1
228 objc_thread_set_data(void *value)
230 cthread_set_data(cthread_self(), (any_t) value);
235 * Returns the thread's local storage pointer. Returns NULL on failure.
238 objc_thread_get_data(void)
240 return (void *) cthread_data(cthread_self());
244 * Allocate a mutex. Return the mutex pointer if successful or NULL if the
245 * allocation failed for any reason.
248 objc_mutex_allocate(void)
253 if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex))))
254 return NULL; /* Abort if malloc failed. */
256 err = mutex_init(&(mutex->lock));
258 if (err != 0) { /* System init failed? */
259 free(mutex); /* Yes, free local memory. */
260 return NULL; /* Abort. */
262 mutex->owner = (_objc_thread_t) -1; /* No owner. */
263 mutex->depth = 0; /* No locks. */
264 return mutex; /* Return mutex handle. */
268 * Deallocate a mutex. Note that this includes an implicit mutex_lock to
269 * insure that no one else is using the lock. It is legal to deallocate
270 * a lock if we have a lock on it, but illegal to deallocate a lock held
272 * Returns the number of locks on the thread. (1 for deallocate).
275 objc_mutex_deallocate(_objc_mutex_t mutex)
277 int depth; /* # of locks on mutex. */
279 if (!mutex) /* Is argument bad? */
280 return -1; /* Yes, abort. */
281 depth = objc_mutex_lock(mutex); /* Must have lock. */
283 mutex_unlock(&(mutex->lock)); /* Must unlock system mutex.*/
284 mutex_clear(&(mutex->lock)); /* Free system mutex. */
286 free(mutex); /* Free memory. */
287 return depth; /* Return last depth. */
291 * Grab a lock on a mutex. If this thread already has a lock on this mutex
292 * then we increment the lock count. If another thread has a lock on the
293 * mutex we block and wait for the thread to release the lock.
294 * Returns the lock count on the mutex held by this thread.
297 objc_mutex_lock(_objc_mutex_t mutex)
299 _objc_thread_t thread_id; /* Cache our thread id. */
301 if (!mutex) /* Is argument bad? */
302 return -1; /* Yes, abort. */
303 thread_id = objc_thread_id(); /* Get this thread's id. */
304 if (mutex->owner == thread_id) /* Already own lock? */
305 return ++mutex->depth; /* Yes, increment depth. */
307 mutex_lock(&(mutex->lock)); /* Lock cthread mutex. */
309 mutex->owner = thread_id; /* Mark thread as owner. */
310 return mutex->depth = 1; /* Increment depth to end. */
314 * Try to grab a lock on a mutex. If this thread already has a lock on
315 * this mutex then we increment the lock count and return it. If another
316 * thread has a lock on the mutex returns -1.
319 objc_mutex_trylock(_objc_mutex_t mutex)
321 _objc_thread_t thread_id; /* Cache our thread id. */
323 if (!mutex) /* Is argument bad? */
324 return -1; /* Yes, abort. */
325 thread_id = objc_thread_id(); /* Get this thread's id. */
326 if (mutex->owner == thread_id) /* Already own lock? */
327 return ++mutex->depth; /* Yes, increment depth. */
329 if (mutex_try_lock(&(mutex->lock)) == 0) /* Lock cthread mutex. */
330 return -1; /* Failed, abort. */
332 mutex->owner = thread_id; /* Mark thread as owner. */
333 return mutex->depth = 1; /* Increment depth to end. */
337 * Decrements the lock count on this mutex by one. If the lock count reaches
338 * zero, release the lock on the mutex. Returns the lock count on the mutex.
339 * It is an error to attempt to unlock a mutex which this thread doesn't hold
340 * in which case return -1 and the mutex is unaffected.
341 * Will also return -1 if the mutex free fails.
344 objc_mutex_unlock(_objc_mutex_t mutex)
346 _objc_thread_t thread_id; /* Cache our thread id. */
348 if (!mutex) /* Is argument bad? */
349 return -1; /* Yes, abort. */
350 thread_id = objc_thread_id(); /* Get this thread's id. */
351 if (mutex->owner != thread_id) /* Does some else own lock? */
352 return -1; /* Yes, abort. */
353 if (mutex->depth > 1) /* Released last lock? */
354 return --mutex->depth; /* No, Decrement depth, end.*/
355 mutex->depth = 0; /* Yes, reset depth to 0. */
356 mutex->owner = (_objc_thread_t) -1; /* Set owner to "no thread".*/
358 mutex_unlock(&(mutex->lock)); /* unlock cthread mutex. */
360 return 0; /* No, return success. */