source: trunk/third/gcc/libobjc/thr.c @ 18477

Revision 18477, 14.2 KB checked in by ghudson, 21 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r18476, which included commits to RCS files with non-trunk default branches.
Line 
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)
4
5This file is part of GNU CC.
6
7GNU CC is free software; you can redistribute it and/or modify it under the
8terms of the GNU General Public License as published by the Free Software
9Foundation; either version 2, or (at your option) any later version.
10
11GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
12WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
14details.
15
16You should have received a copy of the GNU General Public License along with
17GNU CC; see the file COPYING.  If not, write to the Free Software
18Foundation, 59 Temple Place - Suite 330,
19Boston, MA 02111-1307, USA.  */
20
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.  */
26
27#include <stdlib.h>
28#include "runtime.h"
29
30/* Global exit status. */
31int __objc_thread_exit_status = 0;
32
33/* Flag which lets us know if we ever became multi threaded */
34int __objc_is_multi_threaded = 0;
35
36/* The hook function called when the runtime becomes multi threaded */
37objc_thread_callback _objc_became_multi_threaded = NULL;
38
39/*
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.
44
45  It returns the previous hook function or NULL if there is none.
46
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.
50  */
51objc_thread_callback objc_set_thread_callback(objc_thread_callback func)
52{
53  objc_thread_callback temp = _objc_became_multi_threaded;
54  _objc_became_multi_threaded = func;
55  return temp;
56}
57
58/*
59  Private functions
60
61  These functions are utilized by the frontend, but they are not
62  considered part of the public interface.
63  */
64
65/*
66  First function called in a thread, starts everything else.
67
68  This function is passed to the backend by objc_thread_detach
69  as the starting function for a new thread.
70 */
71struct __objc_thread_start_state
72{
73  SEL selector;
74  id object;
75  id argument;
76};
77
78static volatile void
79__objc_thread_detach_function(struct __objc_thread_start_state *istate)
80{
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;
87
88    /* Don't need anymore so free it */
89    objc_free(istate);
90
91    /* Clear out the thread local storage */
92    objc_thread_set_data(NULL);
93
94    /* Check to see if we just became multi threaded */
95    if (!__objc_is_multi_threaded)
96      {
97        __objc_is_multi_threaded = 1;
98
99        /* Call the hook function */
100        if (_objc_became_multi_threaded != NULL)
101          (*_objc_became_multi_threaded)();
102      }
103
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");
110  }
111  else
112    objc_error(nil, OBJC_ERR_BAD_STATE,
113               "objc_thread_detach called with NULL state.\n");
114
115  /* Exit the thread */
116  objc_thread_exit();
117}
118
119/*
120  Frontend functions
121
122  These functions constitute the public interface to the Objective-C thread
123  and mutex functionality.
124  */
125
126/* Frontend thread functions */
127
128/*
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.
132  */
133objc_thread_t
134objc_thread_detach(SEL selector, id object, id argument)
135{
136  struct __objc_thread_start_state *istate;
137  objc_thread_t        thread_id = NULL;
138
139  /* Allocate the state structure */
140  if (!(istate = (struct __objc_thread_start_state *)
141        objc_malloc(sizeof(*istate))))
142    return NULL;
143
144  /* Initialize the state structure */
145  istate->selector = selector;
146  istate->object = object;
147  istate->argument = argument;
148
149  /* lock access */
150  objc_mutex_lock(__objc_runtime_mutex);
151
152  /* Call the backend to spawn the thread */
153  if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function,
154                                        istate)) == NULL)
155    {
156      /* failed! */
157      objc_mutex_unlock(__objc_runtime_mutex);
158      objc_free(istate);
159      return NULL;
160    }
161
162  /* Increment our thread counter */
163  __objc_runtime_threads_alive++;
164  objc_mutex_unlock(__objc_runtime_mutex);
165
166  return thread_id;
167}
168
169/* Set the current thread's priority. */
170int
171objc_thread_set_priority(int priority)
172{
173  /* Call the backend */
174  return __objc_thread_set_priority(priority);
175}
176
177/* Return the current thread's priority. */
178int
179objc_thread_get_priority(void)
180{
181  /* Call the backend */
182  return __objc_thread_get_priority();
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  */
190void
191objc_thread_yield(void)
192{
193  /* Call the backend */
194  __objc_thread_yield();
195}
196
197/*
198  Terminate the current tread.  Doesn't return.
199  Actually, if it failed returns -1.
200  */
201int
202objc_thread_exit(void)
203{
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);
208
209  /* Call the backend to terminate the thread */
210  return __objc_thread_exit();
211}
212
213/*
214  Returns an integer value which uniquely describes a thread.  Must not be
215  NULL which is reserved as a marker for "no thread".
216  */
217objc_thread_t
218objc_thread_id(void)
219{
220  /* Call the backend */
221  return __objc_thread_id();
222}
223
224/*
225  Sets the thread's local storage pointer.
226  Returns 0 if successful or -1 if failed.
227  */
228int
229objc_thread_set_data(void *value)
230{
231  /* Call the backend */
232  return __objc_thread_set_data(value);
233}
234
235/*
236  Returns the thread's local storage pointer.  Returns NULL on failure.
237  */
238void *
239objc_thread_get_data(void)
240{
241  /* Call the backend */
242  return __objc_thread_get_data();
243}
244
245/* Frontend mutex functions */
246
247/*
248  Allocate a mutex.  Return the mutex pointer if successful or NULL if the
249  allocation failed for any reason.
250  */
251objc_mutex_t
252objc_mutex_allocate(void)
253{
254  objc_mutex_t mutex;
255
256  /* Allocate the mutex structure */
257  if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
258    return NULL;
259
260  /* Call backend to create the mutex */
261  if (__objc_mutex_allocate(mutex))
262    {
263      /* failed! */
264      objc_free(mutex);
265      return NULL;
266    }
267
268  /* Initialize mutex */
269  mutex->owner = NULL;
270  mutex->depth = 0;
271  return mutex;
272}
273
274/*
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).
280  */
281int
282objc_mutex_deallocate(objc_mutex_t mutex)
283{
284  int depth;
285
286  /* Valid mutex? */
287  if (!mutex)
288    return -1;
289
290  /* Acquire lock on mutex */
291  depth = objc_mutex_lock(mutex);
292
293  /* Call backend to destroy mutex */
294  if (__objc_mutex_deallocate(mutex))
295    return -1;
296
297  /* Free the mutex structure */
298  objc_free(mutex);
299
300  /* Return last depth */
301  return depth;
302}
303
304/*
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.
309  */
310int
311objc_mutex_lock(objc_mutex_t mutex)
312{
313  objc_thread_t thread_id;
314  int status;
315
316  /* Valid mutex? */
317  if (!mutex)
318    return -1;
319
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;
324
325  /* Call the backend to lock the mutex */
326  status = __objc_mutex_lock(mutex);
327
328  /* Failed? */
329  if (status)
330    return status;
331
332  /* Successfully locked the thread */
333  mutex->owner = thread_id;
334  return mutex->depth = 1;
335}
336
337/*
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.
341  */
342int
343objc_mutex_trylock(objc_mutex_t mutex)
344{
345  objc_thread_t thread_id;
346  int status;
347
348  /* Valid mutex? */
349  if (!mutex)
350    return -1;
351
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;
356   
357  /* Call the backend to try to lock the mutex */
358  status = __objc_mutex_trylock(mutex);
359
360  /* Failed? */
361  if (status)
362    return status;
363
364  /* Successfully locked the thread */
365  mutex->owner = thread_id;
366  return mutex->depth = 1;
367}
368
369/*
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.
376  */
377int
378objc_mutex_unlock(objc_mutex_t mutex)
379{
380  objc_thread_t thread_id;
381  int status;
382
383  /* Valid mutex? */
384  if (!mutex)
385    return -1;
386
387  /* If another thread owns the lock then abort */
388  thread_id = __objc_thread_id();
389  if (mutex->owner != thread_id)
390    return -1;
391
392  /* Decrement depth and return */
393  if (mutex->depth > 1)
394    return --mutex->depth;
395
396  /* Depth down to zero so we are no longer the owner */
397  mutex->depth = 0;
398  mutex->owner = NULL;
399
400  /* Have the backend unlock the mutex */
401  status = __objc_mutex_unlock(mutex);
402
403  /* Failed? */
404  if (status)
405    return status;
406
407  return 0;
408}
409
410/* Frontend condition mutex functions */
411
412/*
413  Allocate a condition.  Return the condition pointer if successful or NULL
414  if the allocation failed for any reason.
415  */
416objc_condition_t
417objc_condition_allocate(void)
418{
419  objc_condition_t condition;
420   
421  /* Allocate the condition mutex structure */
422  if (!(condition =
423        (objc_condition_t)objc_malloc(sizeof(struct objc_condition))))
424    return NULL;
425
426  /* Call the backend to create the condition mutex */
427  if (__objc_condition_allocate(condition))
428    {
429      /* failed! */
430      objc_free(condition);
431      return NULL;
432    }
433
434  /* Success! */
435  return condition;
436}
437
438/*
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.
444  */
445int
446objc_condition_deallocate(objc_condition_t condition)
447{
448  /* Broadcast the condition */
449  if (objc_condition_broadcast(condition))
450    return -1;
451
452  /* Call the backend to destroy */
453  if (__objc_condition_deallocate(condition))
454    return -1;
455
456  /* Free the condition mutex structure */
457  objc_free(condition);
458
459  return 0;
460}
461
462/*
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.
469  */
470int
471objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
472{
473  objc_thread_t thread_id;
474
475  /* Valid arguments? */
476  if (!mutex || !condition)
477    return -1;
478
479  /* Make sure we are owner of mutex */
480  thread_id = __objc_thread_id();
481  if (mutex->owner != thread_id)
482    return -1;
483
484  /* Cannot be locked more than once */
485  if (mutex->depth > 1)
486    return -1;
487
488  /* Virtually unlock the mutex */
489  mutex->depth = 0;
490  mutex->owner = (objc_thread_t)NULL;
491
492  /* Call the backend to wait */
493  __objc_condition_wait(condition, mutex);
494
495  /* Make ourselves owner of the mutex */
496  mutex->owner = thread_id;
497  mutex->depth = 1;
498
499  return 0;
500}
501
502/*
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.
507  */
508int
509objc_condition_broadcast(objc_condition_t condition)
510{
511  /* Valid condition mutex? */
512  if (!condition)
513    return -1;
514
515  return __objc_condition_broadcast(condition);
516}
517
518/*
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.
523  */
524int
525objc_condition_signal(objc_condition_t condition)
526{
527  /* Valid condition mutex? */
528  if (!condition)
529    return -1;
530
531  return __objc_condition_signal(condition);
532}
533
534/* Make the objc thread system aware that a thread which is managed
535   (started, stopped) by external code could access objc facilities
536   from now on.  This is used when you are interfacing with some
537   external non-objc-based environment/system - you must call
538   objc_thread_add() before an alien thread makes any calls to
539   Objective-C.  Do not cause the _objc_became_multi_threaded hook to
540   be executed. */
541void
542objc_thread_add(void)
543{
544  objc_mutex_lock(__objc_runtime_mutex);
545  __objc_is_multi_threaded = 1;
546  __objc_runtime_threads_alive++;
547  objc_mutex_unlock(__objc_runtime_mutex); 
548}
549
550/* Make the objc thread system aware that a thread managed (started,
551   stopped) by some external code will no longer access objc and thus
552   can be forgotten by the objc thread system.  Call
553   objc_thread_remove() when your alien thread is done with making
554   calls to Objective-C. */
555void
556objc_thread_remove(void)
557{
558  objc_mutex_lock(__objc_runtime_mutex);
559  __objc_runtime_threads_alive--;
560  objc_mutex_unlock(__objc_runtime_mutex); 
561}
562
563/* End of File */
Note: See TracBrowser for help on using the repository browser.