OSDN Git Service

Formatting changes.
[pf3gnuchains/gcc-fork.git] / gcc / objc / thr-mach.c
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>
5
6 This file is part of GNU CC.
7
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.
11
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
15 details.
16
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.  */
21
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.  */
27
28 #include <mach/mach.h>
29 #include <mach/cthreads.h>
30 #include <objc/thr.h>
31 #include "runtime.h"
32
33 /********
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.
38  */
39 struct _objc_mutex 
40 {
41     volatile _objc_thread_t     owner;          /* Id of thread that owns.  */
42     volatile int                depth;          /* # of acquires.           */
43     struct mutex                lock;           /* cthread mutex            */
44 };
45
46
47 /********
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.
52  */
53 static int __mach_get_max_thread_priority(cthread_t t, int *base) {
54     thread_t                  threadP;
55     kern_return_t             error;
56     struct thread_sched_info  info;
57     unsigned int              info_count=THREAD_SCHED_INFO_COUNT;
58     
59     if (t == NULL)
60         return -1;
61
62     threadP  = cthread_thread(t);       /* get thread underlying */
63
64     error=thread_info(threadP, THREAD_SCHED_INFO, 
65                       (thread_info_t)&info, &info_count);
66
67     if (error != KERN_SUCCESS)
68         return -1;
69
70     if (base != NULL)
71         *base = info.base_priority;
72
73     return info.max_priority;
74 }
75         
76 /********
77  *  Initialize the threads subsystem.  Returns 0 if successful, or -1 if no
78  *  thread support is available.
79  */
80 int
81 __objc_init_thread_system(void)
82 {
83     DEBUG_PRINTF("__objc_init_thread_system\n");
84     return 0;                                    /* Succeeded.       */
85 }
86
87
88 int
89 __objc_fini_thread_system(void)
90 {
91   return 0;
92 }
93
94 /********
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.
97  */
98 _objc_thread_t
99 objc_thread_create(void (*func)(void *arg), void *arg)
100 {
101     _objc_thread_t      thread_id = NULL;       /* Detached thread id.      */
102     cthread_t           new_thread_handle;      /* cthread handle.          */
103
104     objc_mutex_lock(__objc_runtime_mutex);
105     
106     /* create thread */
107     new_thread_handle = cthread_fork((cthread_fn_t)func, arg);
108
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 */
114     }
115     
116     objc_mutex_unlock(__objc_runtime_mutex);
117     return thread_id;
118 }
119
120 /********
121  *  Set the current thread's priority.
122  */
123 int
124 objc_thread_set_priority(int priority)
125 {
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;
130
131     if (maxPriority == -1)
132         return -1;
133
134     switch (priority) {
135     case OBJC_THREAD_INTERACTIVE_PRIORITY:
136         sys_priority = maxPriority;
137         break;
138     case OBJC_THREAD_BACKGROUND_PRIORITY:
139         sys_priority = (maxPriority * 2) / 3;
140         break;
141     case OBJC_THREAD_LOW_PRIORITY:
142         sys_priority = maxPriority / 3;
143         break;
144     default:
145         return -1;
146     }
147
148     if (sys_priority == 0)
149         return -1;
150     
151     if (cthread_priority(cT, sys_priority, 0) == KERN_SUCCESS)
152         return 0;                               /* Changed priority. End.   */
153     
154     return -1;                                  /* Failed.                  */
155 }
156
157 /********
158  *  Return the current thread's priority [well, whatever it is closest to].
159  */
160 int
161 objc_thread_get_priority(void)
162 {
163     _objc_thread_t *t           = objc_thread_id();
164     cthread_t      cT           = (cthread_t) t; /* see objc_thread_id() */
165     int            basePriority;
166     int            maxPriority;
167     int            sys_priority = 0;
168
169     int interactiveT, backgroundT, lowT; /* threasholds */
170
171     maxPriority = __mach_get_max_thread_priority(cT, &basePriority);
172
173     if(maxPriority == -1)
174         return -1;
175
176     if (basePriority > ( (maxPriority * 2) / 3))
177         return OBJC_THREAD_INTERACTIVE_PRIORITY; /* interactive priority
178                                                  */
179     if (basePriority > ( maxPriority / 3))
180         return OBJC_THREAD_BACKGROUND_PRIORITY; /* background priority
181                                                  */
182     return OBJC_THREAD_LOW_PRIORITY; /* everything else is low */
183 }
184
185 /********
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.
189  */
190 void
191 objc_thread_yield(void)
192 {
193     cthread_yield();                            /* Yield to equal thread.   */
194 }
195
196 /********
197  *  Terminate the current tread.  Doesn't return anything.  Doesn't return.
198  *  Actually, if it failed returns -1.
199  */
200 int
201 objc_thread_exit(void)
202 {
203   objc_mutex_lock(__objc_runtime_mutex);
204   __objc_runtime_threads_alive--;
205   objc_mutex_unlock(__objc_runtime_mutex);
206       
207   cthread_exit(&__objc_thread_exit_status);     /* Terminate thread.        */
208   return -1;
209 }
210
211 /********
212  *  Returns an integer value which uniquely describes a thread.  Must not be
213  *  NULL which is reserved as a marker for "no thread".
214  */
215 _objc_thread_t
216 objc_thread_id(void)
217 {
218   cthread_t self = cthread_self();
219   return (_objc_thread_t)self;
220 }
221
222 /********
223  *  Sets the thread's local storage pointer.  Returns 0 if successful or -1
224  *  if failed.
225  */
226
227 int
228 objc_thread_set_data(void *value)
229 {
230   cthread_set_data(cthread_self(), (any_t) value);
231   return 0;
232 }
233
234 /********
235  *  Returns the thread's local storage pointer.  Returns NULL on failure.
236  */
237 void *
238 objc_thread_get_data(void)
239 {
240   return (void *) cthread_data(cthread_self());
241 }
242
243 /********
244  *  Allocate a mutex.  Return the mutex pointer if successful or NULL if the
245  *  allocation failed for any reason.
246  */
247 _objc_mutex_t
248 objc_mutex_allocate(void)
249 {
250     _objc_mutex_t mutex;
251     int         err = 0;
252     
253     if (!(mutex = (_objc_mutex_t)__objc_xmalloc(sizeof(struct _objc_mutex))))
254         return NULL;                            /* Abort if malloc failed.  */
255
256     err = mutex_init(&(mutex->lock));
257     
258     if (err != 0) {                             /* System init failed?      */
259         free(mutex);                            /* Yes, free local memory.  */
260         return NULL;                            /* Abort.                   */
261     }
262     mutex->owner = (_objc_thread_t) -1;         /* No owner.                */
263     mutex->depth = 0;                           /* No locks.                */
264     return mutex;                               /* Return mutex handle.     */
265 }
266
267 /********
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
271  *  by anyone else.
272  *  Returns the number of locks on the thread.  (1 for deallocate).
273  */
274 int
275 objc_mutex_deallocate(_objc_mutex_t mutex)
276 {
277     int         depth;                          /* # of locks on mutex.     */
278
279     if (!mutex)                                 /* Is argument bad?         */
280         return -1;                              /* Yes, abort.              */
281     depth = objc_mutex_lock(mutex);             /* Must have lock.          */
282     
283     mutex_unlock(&(mutex->lock));               /* Must unlock system mutex.*/
284     mutex_clear(&(mutex->lock));                /* Free system mutex.       */
285     
286     free(mutex);                                /* Free memory.             */
287     return depth;                               /* Return last depth.       */
288 }
289
290 /********
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.
295  */
296 int
297 objc_mutex_lock(_objc_mutex_t mutex)
298 {
299     _objc_thread_t thread_id;                  /* Cache our thread id.     */
300
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.    */
306
307     mutex_lock(&(mutex->lock));                 /* Lock cthread mutex.      */
308     
309     mutex->owner = thread_id;                   /* Mark thread as owner.    */
310     return mutex->depth = 1;                    /* Increment depth to end.  */
311 }
312
313 /********
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.
317  */
318 int
319 objc_mutex_trylock(_objc_mutex_t mutex)
320 {
321     _objc_thread_t         thread_id;           /* Cache our thread id.     */
322
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.    */
328     
329     if (mutex_try_lock(&(mutex->lock)) == 0)    /* Lock cthread mutex.      */
330         return -1;                              /* Failed, abort.           */
331     
332     mutex->owner = thread_id;                   /* Mark thread as owner.    */
333     return mutex->depth = 1;                    /* Increment depth to end.  */
334 }
335
336 /********
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.
342  */
343 int
344 objc_mutex_unlock(_objc_mutex_t mutex)
345 {
346     _objc_thread_t    thread_id;                /* Cache our thread id.     */
347     
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".*/
357     
358     mutex_unlock(&(mutex->lock));               /* unlock cthread mutex.    */
359     
360     return 0;                                   /* No, return success.      */
361 }