~ubuntu-branches/ubuntu/trusty/gnustep-base/trusty

« back to all changes in this revision

Viewing changes to Source/thr-pthread.m

Tags: upstream-1.20.0
ImportĀ upstreamĀ versionĀ 1.20.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* GNU Objective C Runtime Thread Implementation for PCThreads under Darwin.
2
 
   Copyright (C) 1996, 1997 Free Software Foundation, Inc.
3
 
   Contributed by Scott Christley <scottc@net-community.com>
4
 
   Condition functions added by: Mircea Oancea <mircea@first.elcom.pub.ro>
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
11
 
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, 51 Franklin Street, Fifth Floor,
21
 
Boston, MA 02110-1301, 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 <pthread.h>
30
 
#include <stdarg.h>
31
 
#include "GNUstepBase/objc-gnu2next.h"
32
 
#include "thr-mach.h"
33
 
 
34
 
/* Key structure for maintaining thread specific storage */
35
 
static pthread_key_t _objc_thread_storage;
36
 
static pthread_attr_t _objc_thread_attribs;
37
 
 
38
 
/* Global exit status. */
39
 
int __objc_thread_exit_status = 0;
40
 
 
41
 
/* Number of threads alive  */
42
 
int __objc_runtime_threads_alive = 0;
43
 
 
44
 
/* Thread create/exit mutex */
45
 
struct objc_mutex* __objc_runtime_mutex = NULL;
46
 
 
47
 
/* Flag which lets us know if we ever became multi threaded */
48
 
int __objc_is_multi_threaded = 0;
49
 
 
50
 
/* The hook function called when the runtime becomes multi threaded */
51
 
objc_thread_callback _objc_became_multi_threaded = NULL;
52
 
 
53
 
/*
54
 
  Use this to set the hook function that will be called when the
55
 
  runtime initially becomes multi threaded.
56
 
  The hook function is only called once, meaning only when the
57
 
  2nd thread is spawned, not for each and every thread.
58
 
 
59
 
  It returns the previous hook function or NULL if there is none.
60
 
 
61
 
  A program outside of the runtime could set this to some function so
62
 
  it can be informed; for example, the GNUstep Base Library sets it
63
 
  so it can implement the NSBecomingMultiThreaded notification.
64
 
  */
65
 
objc_thread_callback objc_set_thread_callback(objc_thread_callback func)
66
 
{
67
 
  objc_thread_callback temp = _objc_became_multi_threaded;
68
 
  _objc_became_multi_threaded = func;
69
 
  return temp;
70
 
}
71
 
 
72
 
/*
73
 
  Private functions
74
 
 
75
 
  These functions are utilized by the frontend, but they are not
76
 
  considered part of the public interface.
77
 
  */
78
 
 
79
 
/*
80
 
  First function called in a thread, starts everything else.
81
 
 
82
 
  This function is passed to the backend by objc_thread_detach
83
 
  as the starting function for a new thread.
84
 
 */
85
 
struct __objc_thread_start_state
86
 
{
87
 
  SEL selector;
88
 
  id object;
89
 
  id argument;
90
 
};
91
 
 
92
 
objc_thread_t
93
 
__objc_thread_detach(void (*func)(void *arg), void *arg);
94
 
 
95
 
static volatile void
96
 
__objc_thread_detach_function(struct __objc_thread_start_state *istate)
97
 
{
98
 
  /* Valid state? */
99
 
  if (istate) {
100
 
    id (*imp)(id,SEL,id);
101
 
    SEL selector = istate->selector;
102
 
    id object   = istate->object;
103
 
    id argument = istate->argument;
104
 
 
105
 
    /* Don't need anymore so free it */
106
 
    objc_free(istate);
107
 
 
108
 
    /* Clear out the thread local storage */
109
 
    objc_thread_set_data(NULL);
110
 
 
111
 
    /* Check to see if we just became multi threaded */
112
 
    if (!__objc_is_multi_threaded)
113
 
      {
114
 
        __objc_is_multi_threaded = 1;
115
 
 
116
 
        /* Call the hook function */
117
 
        if (_objc_became_multi_threaded != NULL)
118
 
          (*_objc_became_multi_threaded)();
119
 
      }
120
 
 
121
 
    /* Call the method */
122
 
    if ((imp = (id(*)(id, SEL, id))objc_msg_lookup(object, selector)))
123
 
        (*imp)(object, selector, argument);
124
 
    else
125
 
      objc_error(object, OBJC_ERR_UNIMPLEMENTED,
126
 
                 "objc_thread_detach called with bad selector.\n");
127
 
  }
128
 
  else
129
 
    objc_error(nil, OBJC_ERR_BAD_STATE,
130
 
               "objc_thread_detach called with NULL state.\n");
131
 
 
132
 
  /* Exit the thread */
133
 
  objc_thread_exit();
134
 
}
135
 
 
136
 
/*
137
 
  Frontend functions
138
 
 
139
 
  These functions constitute the public interface to the Objective-C thread
140
 
  and mutex functionality.
141
 
  */
142
 
 
143
 
/* Frontend thread functions */
144
 
 
145
 
/*
146
 
  Detach a new thread of execution and return its id.  Returns NULL if fails.
147
 
  Thread is started by sending message with selector to object.  Message
148
 
  takes a single argument.
149
 
  */
150
 
objc_thread_t
151
 
objc_thread_detach(SEL selector, id object, id argument)
152
 
{
153
 
  struct __objc_thread_start_state *istate;
154
 
  objc_thread_t        thread_id = NULL;
155
 
 
156
 
  /* Allocate the state structure */
157
 
  if (!(istate = (struct __objc_thread_start_state *)
158
 
        objc_malloc(sizeof(*istate))))
159
 
    return NULL;
160
 
 
161
 
  /* Initialize the state structure */
162
 
  istate->selector = selector;
163
 
  istate->object = object;
164
 
  istate->argument = argument;
165
 
 
166
 
  /* lock access */
167
 
  objc_mutex_lock(__objc_runtime_mutex);
168
 
 
169
 
  /* Call the backend to spawn the thread */
170
 
  if ((thread_id = __objc_thread_detach((void *)__objc_thread_detach_function,
171
 
                                        istate)) == NULL)
172
 
    {
173
 
      /* failed! */
174
 
      objc_mutex_unlock(__objc_runtime_mutex);
175
 
      objc_free(istate);
176
 
      return NULL;
177
 
    }
178
 
 
179
 
  /* Increment our thread counter */
180
 
  __objc_runtime_threads_alive++;
181
 
  objc_mutex_unlock(__objc_runtime_mutex);
182
 
 
183
 
  return thread_id;
184
 
}
185
 
 
186
 
/* Backend initialization functions */
187
 
 
188
 
/* Initialize the threads subsystem. */
189
 
int
190
 
__objc_init_thread_system(void)
191
 
{
192
 
  /* Initialize the thread storage key */
193
 
  if (pthread_key_create(&_objc_thread_storage, NULL) == 0)
194
 
    {
195
 
      /*
196
 
       * The normal default detach state for threads is PTHREAD_CREATE_JOINABLE
197
 
       * which causes threads to not die when you think they should.
198
 
           */
199
 
      if (pthread_attr_init(&_objc_thread_attribs) == 0)
200
 
        {
201
 
          if (pthread_attr_setdetachstate(&_objc_thread_attribs, 
202
 
                                          PTHREAD_CREATE_DETACHED) == 0)
203
 
            return 0;
204
 
        }
205
 
    }
206
 
 
207
 
  return -1;
208
 
}
209
 
 
210
 
/* Close the threads subsystem. */
211
 
int
212
 
__objc_close_thread_system(void)
213
 
{
214
 
  /* Destroy the thread storage key */
215
 
  if (pthread_key_delete(_objc_thread_storage) == 0)
216
 
    {
217
 
      if (pthread_attr_destroy(&_objc_thread_attribs) == 0)
218
 
        return 0;
219
 
    }
220
 
 
221
 
  return -1;
222
 
}
223
 
 
224
 
/* Backend thread functions */
225
 
 
226
 
/* Create a new thread of execution. */
227
 
objc_thread_t
228
 
__objc_thread_detach(void (*func)(void *arg), void *arg)
229
 
{
230
 
  objc_thread_t thread_id;
231
 
  pthread_t new_thread_handle;
232
 
 
233
 
  if (!(pthread_create(&new_thread_handle, &_objc_thread_attribs,
234
 
         (void *)func, arg)))
235
 
      thread_id = *(objc_thread_t *)&new_thread_handle;
236
 
  else
237
 
    thread_id = NULL;
238
 
 
239
 
  return thread_id;
240
 
}
241
 
 
242
 
/* Set the current thread's priority. */
243
 
int
244
 
objc_thread_set_priority(int priority)
245
 
{
246
 
  /* Not implemented yet */
247
 
  return -1;
248
 
}
249
 
 
250
 
/* Return the current thread's priority. */
251
 
int
252
 
objc_thread_get_priority(void)
253
 
{
254
 
  /* Not implemented yet */
255
 
  return OBJC_THREAD_INTERACTIVE_PRIORITY;
256
 
}
257
 
 
258
 
/* Yield our process time to another thread. */
259
 
void
260
 
objc_thread_yield(void)
261
 
{
262
 
#if !defined(__APPLE__)
263
 
  /* Not defined in darwin? */
264
 
  pthread_yield();
265
 
#else
266
 
  sched_yield();
267
 
#endif
268
 
}
269
 
 
270
 
/* Terminate the current thread. */
271
 
int
272
 
objc_thread_exit(void)
273
 
{
274
 
  /* Decrement our counter of the number of threads alive */
275
 
  objc_mutex_lock(__objc_runtime_mutex);
276
 
  __objc_runtime_threads_alive--;
277
 
  objc_mutex_unlock(__objc_runtime_mutex);
278
 
 
279
 
  /* exit the thread */
280
 
  pthread_exit(&__objc_thread_exit_status);
281
 
 
282
 
  /* Failed if we reached here */
283
 
  return -1;
284
 
}
285
 
 
286
 
/* Returns an integer value which uniquely describes a thread. */
287
 
objc_thread_t
288
 
objc_thread_id(void)
289
 
{
290
 
  pthread_t self = pthread_self();
291
 
 
292
 
  return *(objc_thread_t *)&self;
293
 
}
294
 
 
295
 
/* Sets the thread's local storage pointer. */
296
 
int
297
 
objc_thread_set_data(void *value)
298
 
{
299
 
  return pthread_setspecific(_objc_thread_storage, value);
300
 
}
301
 
 
302
 
/* Returns the thread's local storage pointer. */
303
 
void *
304
 
objc_thread_get_data(void)
305
 
{
306
 
  return pthread_getspecific(_objc_thread_storage);
307
 
}
308
 
 
309
 
/* Backend mutex functions */
310
 
 
311
 
/* Allocate a mutex. */
312
 
objc_mutex_t
313
 
objc_mutex_allocate(void)
314
 
{
315
 
  objc_mutex_t mutex;
316
 
 
317
 
  /* Allocate the mutex structure */
318
 
  if (!(mutex = (objc_mutex_t)objc_malloc(sizeof(struct objc_mutex))))
319
 
    return NULL;
320
 
 
321
 
  /* Call backend to create the mutex */
322
 
  if (pthread_mutex_init((pthread_mutex_t *)(&(mutex->backend)), NULL))
323
 
    {
324
 
      /* failed! */
325
 
      objc_free(mutex);
326
 
      return NULL;
327
 
    }
328
 
 
329
 
  /* Initialize mutex */
330
 
  mutex->owner = NULL;
331
 
  mutex->depth = 0;
332
 
  return mutex;
333
 
}
334
 
 
335
 
/* Deallocate a mutex. */
336
 
int
337
 
objc_mutex_deallocate(objc_mutex_t mutex)
338
 
{
339
 
  int depth;
340
 
 
341
 
  /* Valid mutex? */
342
 
  if (!mutex)
343
 
    return -1;
344
 
 
345
 
  /* Acquire lock on mutex */
346
 
  depth = objc_mutex_lock(mutex);
347
 
 
348
 
  if (pthread_mutex_destroy((pthread_mutex_t *)(&(mutex->backend))))
349
 
    return -1;
350
 
 
351
 
  /* Free the mutex structure */
352
 
  objc_free(mutex);
353
 
 
354
 
  /* Return last depth */
355
 
  return depth;
356
 
}
357
 
 
358
 
/* Grab a lock on a mutex. */
359
 
int
360
 
objc_mutex_lock(objc_mutex_t mutex)
361
 
{
362
 
  objc_thread_t thread_id;
363
 
  int status;
364
 
 
365
 
  /* Valid mutex? */
366
 
  if (!mutex)
367
 
    return -1;
368
 
 
369
 
  /* If we already own the lock then increment depth */
370
 
  thread_id = objc_thread_id();
371
 
  if (mutex->owner == thread_id)
372
 
    return ++mutex->depth;
373
 
 
374
 
  /* Call the backend to lock the mutex */
375
 
  status = pthread_mutex_lock((pthread_mutex_t *)(&(mutex->backend)));
376
 
 
377
 
  /* Failed? */
378
 
  if (status)
379
 
    return status;
380
 
 
381
 
  /* Successfully locked the thread */
382
 
  mutex->owner = thread_id;
383
 
  return mutex->depth = 1;
384
 
}
385
 
 
386
 
/* Try to grab a lock on a mutex. */
387
 
int
388
 
objc_mutex_trylock(objc_mutex_t mutex)
389
 
{
390
 
  objc_thread_t thread_id;
391
 
  int status;
392
 
 
393
 
  /* Valid mutex? */
394
 
  if (!mutex)
395
 
    return -1;
396
 
 
397
 
  /* If we already own the lock then increment depth */
398
 
  thread_id = objc_thread_id();
399
 
  if (mutex->owner == thread_id)
400
 
    return ++mutex->depth;
401
 
 
402
 
  /* Call the backend to try to lock the mutex */
403
 
  status = pthread_mutex_trylock((pthread_mutex_t *)(&(mutex->backend)));
404
 
 
405
 
  /* Failed? */
406
 
  if (status)
407
 
    return status;
408
 
 
409
 
  /* Successfully locked the thread */
410
 
  mutex->owner = thread_id;
411
 
  return mutex->depth = 1;
412
 
}
413
 
 
414
 
/* Unlock the mutex */
415
 
int
416
 
objc_mutex_unlock(objc_mutex_t mutex)
417
 
{
418
 
  objc_thread_t thread_id;
419
 
 
420
 
  /* Valid mutex? */
421
 
  if (!mutex)
422
 
    return -1;
423
 
 
424
 
  /* If another thread owns the lock then abort */
425
 
  thread_id = objc_thread_id();
426
 
  if (mutex->owner != thread_id)
427
 
    return -1;
428
 
 
429
 
  /* Decrement depth and return */
430
 
  if (mutex->depth > 1)
431
 
    return --mutex->depth;
432
 
 
433
 
  /* Depth down to zero so we are no longer the owner */
434
 
  mutex->depth = 0;
435
 
  mutex->owner = NULL;
436
 
 
437
 
  /* Have the backend unlock the mutex */
438
 
  return pthread_mutex_unlock((pthread_mutex_t *)(&(mutex->backend)));
439
 
}
440
 
 
441
 
/* Backend condition mutex functions */
442
 
 
443
 
/* Allocate a condition. */
444
 
objc_condition_t
445
 
objc_condition_allocate(void)
446
 
{
447
 
  objc_condition_t condition;
448
 
 
449
 
  /* Allocate the condition mutex structure */
450
 
  if (!(condition =
451
 
        (objc_condition_t)objc_malloc(sizeof(struct objc_condition))))
452
 
    return NULL;
453
 
 
454
 
  /* Call the backend to create the condition mutex */
455
 
  if (pthread_cond_init((pthread_cond_t *)(&(condition->backend)), NULL))
456
 
    {
457
 
      /* failed! */
458
 
      objc_free(condition);
459
 
      return NULL;
460
 
    }
461
 
 
462
 
  /* Success! */
463
 
  return condition;
464
 
}
465
 
 
466
 
/* Deallocate a condition. */
467
 
int
468
 
objc_condition_deallocate(objc_condition_t condition)
469
 
{
470
 
  /* Broadcast the condition */
471
 
  if (objc_condition_broadcast(condition))
472
 
    return -1;
473
 
 
474
 
  /* Call the backend to destroy */
475
 
  if (pthread_cond_destroy((pthread_cond_t *)(&(condition->backend))))
476
 
    return -1;
477
 
 
478
 
  /* Free the condition mutex structure */
479
 
  objc_free(condition);
480
 
 
481
 
  return 0;
482
 
}
483
 
 
484
 
/* Wait on the condition */
485
 
int
486
 
objc_condition_wait(objc_condition_t condition, objc_mutex_t mutex)
487
 
{
488
 
  objc_thread_t thread_id;
489
 
 
490
 
  /* Valid arguments? */
491
 
  if (!mutex || !condition)
492
 
    return -1;
493
 
 
494
 
  /* Make sure we are owner of mutex */
495
 
  thread_id = objc_thread_id();
496
 
  if (mutex->owner != thread_id)
497
 
    return -1;
498
 
 
499
 
  /* Cannot be locked more than once */
500
 
  if (mutex->depth > 1)
501
 
    return -1;
502
 
 
503
 
  /* Virtually unlock the mutex */
504
 
  mutex->depth = 0;
505
 
  mutex->owner = (objc_thread_t)NULL;
506
 
 
507
 
  /* Call the backend to wait */
508
 
  pthread_cond_wait((pthread_cond_t *)(&(condition->backend)),
509
 
                           (pthread_mutex_t *)(&(mutex->backend)));
510
 
 
511
 
  /* Make ourselves owner of the mutex */
512
 
  mutex->owner = thread_id;
513
 
  mutex->depth = 1;
514
 
 
515
 
  return 0;
516
 
}
517
 
 
518
 
/* Wake up all threads waiting on this condition. */
519
 
int
520
 
objc_condition_broadcast(objc_condition_t condition)
521
 
{
522
 
  /* Valid condition mutex? */
523
 
  if (!condition)
524
 
    return -1;
525
 
 
526
 
  return pthread_cond_broadcast((pthread_cond_t *)(&(condition->backend)));
527
 
}
528
 
 
529
 
/* Wake up one thread waiting on this condition. */
530
 
int
531
 
objc_condition_signal(objc_condition_t condition)
532
 
{
533
 
  /* Valid condition mutex? */
534
 
  if (!condition)
535
 
    return -1;
536
 
 
537
 
  return pthread_cond_signal((pthread_cond_t *)(&(condition->backend)));
538
 
}
539
 
 
540
 
/* Make the objc thread system aware that a thread which is managed
541
 
   (started, stopped) by external code could access objc facilities
542
 
   from now on.  This is used when you are interfacing with some
543
 
   external non-objc-based environment/system - you must call
544
 
   objc_thread_add() before an alien thread makes any calls to
545
 
   Objective-C.  Do not cause the _objc_became_multi_threaded hook to
546
 
   be executed. */
547
 
void
548
 
objc_thread_add(void)
549
 
{
550
 
  objc_mutex_lock(__objc_runtime_mutex);
551
 
  __objc_is_multi_threaded = 1;
552
 
  __objc_runtime_threads_alive++;
553
 
  objc_mutex_unlock(__objc_runtime_mutex);
554
 
}
555
 
 
556
 
/* Make the objc thread system aware that a thread managed (started,
557
 
   stopped) by some external code will no longer access objc and thus
558
 
   can be forgotten by the objc thread system.  Call
559
 
   objc_thread_remove() when your alien thread is done with making
560
 
   calls to Objective-C. */
561
 
void
562
 
objc_thread_remove(void)
563
 
{
564
 
  objc_mutex_lock(__objc_runtime_mutex);
565
 
  __objc_runtime_threads_alive--;
566
 
  objc_mutex_unlock(__objc_runtime_mutex);
567
 
}
568
 
 
569
 
/* End of File */