OSDN Git Service

(OBJC_VERSION): Increment version as recent changes have made old
[pf3gnuchains/gcc-fork.git] / gcc / objc / thr-mach.c
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>
6
7 This file is part of GNU CC.
8
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.
12
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
16 details.
17
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.  */
22
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.  */
28
29 #include <mach/mach.h>
30 #include <mach/cthreads.h>
31 #include <objc/thr.h>
32 #include "runtime.h"
33
34 /********
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.
39  */
40 struct objc_mutex 
41 {
42     volatile objc_thread_t     owner;          /* Id of thread that owns.  */
43     volatile int                depth;          /* # of acquires.           */
44     struct mutex                lock;           /* cthread mutex            */
45 };
46
47 struct objc_condition 
48 {
49     struct condition            condition;      /* cthread condition        */
50 };
51
52 /********
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.
57  */
58 static int __mach_get_max_thread_priority(cthread_t t, int *base) {
59     thread_t                  threadP;
60     kern_return_t             error;
61     struct thread_sched_info  info;
62     unsigned int              info_count=THREAD_SCHED_INFO_COUNT;
63     
64     if (t == NULL)
65         return -1;
66
67     threadP  = cthread_thread(t);       /* get thread underlying */
68
69     error=thread_info(threadP, THREAD_SCHED_INFO, 
70                       (thread_info_t)&info, &info_count);
71
72     if (error != KERN_SUCCESS)
73         return -1;
74
75     if (base != NULL)
76         *base = info.base_priority;
77
78     return info.max_priority;
79 }
80         
81 /********
82  *  Initialize the threads subsystem.  Returns 0 if successful, or -1 if no
83  *  thread support is available.
84  */
85 int
86 __objc_init_thread_system(void)
87 {
88     DEBUG_PRINTF("__objc_init_thread_system\n");
89     return 0;                                    /* Succeeded.       */
90 }
91
92
93 int
94 __objc_fini_thread_system(void)
95 {
96   return 0;
97 }
98
99 /********
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.
102  */
103 objc_thread_t
104 objc_thread_create(void (*func)(void *arg), void *arg)
105 {
106     objc_thread_t      thread_id = NULL;       /* Detached thread id.      */
107     cthread_t           new_thread_handle;      /* cthread handle.          */
108
109     objc_mutex_lock(__objc_runtime_mutex);
110     
111     /* create thread */
112     new_thread_handle = cthread_fork((cthread_fn_t)func, arg);
113
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 */
119     }
120     
121     objc_mutex_unlock(__objc_runtime_mutex);
122     return thread_id;
123 }
124
125 /********
126  *  Set the current thread's priority.
127  */
128 int
129 objc_thread_set_priority(int priority)
130 {
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;
135
136     if (maxPriority == -1)
137         return -1;
138
139     switch (priority) {
140     case OBJC_THREAD_INTERACTIVE_PRIORITY:
141         sys_priority = maxPriority;
142         break;
143     case OBJC_THREAD_BACKGROUND_PRIORITY:
144         sys_priority = (maxPriority * 2) / 3;
145         break;
146     case OBJC_THREAD_LOW_PRIORITY:
147         sys_priority = maxPriority / 3;
148         break;
149     default:
150         return -1;
151     }
152
153     if (sys_priority == 0)
154         return -1;
155     
156     if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS)
157         return 0;                               /* Changed priority. End.   */
158     
159     return -1;                                  /* Failed.                  */
160 }
161
162 /********
163  *  Return the current thread's priority [well, whatever it is closest to].
164  */
165 int
166 objc_thread_get_priority(void)
167 {
168     objc_thread_t *t            = objc_thread_id();
169     cthread_t      cT           = (cthread_t) t; /* see objc_thread_id() */
170     int            basePriority;
171     int            maxPriority;
172     int            sys_priority = 0;
173
174     int interactiveT, backgroundT, lowT; /* threasholds */
175
176     maxPriority = __mach_get_max_thread_priority(cT, &basePriority);
177
178     if(maxPriority == -1)
179         return -1;
180
181     if (basePriority > ( (maxPriority * 2) / 3))
182         return OBJC_THREAD_INTERACTIVE_PRIORITY; /* interactive priority
183                                                  */
184     if (basePriority > ( maxPriority / 3))
185         return OBJC_THREAD_BACKGROUND_PRIORITY; /* background priority
186                                                  */
187     return OBJC_THREAD_LOW_PRIORITY; /* everything else is low */
188 }
189
190 /********
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.
194  */
195 void
196 objc_thread_yield(void)
197 {
198     cthread_yield();                            /* Yield to equal thread.   */
199 }
200
201 /********
202  *  Terminate the current tread.  Doesn't return anything.  Doesn't return.
203  *  Actually, if it failed returns -1.
204  */
205 int
206 objc_thread_exit(void)
207 {
208   objc_mutex_lock(__objc_runtime_mutex);
209   __objc_runtime_threads_alive--;
210   objc_mutex_unlock(__objc_runtime_mutex);
211       
212   cthread_exit(&__objc_thread_exit_status);     /* Terminate thread.        */
213   return -1;
214 }
215
216 /********
217  *  Returns an integer value which uniquely describes a thread.  Must not be
218  *  NULL which is reserved as a marker for "no thread".
219  */
220 objc_thread_t
221 objc_thread_id(void)
222 {
223   cthread_t self = cthread_self();
224   return (objc_thread_t)self;
225 }
226
227 /********
228  *  Sets the thread's local storage pointer.  Returns 0 if successful or -1
229  *  if failed.
230  */
231
232 int
233 objc_thread_set_data(void *value)
234 {
235   cthread_set_data(cthread_self(), (any_t) value);
236   return 0;
237 }
238
239 /********
240  *  Returns the thread's local storage pointer.  Returns NULL on failure.
241  */
242 void *
243 objc_thread_get_data(void)
244 {
245   return (void *) cthread_data(cthread_self());
246 }
247
248 /********
249  *  Allocate a mutex.  Return the mutex pointer if successful or NULL if the
250  *  allocation failed for any reason.
251  */
252 objc_mutex_t
253 objc_mutex_allocate(void)
254 {
255     objc_mutex_t mutex;
256     int         err = 0;
257     
258     if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
259         return NULL;                            /* Abort if malloc failed.  */
260
261     err = mutex_init(&(mutex->lock));
262     
263     if (err != 0) {                             /* System init failed?      */
264         objc_free(mutex);                       /* Yes, free local memory.  */
265         return NULL;                            /* Abort.                   */
266     }
267     mutex->owner = (objc_thread_t) -1;         /* No owner.                */
268     mutex->depth = 0;                           /* No locks.                */
269     return mutex;                               /* Return mutex handle.     */
270 }
271
272 /********
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
276  *  by anyone else.
277  *  Returns the number of locks on the thread.  (1 for deallocate).
278  */
279 int
280 objc_mutex_deallocate(objc_mutex_t mutex)
281 {
282     int         depth;                          /* # of locks on mutex.     */
283
284     if (!mutex)                                 /* Is argument bad?         */
285         return -1;                              /* Yes, abort.              */
286     depth = objc_mutex_lock(mutex);             /* Must have lock.          */
287     
288     mutex_unlock(&(mutex->lock));               /* Must unlock system mutex.*/
289     mutex_clear(&(mutex->lock));                /* Free system mutex.       */
290     
291     objc_free(mutex);                           /* Free memory.             */
292     return depth;                               /* Return last depth.       */
293 }
294
295 /********
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.
300  */
301 int
302 objc_mutex_lock(objc_mutex_t mutex)
303 {
304     objc_thread_t thread_id;                  /* Cache our thread id.     */
305
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.    */
311
312     mutex_lock(&(mutex->lock));                 /* Lock cthread mutex.      */
313     
314     mutex->owner = thread_id;                   /* Mark thread as owner.    */
315     return mutex->depth = 1;                    /* Increment depth to end.  */
316 }
317
318 /********
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.
322  */
323 int
324 objc_mutex_trylock(objc_mutex_t mutex)
325 {
326     objc_thread_t         thread_id;           /* Cache our thread id.     */
327
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.    */
333     
334     if (mutex_try_lock(&(mutex->lock)) == 0)    /* Lock cthread mutex.      */
335         return -1;                              /* Failed, abort.           */
336     
337     mutex->owner = thread_id;                   /* Mark thread as owner.    */
338     return mutex->depth = 1;                    /* Increment depth to end.  */
339 }
340
341 /********
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.
347  */
348 int
349 objc_mutex_unlock(objc_mutex_t mutex)
350 {
351     objc_thread_t    thread_id;                /* Cache our thread id.     */
352     
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".*/
362     
363     mutex_unlock(&(mutex->lock));               /* unlock cthread mutex.    */
364     
365     return 0;                                   /* No, return success.      */
366 }
367
368 /********
369  *  Allocate a condition.  Return the condition pointer if successful or NULL
370  * if the allocation failed for any reason.
371  */
372 objc_condition_t 
373 objc_condition_allocate(void)
374 {
375     objc_condition_t condition;
376     
377     if (!(condition = (objc_condition_t)objc_malloc(
378                         sizeof(struct objc_condition))))
379         return NULL;                            /* Abort if malloc failed.  */
380
381     condition_init(&(condition->condition));
382     
383     return condition;                           /* Return mutex handle.     */
384 }
385
386 /********
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.
392  */
393 int
394 objc_condition_deallocate(objc_condition_t condition)
395 {
396         condition_broadcast(&(condition->condition));
397         condition_clear(&(condition->condition));
398         objc_free(condition);
399         return 0;
400 }
401
402 /********
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.
409  */
410 int
411 objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
412 {
413     objc_thread_t    thread_id;                /* Cache our thread id.     */
414     
415     if (!mutex || !condition)                   /* Is argument bad?         */
416         return -1;                              /* Yes, abort.              */
417
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".*/
426     
427     condition_wait(&(condition->condition),
428                 &(mutex->lock));                        /* unlock, wait ..., lock   */
429     
430     mutex->owner = thread_id;                   /* Mark thread as owner.    */
431     mutex->depth = 1;                           /* Increment depth to end.  */
432     return 0;                                   /* Return success.          */
433 }
434
435 /********
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.
440  */
441 int
442 objc_condition_broadcast(objc_condition_t condition)
443 {
444     if (!condition)
445                 return -1;
446         condition_broadcast(&(condition->condition));
447         return 0;
448 }
449
450 /********
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.
455  */
456 int
457 objc_condition_signal(objc_condition_t condition)
458 {
459     if (!condition)
460                 return -1;
461         condition_signal(&(condition->condition));
462         return 0;
463 }
464