Removed extra reference to packihx
[fw/sdcc] / support / gc / linux_threads.c
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_kill_other_threads_np'
122 (was `__pthread_initial_thread_bos' but that disappeared),
123 which is a symbol defined in LinuxThreads, but (hopefully) not in other
124 thread packages.
125 */
126 void (*dummy_var_to_force_linux_threads)() = pthread_kill_other_threads_np;
127
128 #define LINUX_THREADS_STACK_SIZE  (2 * 1024 * 1024)
129
130 static inline ptr_t GC_linux_thread_top_of_stack(void)
131 {
132   char *sp = GC_approx_sp();
133   ptr_t tos = (ptr_t) (((unsigned long)sp | (LINUX_THREADS_STACK_SIZE - 1)) + 1);
134 #if DEBUG_THREADS
135   GC_printf1("SP = %lx\n", (unsigned long)sp);
136   GC_printf1("TOS = %lx\n", (unsigned long)tos);
137 #endif
138   return tos;
139 }
140
141 void GC_suspend_handler(int sig)
142 {
143     int dummy;
144     pthread_t my_thread = pthread_self();
145     GC_thread me;
146     sigset_t all_sigs;
147     sigset_t old_sigs;
148     int i;
149     sigset_t mask;
150
151     if (sig != SIG_SUSPEND) ABORT("Bad signal in suspend_handler");
152
153 #if DEBUG_THREADS
154     GC_printf1("Suspending 0x%x\n", my_thread);
155 #endif
156
157     me = GC_lookup_thread(my_thread);
158     /* The lookup here is safe, since I'm doing this on behalf  */
159     /* of a thread which holds the allocation lock in order     */
160     /* to stop the world.  Thus concurrent modification of the  */
161     /* data structure is impossible.                            */
162     me -> stack_ptr = (ptr_t)(&dummy);
163     me -> stack_end = GC_linux_thread_top_of_stack();
164
165     /* Tell the thread that wants to stop the world that this   */
166     /* thread has been stopped.  Note that sem_post() is        */
167     /* the only async-signal-safe primitive in LinuxThreads.    */
168     sem_post(&GC_suspend_ack_sem);
169
170     /* Wait until that thread tells us to restart by sending    */
171     /* this thread a SIG_RESTART signal.                        */
172     /* SIG_RESTART should be masked at this point.  Thus there  */
173     /* is no race.                                              */
174     if (sigfillset(&mask) != 0) ABORT("sigfillset() failed");
175     if (sigdelset(&mask, SIG_RESTART) != 0) ABORT("sigdelset() failed");
176     do {
177             me->signal = 0;
178             sigsuspend(&mask);             /* Wait for signal */
179     } while (me->signal != SIG_RESTART);
180
181 #if DEBUG_THREADS
182     GC_printf1("Continuing 0x%x\n", my_thread);
183 #endif
184 }
185
186 void GC_restart_handler(int sig)
187 {
188     GC_thread me;
189
190     if (sig != SIG_RESTART) ABORT("Bad signal in suspend_handler");
191
192     /* Let the GC_suspend_handler() know that we got a SIG_RESTART. */
193     /* The lookup here is safe, since I'm doing this on behalf  */
194     /* of a thread which holds the allocation lock in order     */
195     /* to stop the world.  Thus concurrent modification of the  */
196     /* data structure is impossible.                            */
197     me = GC_lookup_thread(pthread_self());
198     me->signal = SIG_RESTART;
199
200     /*
201     ** Note: even if we didn't do anything useful here,
202     ** it would still be necessary to have a signal handler,
203     ** rather than ignoring the signals, otherwise
204     ** the signals will not be delivered at all, and
205     ** will thus not interrupt the sigsuspend() above.
206     */
207
208 #if DEBUG_THREADS
209     GC_printf1("In GC_restart_handler for 0x%x\n", pthread_self());
210 #endif
211 }
212
213 GC_bool GC_thr_initialized = FALSE;
214
215 # define THREAD_TABLE_SZ 128    /* Must be power of 2   */
216 volatile GC_thread GC_threads[THREAD_TABLE_SZ];
217
218 /* Add a thread to GC_threads.  We assume it wasn't already there.      */
219 /* Caller holds allocation lock.                                        */
220 GC_thread GC_new_thread(pthread_t id)
221 {
222     int hv = ((word)id) % THREAD_TABLE_SZ;
223     GC_thread result;
224     static struct GC_Thread_Rep first_thread;
225     static GC_bool first_thread_used = FALSE;
226     
227     if (!first_thread_used) {
228         result = &first_thread;
229         first_thread_used = TRUE;
230         /* Dont acquire allocation lock, since we may already hold it. */
231     } else {
232         result = (struct GC_Thread_Rep *)
233                  GC_generic_malloc_inner(sizeof(struct GC_Thread_Rep), NORMAL);
234     }
235     if (result == 0) return(0);
236     result -> id = id;
237     result -> next = GC_threads[hv];
238     GC_threads[hv] = result;
239     /* result -> flags = 0; */
240     return(result);
241 }
242
243 /* Delete a thread from GC_threads.  We assume it is there.     */
244 /* (The code intentionally traps if it wasn't.)                 */
245 /* Caller holds allocation lock.                                */
246 void GC_delete_thread(pthread_t id)
247 {
248     int hv = ((word)id) % THREAD_TABLE_SZ;
249     register GC_thread p = GC_threads[hv];
250     register GC_thread prev = 0;
251     
252     while (!pthread_equal(p -> id, id)) {
253         prev = p;
254         p = p -> next;
255     }
256     if (prev == 0) {
257         GC_threads[hv] = p -> next;
258     } else {
259         prev -> next = p -> next;
260     }
261 }
262
263 /* If a thread has been joined, but we have not yet             */
264 /* been notified, then there may be more than one thread        */
265 /* in the table with the same pthread id.                       */
266 /* This is OK, but we need a way to delete a specific one.      */
267 void GC_delete_gc_thread(pthread_t id, GC_thread gc_id)
268 {
269     int hv = ((word)id) % THREAD_TABLE_SZ;
270     register GC_thread p = GC_threads[hv];
271     register GC_thread prev = 0;
272
273     while (p != gc_id) {
274         prev = p;
275         p = p -> next;
276     }
277     if (prev == 0) {
278         GC_threads[hv] = p -> next;
279     } else {
280         prev -> next = p -> next;
281     }
282 }
283
284 /* Return a GC_thread corresponding to a given thread_t.        */
285 /* Returns 0 if it's not there.                                 */
286 /* Caller holds  allocation lock or otherwise inhibits          */
287 /* updates.                                                     */
288 /* If there is more than one thread with the given id we        */
289 /* return the most recent one.                                  */
290 GC_thread GC_lookup_thread(pthread_t id)
291 {
292     int hv = ((word)id) % THREAD_TABLE_SZ;
293     register GC_thread p = GC_threads[hv];
294     
295     while (p != 0 && !pthread_equal(p -> id, id)) p = p -> next;
296     return(p);
297 }
298
299 /* Caller holds allocation lock.        */
300 void GC_stop_world()
301 {
302     pthread_t my_thread = pthread_self();
303     register int i;
304     register GC_thread p;
305     register int n_live_threads = 0;
306     register int result;
307
308     for (i = 0; i < THREAD_TABLE_SZ; i++) {
309       for (p = GC_threads[i]; p != 0; p = p -> next) {
310         if (p -> id != my_thread) {
311             if (p -> flags & FINISHED) continue;
312             n_live_threads++;
313             #if DEBUG_THREADS
314               GC_printf1("Sending suspend signal to 0x%x\n", p -> id);
315             #endif
316             result = pthread_kill(p -> id, SIG_SUSPEND);
317             switch(result) {
318                 case ESRCH:
319                     /* Not really there anymore.  Possible? */
320                     n_live_threads--;
321                     break;
322                 case 0:
323                     break;
324                 default:
325                     ABORT("pthread_kill failed");
326             }
327         }
328       }
329     }
330     for (i = 0; i < n_live_threads; i++) {
331         sem_wait(&GC_suspend_ack_sem);
332     }
333     #if DEBUG_THREADS
334     GC_printf1("World stopped 0x%x\n", pthread_self());
335     #endif
336 }
337
338 /* Caller holds allocation lock.        */
339 void GC_start_world()
340 {
341     pthread_t my_thread = pthread_self();
342     register int i;
343     register GC_thread p;
344     register int n_live_threads = 0;
345     register int result;
346     
347 #   if DEBUG_THREADS
348       GC_printf0("World starting\n");
349 #   endif
350
351     for (i = 0; i < THREAD_TABLE_SZ; i++) {
352       for (p = GC_threads[i]; p != 0; p = p -> next) {
353         if (p -> id != my_thread) {
354             if (p -> flags & FINISHED) continue;
355             n_live_threads++;
356             #if DEBUG_THREADS
357               GC_printf1("Sending restart signal to 0x%x\n", p -> id);
358             #endif
359             result = pthread_kill(p -> id, SIG_RESTART);
360             switch(result) {
361                 case ESRCH:
362                     /* Not really there anymore.  Possible? */
363                     n_live_threads--;
364                     break;
365                 case 0:
366                     break;
367                 default:
368                     ABORT("pthread_kill failed");
369             }
370         }
371       }
372     }
373     #if DEBUG_THREADS
374       GC_printf0("World started\n");
375     #endif
376 }
377
378 /* We hold allocation lock.  We assume the world is stopped.    */
379 void GC_push_all_stacks()
380 {
381     register int i;
382     register GC_thread p;
383     register ptr_t sp = GC_approx_sp();
384     register ptr_t lo, hi;
385     pthread_t me = pthread_self();
386     
387     if (!GC_thr_initialized) GC_thr_init();
388     #if DEBUG_THREADS
389         GC_printf1("Pushing stacks from thread 0x%lx\n", (unsigned long) me);
390     #endif
391     for (i = 0; i < THREAD_TABLE_SZ; i++) {
392       for (p = GC_threads[i]; p != 0; p = p -> next) {
393         if (p -> flags & FINISHED) continue;
394         if (pthread_equal(p -> id, me)) {
395             lo = GC_approx_sp();
396         } else {
397             lo = p -> stack_ptr;
398         }
399         if ((p -> flags & MAIN_THREAD) == 0) {
400             if (pthread_equal(p -> id, me)) {
401                 hi = GC_linux_thread_top_of_stack();
402             } else {
403                 hi = p -> stack_end;
404             }
405         } else {
406             /* The original stack. */
407             hi = GC_stackbottom;
408         }
409         #if DEBUG_THREADS
410             GC_printf3("Stack for thread 0x%lx = [%lx,%lx)\n",
411                 (unsigned long) p -> id,
412                 (unsigned long) lo, (unsigned long) hi);
413         #endif
414         GC_push_all_stack(lo, hi);
415       }
416     }
417 }
418
419
420 /* We hold the allocation lock. */
421 void GC_thr_init()
422 {
423     GC_thread t;
424     struct sigaction act;
425
426     if (GC_thr_initialized) return;
427     GC_thr_initialized = TRUE;
428
429     if (sem_init(&GC_suspend_ack_sem, 0, 0) != 0)
430         ABORT("sem_init failed");
431
432     act.sa_flags = SA_RESTART;
433     if (sigfillset(&act.sa_mask) != 0) {
434         ABORT("sigfillset() failed");
435     }
436     /* SIG_RESTART is unmasked by the handler when necessary.   */
437     act.sa_handler = GC_suspend_handler;
438     if (sigaction(SIG_SUSPEND, &act, NULL) != 0) {
439         ABORT("Cannot set SIG_SUSPEND handler");
440     }
441
442     act.sa_handler = GC_restart_handler;
443     if (sigaction(SIG_RESTART, &act, NULL) != 0) {
444         ABORT("Cannot set SIG_SUSPEND handler");
445     }
446
447     /* Add the initial thread, so we can stop it.       */
448       t = GC_new_thread(pthread_self());
449       t -> stack_ptr = 0;
450       t -> flags = DETACHED | MAIN_THREAD;
451 }
452
453 int GC_pthread_sigmask(int how, const sigset_t *set, sigset_t *oset)
454 {
455     sigset_t fudged_set;
456     
457     if (set != NULL && (how == SIG_BLOCK || how == SIG_SETMASK)) {
458         fudged_set = *set;
459         sigdelset(&fudged_set, SIG_SUSPEND);
460         set = &fudged_set;
461     }
462     return(pthread_sigmask(how, set, oset));
463 }
464
465 struct start_info {
466     void *(*start_routine)(void *);
467     void *arg;
468     word flags;
469     sem_t registered;           /* 1 ==> in our thread table, but       */
470                                 /* parent hasn't yet noticed.           */
471 };
472
473
474 void GC_thread_exit_proc(void *arg)
475 {
476     GC_thread me;
477     struct start_info * si = arg;
478
479     LOCK();
480     me = GC_lookup_thread(pthread_self());
481     if (me -> flags & DETACHED) {
482         GC_delete_thread(pthread_self());
483     } else {
484         me -> flags |= FINISHED;
485     }
486     UNLOCK();
487 }
488
489 int GC_pthread_join(pthread_t thread, void **retval)
490 {
491     int result;
492     GC_thread thread_gc_id;
493     
494     LOCK();
495     thread_gc_id = GC_lookup_thread(thread);
496     /* This is guaranteed to be the intended one, since the thread id   */
497     /* cant have been recycled by pthreads.                             */
498     UNLOCK();
499     result = pthread_join(thread, retval);
500     LOCK();
501     /* Here the pthread thread id may have been recycled. */
502     GC_delete_gc_thread(thread, thread_gc_id);
503     UNLOCK();
504     return result;
505 }
506
507 void * GC_start_routine(void * arg)
508 {
509     struct start_info * si = arg;
510     void * result;
511     GC_thread me;
512     pthread_t my_pthread;
513     void *(*start)(void *);
514     void *start_arg;
515
516     my_pthread = pthread_self();
517     LOCK();
518     me = GC_new_thread(my_pthread);
519     me -> flags = si -> flags;
520     me -> stack_ptr = 0;
521     me -> stack_end = 0;
522     UNLOCK();
523     start = si -> start_routine;
524     start_arg = si -> arg;
525     sem_post(&(si -> registered));
526     pthread_cleanup_push(GC_thread_exit_proc, si);
527 #   ifdef DEBUG_THREADS
528         GC_printf1("Starting thread 0x%lx\n", pthread_self());
529         GC_printf1("pid = %ld\n", (long) getpid());
530         GC_printf1("sp = 0x%lx\n", (long) &arg);
531         GC_printf1("start_routine = 0x%lx\n", start);
532 #   endif
533     result = (*start)(start_arg);
534 #if DEBUG_THREADS
535         GC_printf1("Finishing thread 0x%x\n", pthread_self());
536 #endif
537     me -> status = result;
538     me -> flags |= FINISHED;
539     pthread_cleanup_pop(1);
540     /* Cleanup acquires lock, ensuring that we can't exit               */
541     /* while a collection that thinks we're alive is trying to stop     */
542     /* us.                                                              */
543     return(result);
544 }
545
546 int
547 GC_pthread_create(pthread_t *new_thread,
548                   const pthread_attr_t *attr,
549                   void *(*start_routine)(void *), void *arg)
550 {
551     int result;
552     GC_thread t;
553     pthread_t my_new_thread;
554     void * stack;
555     size_t stacksize;
556     pthread_attr_t new_attr;
557     int detachstate;
558     word my_flags = 0;
559     struct start_info * si = GC_malloc(sizeof(struct start_info)); 
560         /* This is otherwise saved only in an area mmapped by the thread */
561         /* library, which isn't visible to the collector.                */
562
563     if (0 == si) return(ENOMEM);
564     sem_init(&(si -> registered), 0, 0);
565     si -> start_routine = start_routine;
566     si -> arg = arg;
567     LOCK();
568     if (!GC_thr_initialized) GC_thr_init();
569     if (NULL == attr) {
570         stack = 0;
571         (void) pthread_attr_init(&new_attr);
572     } else {
573         new_attr = *attr;
574     }
575     pthread_attr_getdetachstate(&new_attr, &detachstate);
576     if (PTHREAD_CREATE_DETACHED == detachstate) my_flags |= DETACHED;
577     si -> flags = my_flags;
578     UNLOCK();
579     result = pthread_create(new_thread, &new_attr, GC_start_routine, si);
580     /* Wait until child has been added to the thread table.             */
581     /* This also ensures that we hold onto si until the child is done   */
582     /* with it.  Thus it doesn't matter whether it is otherwise         */
583     /* visible to the collector.                                        */
584         if (0 != sem_wait(&(si -> registered))) ABORT("sem_wait failed");
585         sem_destroy(&(si -> registered));
586     /* pthread_attr_destroy(&new_attr); */
587     /* pthread_attr_destroy(&new_attr); */
588     return(result);
589 }
590
591 GC_bool GC_collecting = 0;
592                         /* A hint that we're in the collector and       */
593                         /* holding the allocation lock for an           */
594                         /* extended period.                             */
595
596 /* Reasonably fast spin locks.  Basically the same implementation */
597 /* as STL alloc.h.  This isn't really the right way to do this.   */
598 /* but until the POSIX scheduling mess gets straightened out ...  */
599
600 volatile unsigned int GC_allocate_lock = 0;
601
602
603 void GC_lock()
604 {
605 #   define low_spin_max 30  /* spin cycles if we suspect uniprocessor */
606 #   define high_spin_max 1000 /* spin cycles for multiprocessor */
607     static unsigned spin_max = low_spin_max;
608     unsigned my_spin_max;
609     static unsigned last_spins = 0;
610     unsigned my_last_spins;
611     volatile unsigned junk;
612 #   define PAUSE junk *= junk; junk *= junk; junk *= junk; junk *= junk
613     int i;
614
615     if (!GC_test_and_set(&GC_allocate_lock)) {
616         return;
617     }
618     junk = 0;
619     my_spin_max = spin_max;
620     my_last_spins = last_spins;
621     for (i = 0; i < my_spin_max; i++) {
622         if (GC_collecting) goto yield;
623         if (i < my_last_spins/2 || GC_allocate_lock) {
624             PAUSE; 
625             continue;
626         }
627         if (!GC_test_and_set(&GC_allocate_lock)) {
628             /*
629              * got it!
630              * Spinning worked.  Thus we're probably not being scheduled
631              * against the other process with which we were contending.
632              * Thus it makes sense to spin longer the next time.
633              */
634             last_spins = i;
635             spin_max = high_spin_max;
636             return;
637         }
638     }
639     /* We are probably being scheduled against the other process.  Sleep. */
640     spin_max = low_spin_max;
641 yield:
642     for (i = 0;; ++i) {
643         if (!GC_test_and_set(&GC_allocate_lock)) {
644             return;
645         }
646 #       define SLEEP_THRESHOLD 12
647                 /* nanosleep(<= 2ms) just spins under Linux.  We        */
648                 /* want to be careful to avoid that behavior.           */
649         if (i < SLEEP_THRESHOLD) {
650             sched_yield();
651         } else {
652             struct timespec ts;
653         
654             if (i > 26) i = 26;
655                         /* Don't wait for more than about 60msecs, even */
656                         /* under extreme contention.                    */
657             ts.tv_sec = 0;
658             ts.tv_nsec = 1 << i;
659             nanosleep(&ts, 0);
660         }
661     }
662 }
663
664 # endif /* LINUX_THREADS */
665