~ubuntu-branches/ubuntu/precise/kompozer/precise

« back to all changes in this revision

Viewing changes to mozilla/gc/boehm/linux_threads.c

  • Committer: Bazaar Package Importer
  • Author(s): Anthony Yarusso
  • Date: 2007-08-27 01:11:03 UTC
  • Revision ID: james.westby@ubuntu.com-20070827011103-2jgf4s6532gqu2ka
Tags: upstream-0.7.10
ImportĀ upstreamĀ versionĀ 0.7.10

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* 
 
2
 * Copyright (c) 1994 by Xerox Corporation.  All rights reserved.
 
3
 * Copyright (c) 1996 by Silicon Graphics.  All rights reserved.
 
4
 * Copyright (c) 1998 by Fergus Henderson.  All rights reserved.
 
5
 *
 
6
 * THIS MATERIAL IS PROVIDED AS IS, WITH ABSOLUTELY NO WARRANTY EXPRESSED
 
7
 * OR IMPLIED.  ANY USE IS AT YOUR OWN RISK.
 
8
 *
 
9
 * Permission is hereby granted to use or copy this program
 
10
 * for any purpose,  provided the above notices are retained on all copies.
 
11
 * Permission to modify the code and to distribute modified code is granted,
 
12
 * provided the above notices are retained, and a notice that the code was
 
13
 * modified is included with the above copyright notice.
 
14
 */
 
15
/*
 
16
 * Support code for LinuxThreads, the clone()-based kernel
 
17
 * thread package for Linux which is included in libc6.
 
18
 *
 
19
 * This code relies on implementation details of LinuxThreads,
 
20
 * (i.e. properties not guaranteed by the Pthread standard):
 
21
 *
 
22
 *      - the function GC_linux_thread_top_of_stack(void)
 
23
 *        relies on the way LinuxThreads lays out thread stacks
 
24
 *        in the address space.
 
25
 *
 
26
 * Note that there is a lot of code duplication between linux_threads.c
 
27
 * and irix_threads.c; any changes made here may need to be reflected
 
28
 * there too.
 
29
 */
 
30
 
 
31
/* #define DEBUG_THREADS 1 */
 
32
 
 
33
/* ANSI C requires that a compilation unit contains something */
 
34
# include "gc_priv.h"
 
35
 
 
36
# if defined(LINUX_THREADS)
 
37
 
 
38
# include <pthread.h>
 
39
# include <time.h>
 
40
# include <errno.h>
 
41
# include <unistd.h>
 
42
# include <sys/mman.h>
 
43
# include <sys/time.h>
 
44
# include <semaphore.h>
 
45
 
 
46
#undef pthread_create
 
47
#undef pthread_sigmask
 
48
#undef pthread_join
 
49
 
 
50
void GC_thr_init();
 
51
 
 
52
#if 0
 
53
void GC_print_sig_mask()
 
54
{
 
55
    sigset_t blocked;
 
56
    int i;
 
57
 
 
58
    if (pthread_sigmask(SIG_BLOCK, NULL, &blocked) != 0)
 
59
        ABORT("pthread_sigmask");
 
60
    GC_printf0("Blocked: ");
 
61
    for (i = 1; i <= MAXSIG; i++) {
 
62
        if (sigismember(&blocked, i)) { GC_printf1("%ld ",(long) i); }
 
63
    }
 
64
    GC_printf0("\n");
 
65
}
 
66
#endif
 
67
 
 
68
/* We use the allocation lock to protect thread-related data structures. */
 
69
 
 
70
/* The set of all known threads.  We intercept thread creation and      */
 
71
/* joins.  We never actually create detached threads.  We allocate all  */
 
72
/* new thread stacks ourselves.  These allow us to maintain this        */
 
73
/* data structure.                                                      */
 
74
/* Protected by GC_thr_lock.                                            */
 
75
/* Some of this should be declared volatile, but that's incosnsistent   */
 
76
/* with some library routine declarations.                              */
 
77
typedef struct GC_Thread_Rep {
 
78
    struct GC_Thread_Rep * next;  /* More recently allocated threads    */
 
79
                                  /* with a given pthread id come       */
 
80
                                  /* first.  (All but the first are     */
 
81
                                  /* guaranteed to be dead, but we may  */
 
82
                                  /* not yet have registered the join.) */
 
83
    pthread_t id;
 
84
    word flags;
 
85
#       define FINISHED 1       /* Thread has exited.   */
 
86
#       define DETACHED 2       /* Thread is intended to be detached.   */
 
87
#       define MAIN_THREAD 4    /* True for the original thread only.   */
 
88
 
 
89
    ptr_t stack_end;
 
90
    ptr_t stack_ptr;            /* Valid only when stopped. */
 
91
    int signal;
 
92
    void * status;              /* The value returned from the thread.  */
 
93
                                /* Used only to avoid premature         */
 
94
                                /* reclamation of any data it might     */
 
95
                                /* reference.                           */
 
96
} * GC_thread;
 
97
 
 
98
GC_thread GC_lookup_thread(pthread_t id);
 
99
 
 
100
/*
 
101
 * The only way to suspend threads given the pthread interface is to send
 
102
 * signals.  We can't use SIGSTOP directly, because we need to get the
 
103
 * thread to save its stack pointer in the GC thread table before
 
104
 * suspending.  So we have to reserve a signal of our own for this.
 
105
 * This means we have to intercept client calls to change the signal mask.
 
106
 * The linuxthreads package already uses SIGUSR1 and SIGUSR2,
 
107
 * so we need to reuse something else.  I chose SIGPWR.
 
108
 * (Perhaps SIGUNUSED would be a better choice.)
 
109
 */
 
110
#define SIG_SUSPEND SIGPWR
 
111
 
 
112
#define SIG_RESTART SIGXCPU
 
113
 
 
114
sem_t GC_suspend_ack_sem;
 
115
 
 
116
/*
 
117
GC_linux_thread_top_of_stack() relies on implementation details of
 
118
LinuxThreads, namely that thread stacks are allocated on 2M boundaries
 
119
and grow to no more than 2M.
 
120
To make sure that we're using LinuxThreads and not some other thread
 
121
package, we generate a dummy reference to `__pthread_initial_thread_bos',
 
122
which is a symbol defined in LinuxThreads, but (hopefully) not in other
 
123
thread packages.
 
124
*/
 
125
#if 0
 
126
extern char * __pthread_initial_thread_bos;
 
127
char **dummy_var_to_force_linux_threads = &__pthread_initial_thread_bos;
 
128
#endif
 
129
 
 
130
#define LINUX_THREADS_STACK_SIZE  (2 * 1024 * 1024)
 
131
 
 
132
static inline ptr_t GC_linux_thread_top_of_stack(void)
 
133
{
 
134
  char *sp = GC_approx_sp();
 
135
  ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1);
 
136
#if DEBUG_THREADS
 
137
  GC_printf1("SP = %lx\n", (unsigned long)sp);
 
138
  GC_printf1("TOS = %lx\n", (unsigned long)tos);
 
139
#endif
 
140
  return tos;
 
141
}
 
142
 
 
143
void GC_suspend_handler(int sig)
 
144
{
 
145
    int dummy;
 
146
    pthread_t my_thread = pthread_self();
 
147
    GC_thread me;
 
148
    sigset_t all_sigs;
 
149
    sigset_t old_sigs;
 
150
    int i;
 
151
    sigset_t mask;
 
152
 
 
153
    if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
 
154
 
 
155
#if DEBUG_THREADS
 
156
    GC_printf1("Suspending 0x%x\n", my_thread);
 
157
#endif
 
158
 
 
159
    me = GC_lookup_thread(my_thread);
 
160
    /* The lookup here is safe, since I'm doing this on behalf  */
 
161
    /* of a thread which holds the allocation lock in order     */
 
162
    /* to stop the world.  Thus concurrent modification of the  */
 
163
    /* data structure is impossible.                            */
 
164
    me -> stack_ptr = (ptr_t)(&dummy);
 
165
    me -> stack_end = GC_linux_thread_top_of_stack();
 
166
 
 
167
    /* Tell the thread that wants to stop the world that this   */
 
168
    /* thread has been stopped.  Note that sem_post() is        */
 
169
    /* the only async-signal-safe primitive in LinuxThreads.    */
 
170
    sem_post(&GC_suspend_ack_sem);
 
171
 
 
172
    /* Wait until that thread tells us to restart by sending    */
 
173
    /* this thread a SIG_RESTART signal.                        */
 
174
    /* SIG_RESTART should be masked at this point.  Thus there  */
 
175
    /* is no race.                                              */
 
176
    if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
 
177
    if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed");
 
178
    do {
 
179
            me->signal = 0;
 
180
            sigsuspend(&mask);             /* Wait for signal */
 
181
    } while (me->signal != SIG_RESTART);
 
182
 
 
183
#if DEBUG_THREADS
 
184
    GC_printf1("Continuing 0x%x\n", my_thread);
 
185
#endif
 
186
}
 
187
 
 
188
void GC_restart_handler(int sig)
 
189
{
 
190
    GC_thread me;
 
191
 
 
192
    if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler");
 
193
 
 
194
    /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */
 
195
    /* The lookup here is safe, since I'm doing this on behalf  */
 
196
    /* of a thread which holds the allocation lock in order     */
 
197
    /* to stop the world.  Thus concurrent modification of the  */
 
198
    /* data structure is impossible.                            */
 
199
    me = GC_lookup_thread(pthread_self());
 
200
    me->signal = SIG_RESTART;
 
201
 
 
202
    /*
 
203
    ** Note: even if we didn't do anything useful here,
 
204
    ** it would still be necessary to have a signal handler,
 
205
    ** rather than ignoring the signals, otherwise
 
206
    ** the signals will not be delivered at all, and
 
207
    ** will thus not interrupt the sigsuspend() above.
 
208
    */
 
209
 
 
210
#if DEBUG_THREADS
 
211
    GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self());
 
212
#endif
 
213
}
 
214
 
 
215
GC_bool GC_thr_initialized = FALSE;
 
216
 
 
217
# define THREAD_TABLE_SZ 128    /* Must be power of 2   */
 
218
volatile GC_thread GC_threads[THREAD_TABLE_SZ];
 
219
 
 
220
/* Add a thread to GC_threads.  We assume it wasn't already there.      */
 
221
/* Caller holds allocation lock.                                        */
 
222
GC_thread GC_new_thread(pthread_t id)
 
223
{
 
224
    int hv = ((word)id) % THREAD_TABLE_SZ;
 
225
    GC_thread result;
 
226
    static struct GC_Thread_Rep first_thread;
 
227
    static GC_bool first_thread_used = FALSE;
 
228
    
 
229
    if (!first_thread_used) {
 
230
        result = &first_thread;
 
231
        first_thread_used = TRUE;
 
232
        /* Dont acquire allocation lock, since we may already hold it. */
 
233
    } else {
 
234
        result = (struct GC_Thread_Rep *)
 
235
                 GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
 
236
    }
 
237
    if (result == 0) return(0);
 
238
    result -> id = id;
 
239
    result -> next = GC_threads[hv];
 
240
    GC_threads[hv] = result;
 
241
    /* result -> flags = 0; */
 
242
    return(result);
 
243
}
 
244
 
 
245
/* Delete a thread from GC_threads.  We assume it is there.     */
 
246
/* (The code intentionally traps if it wasn't.)                 */
 
247
/* Caller holds allocation lock.                                */
 
248
void GC_delete_thread(pthread_t id)
 
249
{
 
250
    int hv = ((word)id) % THREAD_TABLE_SZ;
 
251
    register GC_thread p = GC_threads[hv];
 
252
    register GC_thread prev = 0;
 
253
    
 
254
    while (!pthread_equal(p -> id, id)) {
 
255
        prev = p;
 
256
        p = p -> next;
 
257
    }
 
258
    if (prev == 0) {
 
259
        GC_threads[hv] = p -> next;
 
260
    } else {
 
261
        prev -> next = p -> next;
 
262
    }
 
263
}
 
264
 
 
265
/* If a thread has been joined, but we have not yet             */
 
266
/* been notified, then there may be more than one thread        */
 
267
/* in the table with the same pthread id.                       */
 
268
/* This is OK, but we need a way to delete a specific one.      */
 
269
void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
 
270
{
 
271
    int hv = ((word)id) % THREAD_TABLE_SZ;
 
272
    register GC_thread p = GC_threads[hv];
 
273
    register GC_thread prev = 0;
 
274
 
 
275
    while (p != gc_id) {
 
276
        prev = p;
 
277
        p = p -> next;
 
278
    }
 
279
    if (prev == 0) {
 
280
        GC_threads[hv] = p -> next;
 
281
    } else {
 
282
        prev -> next = p -> next;
 
283
    }
 
284
}
 
285
 
 
286
/* Return a GC_thread corresponding to a given thread_t.        */
 
287
/* Returns 0 if it's not there.                                 */
 
288
/* Caller holds  allocation lock or otherwise inhibits          */
 
289
/* updates.                                                     */
 
290
/* If there is more than one thread with the given id we        */
 
291
/* return the most recent one.                                  */
 
292
GC_thread GC_lookup_thread(pthread_t id)
 
293
{
 
294
    int hv = ((word)id) % THREAD_TABLE_SZ;
 
295
    register GC_thread p = GC_threads[hv];
 
296
    
 
297
    while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
 
298
    return(p);
 
299
}
 
300
 
 
301
/* Caller holds allocation lock.        */
 
302
void GC_stop_world()
 
303
{
 
304
    pthread_t my_thread = pthread_self();
 
305
    register int i;
 
306
    register GC_thread p;
 
307
    register int n_live_threads = 0;
 
308
    register int result;
 
309
 
 
310
    for (i = 0; i < THREAD_TABLE_SZ; i++) {
 
311
      for (p = GC_threads[i]; p != 0; p = p -> next) {
 
312
        if (p -> id != my_thread) {
 
313
            if (p -> flags & FINISHED) continue;
 
314
            n_live_threads++;
 
315
            #if DEBUG_THREADS
 
316
              GC_printf1("Sending suspend signal to 0x%x\n", p -> id);
 
317
            #endif
 
318
            result = pthread_kill(p -> id, SIG_SUSPEND);
 
319
            switch(result) {
 
320
                case ESRCH:
 
321
                    /* Not really there anymore.  Possible? */
 
322
                    n_live_threads--;
 
323
                    break;
 
324
                case 0:
 
325
                    break;
 
326
                default:
 
327
                    ABORT("pthread_kill failed");
 
328
            }
 
329
        }
 
330
      }
 
331
    }
 
332
    for (i = 0; i < n_live_threads; i++) {
 
333
        sem_wait(&GC_suspend_ack_sem);
 
334
    }
 
335
    #if DEBUG_THREADS
 
336
    GC_printf1("World stopped 0x%x\n", pthread_self());
 
337
    #endif
 
338
}
 
339
 
 
340
/* Caller holds allocation lock.        */
 
341
void GC_start_world()
 
342
{
 
343
    pthread_t my_thread = pthread_self();
 
344
    register int i;
 
345
    register GC_thread p;
 
346
    register int n_live_threads = 0;
 
347
    register int result;
 
348
    
 
349
#   if DEBUG_THREADS
 
350
      GC_printf0("World starting\n");
 
351
#   endif
 
352
 
 
353
    for (i = 0; i < THREAD_TABLE_SZ; i++) {
 
354
      for (p = GC_threads[i]; p != 0; p = p -> next) {
 
355
        if (p -> id != my_thread) {
 
356
            if (p -> flags & FINISHED) continue;
 
357
            n_live_threads++;
 
358
            #if DEBUG_THREADS
 
359
              GC_printf1("Sending restart signal to 0x%x\n", p -> id);
 
360
            #endif
 
361
            result = pthread_kill(p -> id, SIG_RESTART);
 
362
            switch(result) {
 
363
                case ESRCH:
 
364
                    /* Not really there anymore.  Possible? */
 
365
                    n_live_threads--;
 
366
                    break;
 
367
                case 0:
 
368
                    break;
 
369
                default:
 
370
                    ABORT("pthread_kill failed");
 
371
            }
 
372
        }
 
373
      }
 
374
    }
 
375
    #if DEBUG_THREADS
 
376
      GC_printf0("World started\n");
 
377
    #endif
 
378
}
 
379
 
 
380
/* We hold allocation lock.  We assume the world is stopped.    */
 
381
void GC_push_all_stacks()
 
382
{
 
383
    register int i;
 
384
    register GC_thread p;
 
385
    register ptr_t sp = GC_approx_sp();
 
386
    register ptr_t lo, hi;
 
387
    pthread_t me = pthread_self();
 
388
    
 
389
    if (!GC_thr_initialized) GC_thr_init();
 
390
    #if DEBUG_THREADS
 
391
        GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
 
392
    #endif
 
393
    for (i = 0; i < THREAD_TABLE_SZ; i++) {
 
394
      for (p = GC_threads[i]; p != 0; p = p -> next) {
 
395
        if (p -> flags & FINISHED) continue;
 
396
        if (pthread_equal(p -> id, me)) {
 
397
            lo = GC_approx_sp();
 
398
        } else {
 
399
            lo = p -> stack_ptr;
 
400
        }
 
401
        if ((p -> flags & MAIN_THREAD) == 0) {
 
402
            if (pthread_equal(p -> id, me)) {
 
403
                hi = GC_linux_thread_top_of_stack();
 
404
            } else {
 
405
                hi = p -> stack_end;
 
406
            }
 
407
        } else {
 
408
            /* The original stack. */
 
409
            hi = GC_stackbottom;
 
410
        }
 
411
        #if DEBUG_THREADS
 
412
            GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
 
413
                (unsigned long) p -> id,
 
414
                (unsigned long) lo, (unsigned long) hi);
 
415
        #endif
 
416
        GC_push_all_stack(lo, hi);
 
417
      }
 
418
    }
 
419
}
 
420
 
 
421
 
 
422
/* We hold the allocation lock. */
 
423
void GC_thr_init()
 
424
{
 
425
    GC_thread t;
 
426
    struct sigaction act;
 
427
 
 
428
    if (GC_thr_initialized) return;
 
429
    GC_thr_initialized = TRUE;
 
430
 
 
431
    if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
 
432
        ABORT("sem_init failed");
 
433
 
 
434
    act.sa_flags = SA_RESTART;
 
435
    if (sigfillset(&act.sa_mask) != 0) {
 
436
        ABORT("sigfillset() failed");
 
437
    }
 
438
    /* SIG_RESTART is unmasked by the handler when necessary.   */
 
439
    act.sa_handler = GC_suspend_handler;
 
440
    if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
 
441
        ABORT("Cannot set SIG_SUSPEND handler");
 
442
    }
 
443
 
 
444
    act.sa_handler = GC_restart_handler;
 
445
    if (sigaction(SIG_RESTART, &act, NULL) != 0) {
 
446
        ABORT("Cannot set SIG_SUSPEND handler");
 
447
    }
 
448
 
 
449
    /* Add the initial thread, so we can stop it.       */
 
450
      t = GC_new_thread(pthread_self());
 
451
      t -> stack_ptr = 0;
 
452
      t -> flags = DETACHED | MAIN_THREAD;
 
453
}
 
454
 
 
455
int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
 
456
{
 
457
    sigset_t fudged_set;
 
458
    
 
459
    if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
 
460
        fudged_set = *set;
 
461
        sigdelset(&fudged_set, SIG_SUSPEND);
 
462
        set = &fudged_set;
 
463
    }
 
464
    return(pthread_sigmask(how, set, oset));
 
465
}
 
466
 
 
467
struct start_info {
 
468
    void *(*start_routine)(void *);
 
469
    void *arg;
 
470
    word flags;
 
471
    sem_t registered;           /* 1 ==> in our thread table, but       */
 
472
                                /* parent hasn't yet noticed.           */
 
473
};
 
474
 
 
475
 
 
476
void GC_thread_exit_proc(void *arg)
 
477
{
 
478
    GC_thread me;
 
479
    struct start_info * si = arg;
 
480
 
 
481
    LOCK();
 
482
    me = GC_lookup_thread(pthread_self());
 
483
    if (me -> flags & DETACHED) {
 
484
        GC_delete_thread(pthread_self());
 
485
    } else {
 
486
        me -> flags |= FINISHED;
 
487
    }
 
488
    UNLOCK();
 
489
}
 
490
 
 
491
int GC_pthread_join(pthread_t thread, void **retval)
 
492
{
 
493
    int result;
 
494
    GC_thread thread_gc_id;
 
495
    
 
496
    LOCK();
 
497
    thread_gc_id = GC_lookup_thread(thread);
 
498
    /* This is guaranteed to be the intended one, since the thread id   */
 
499
    /* cant have been recycled by pthreads.                             */
 
500
    UNLOCK();
 
501
    result = pthread_join(thread, retval);
 
502
    LOCK();
 
503
    /* Here the pthread thread id may have been recycled. */
 
504
    GC_delete_gc_thread(thread, thread_gc_id);
 
505
    UNLOCK();
 
506
    return result;
 
507
}
 
508
 
 
509
void * GC_start_routine(void * arg)
 
510
{
 
511
    struct start_info * si = arg;
 
512
    void * result;
 
513
    GC_thread me;
 
514
    pthread_t my_pthread;
 
515
    void *(*start)(void *);
 
516
    void *start_arg;
 
517
 
 
518
    my_pthread = pthread_self();
 
519
    LOCK();
 
520
    me = GC_new_thread(my_pthread);
 
521
    me -> flags = si -> flags;
 
522
    me -> stack_ptr = 0;
 
523
    me -> stack_end = 0;
 
524
    UNLOCK();
 
525
    start = si -> start_routine;
 
526
    start_arg = si -> arg;
 
527
    sem_post(&(si -> registered));
 
528
    pthread_cleanup_push(GC_thread_exit_proc, si);
 
529
#   ifdef DEBUG_THREADS
 
530
        GC_printf1("Starting thread 0x%lx\n", pthread_self());
 
531
        GC_printf1("pid = %ld\n", (long) getpid());
 
532
        GC_printf1("sp = 0x%lx\n", (long) &arg);
 
533
        GC_printf1("start_routine = 0x%lx\n", start);
 
534
#   endif
 
535
    result = (*start)(start_arg);
 
536
#if DEBUG_THREADS
 
537
        GC_printf1("Finishing thread 0x%x\n", pthread_self());
 
538
#endif
 
539
    me -> status = result;
 
540
    me -> flags |= FINISHED;
 
541
    pthread_cleanup_pop(1);
 
542
    /* Cleanup acquires lock, ensuring that we can't exit               */
 
543
    /* while a collection that thinks we're alive is trying to stop     */
 
544
    /* us.                                                              */
 
545
    return(result);
 
546
}
 
547
 
 
548
int
 
549
GC_pthread_create(pthread_t *new_thread,
 
550
                  const pthread_attr_t *attr,
 
551
                  void *(*start_routine)(void *), void *arg)
 
552
{
 
553
    int result;
 
554
    GC_thread t;
 
555
    pthread_t my_new_thread;
 
556
    void * stack;
 
557
    size_t stacksize;
 
558
    pthread_attr_t new_attr;
 
559
    int detachstate;
 
560
    word my_flags = 0;
 
561
    struct start_info * si = GC_malloc(sizeof(struct start_info)); 
 
562
        /* This is otherwise saved only in an area mmapped by the thread */
 
563
        /* library, which isn't visible to the collector.                */
 
564
 
 
565
    if (0 == si) return(ENOMEM);
 
566
    sem_init(&(si -> registered), 0, 0);
 
567
    si -> start_routine = start_routine;
 
568
    si -> arg = arg;
 
569
    LOCK();
 
570
    if (!GC_thr_initialized) GC_thr_init();
 
571
    if (NULL == attr) {
 
572
        stack = 0;
 
573
        (void) pthread_attr_init(&new_attr);
 
574
    } else {
 
575
        new_attr = *attr;
 
576
    }
 
577
    pthread_attr_getdetachstate(&new_attr, &detachstate);
 
578
    if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
 
579
    si -> flags = my_flags;
 
580
    UNLOCK();
 
581
    result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
 
582
    /* Wait until child has been added to the thread table.             */
 
583
    /* This also ensures that we hold onto si until the child is done   */
 
584
    /* with it.  Thus it doesn't matter whether it is otherwise         */
 
585
    /* visible to the collector.                                        */
 
586
        if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed");
 
587
        sem_destroy(&(si -> registered));
 
588
    /* pthread_attr_destroy(&new_attr); */
 
589
    /* pthread_attr_destroy(&new_attr); */
 
590
    return(result);
 
591
}
 
592
 
 
593
GC_bool GC_collecting = 0;
 
594
                        /* A hint that we're in the collector and       */
 
595
                        /* holding the allocation lock for an           */
 
596
                        /* extended period.                             */
 
597
 
 
598
/* Reasonably fast spin locks.  Basically the same implementation */
 
599
/* as STL alloc.h.  This isn't really the right way to do this.   */
 
600
/* but until the POSIX scheduling mess gets straightened out ...  */
 
601
 
 
602
volatile unsigned int GC_allocate_lock = 0;
 
603
 
 
604
 
 
605
void GC_lock()
 
606
{
 
607
#   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
 
608
#   define high_spin_max 1000 /* spin cycles for multiprocessor */
 
609
    static unsigned spin_max = low_spin_max;
 
610
    unsigned my_spin_max;
 
611
    static unsigned last_spins = 0;
 
612
    unsigned my_last_spins;
 
613
    volatile unsigned junk;
 
614
#   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
 
615
    int i;
 
616
 
 
617
    if (!GC_test_and_set(&GC_allocate_lock)) {
 
618
        return;
 
619
    }
 
620
    junk = 0;
 
621
    my_spin_max = spin_max;
 
622
    my_last_spins = last_spins;
 
623
    for (i = 0; i < my_spin_max; i++) {
 
624
        if (GC_collecting) goto yield;
 
625
        if (i < my_last_spins/2 || GC_allocate_lock) {
 
626
            PAUSE; 
 
627
            continue;
 
628
        }
 
629
        if (!GC_test_and_set(&GC_allocate_lock)) {
 
630
            /*
 
631
             * got it!
 
632
             * Spinning worked.  Thus we're probably not being scheduled
 
633
             * against the other process with which we were contending.
 
634
             * Thus it makes sense to spin longer the next time.
 
635
             */
 
636
            last_spins = i;
 
637
            spin_max = high_spin_max;
 
638
            return;
 
639
        }
 
640
    }
 
641
    /* We are probably being scheduled against the other process.  Sleep. */
 
642
    spin_max = low_spin_max;
 
643
yield:
 
644
    for (i = 0;; ++i) {
 
645
        if (!GC_test_and_set(&GC_allocate_lock)) {
 
646
            return;
 
647
        }
 
648
#       define SLEEP_THRESHOLD 12
 
649
                /* nanosleep(<= 2ms) just spins under Linux.  We        */
 
650
                /* want to be careful to avoid that behavior.           */
 
651
        if (i < SLEEP_THRESHOLD) {
 
652
            sched_yield();
 
653
        } else {
 
654
            struct timespec ts;
 
655
        
 
656
            if (i > 26) i = 26;
 
657
                        /* Don't wait for more than about 60msecs, even */
 
658
                        /* under extreme contention.                    */
 
659
            ts.tv_sec = 0;
 
660
            ts.tv_nsec = 1 << i;
 
661
            nanosleep(&ts, 0);
 
662
        }
 
663
    }
 
664
}
 
665
 
 
666
# endif /* LINUX_THREADS */
 
667