diff options
author | Michael I. Bushnell <mib@gnu.org> | 1992-10-06 18:31:16 +0000 |
---|---|---|
committer | Michael I. Bushnell <mib@gnu.org> | 1992-10-06 18:31:16 +0000 |
commit | da328cb2c579c5b60725cbba903eff753061e17a (patch) | |
tree | c8f6634ad0298e628fa96bbd94f58dd504ef5d55 /libthreads/cthreads.c | |
parent | 964ab87456cf79f0f5787796c3e8917502d9f9d8 (diff) |
Initial revision
Diffstat (limited to 'libthreads/cthreads.c')
-rw-r--r-- | libthreads/cthreads.c | 451 |
1 files changed, 451 insertions, 0 deletions
diff --git a/libthreads/cthreads.c b/libthreads/cthreads.c new file mode 100644 index 00000000..1964c335 --- /dev/null +++ b/libthreads/cthreads.c @@ -0,0 +1,451 @@ +/* + * Mach Operating System + * Copyright (c) 1991,1990,1989 Carnegie Mellon University + * All Rights Reserved. + * + * Permission to use, copy, modify and distribute this software and its + * documentation is hereby granted, provided that both the copyright + * notice and this permission notice appear in all copies of the + * software, derivative works or modified versions, and any portions + * thereof, and that both notices appear in supporting documentation. + * + * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" + * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR + * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. + * + * Carnegie Mellon requests users of this software to return to + * + * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU + * School of Computer Science + * Carnegie Mellon University + * Pittsburgh PA 15213-3890 + * + * any improvements or extensions that they make and grant Carnegie Mellon + * the rights to redistribute these changes. + */ +/* + * HISTORY + * $Log: cthreads.c,v $ + * Revision 2.11 92/07/20 13:33:37 cmaeda + * In cthread_init, do machine dependent initialization if it's defined. + * [92/05/11 14:41:08 cmaeda] + * + * Revision 2.10 91/08/28 11:19:26 jsb + * Fixed mig_init initialization in cthread_fork_child. + * [91/08/23 rpd] + * + * Revision 2.9 91/07/31 18:34:23 dbg + * Fix bad self-pointer reference. + * + * Don't declare _setjmp and _longjmp; they are included by + * cthreads.h. + * [91/07/30 17:33:50 dbg] + * + * Revision 2.8 91/05/14 17:56:31 mrt + * Correcting copyright + * + * Revision 2.7 91/02/14 14:19:47 mrt + * Added new Mach copyright + * [91/02/13 12:41:07 mrt] + * + * Revision 2.6 90/11/05 14:37:03 rpd + * Added cthread_fork_{prepare,parent,child}. + * [90/11/02 rwd] + * + * Add spin_lock_t. + * [90/10/31 rwd] + * + * Revision 2.5 90/08/07 14:30:58 rpd + * Removed RCS keyword nonsense. + * + * Revision 2.4 90/06/02 15:13:49 rpd + * Converted to new IPC. + * [90/03/20 20:56:44 rpd] + * + * Revision 2.3 90/01/19 14:37:12 rwd + * Make cthread_init return pointer to new stack. + * [89/12/18 19:17:45 rwd] + * + * Revision 2.2 89/12/08 19:53:37 rwd + * Change cproc and cthread counters to globals with better names. + * [89/11/02 rwd] + * + * Revision 2.1 89/08/03 17:09:34 rwd + * Created. + * + * + * 31-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University + * Changed cthread_exit() logic for the case of the main thread, + * to fix thread and stack memory leak found by Camelot group. + * + * 21-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University + * Added consistency check in beginning of cthread_body(). + * + * 11-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University + * Removed cthread_port() and cthread_set_port(). + * Removed port deallocation from cthread_free(). + * Minor changes to cthread_body(), cthread_exit(), and cthread_done(). + * + * 10-Aug-87 Eric Cooper (ecc) at Carnegie Mellon University + * Changed call to mig_init() in cthread_init() to pass 1 as argument. + * + * 31-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University + * Added call to mig_init() from cthread_init(). + */ +/* + * File: cthreads.c + * Author: Eric Cooper, Carnegie Mellon University + * Date: July, 1987 + * + * Implementation of fork, join, exit, etc. + */ + +#include <cthreads.h> +#include "cthread_internals.h" + +/* + * C Threads imports: + */ +extern void cproc_create(); +extern vm_offset_t cproc_init(); +extern void mig_init(); + +/* + * Mach imports: + */ + +/* + * C library imports: + */ + +/* + * Thread status bits. + */ +#define T_MAIN 0x1 +#define T_RETURNED 0x2 +#define T_DETACHED 0x4 + +#ifdef DEBUG +int cthread_debug = FALSE; +#endif DEBUG + +private struct cthread_queue cthreads = QUEUE_INITIALIZER; +private struct mutex cthread_lock = MUTEX_INITIALIZER; +private struct condition cthread_needed = CONDITION_INITIALIZER; +private struct condition cthread_idle = CONDITION_INITIALIZER; +int cthread_cprocs = 0; +int cthread_cthreads = 0; +int cthread_max_cprocs = 0; + +private cthread_t free_cthreads = NO_CTHREAD; /* free list */ +private spin_lock_t free_lock = SPIN_LOCK_INITIALIZER; /* unlocked */ + +private struct cthread initial_cthread = { 0 }; + +private cthread_t +cthread_alloc(func, arg) + cthread_fn_t func; + any_t arg; +{ + register cthread_t t = NO_CTHREAD; + + if (free_cthreads != NO_CTHREAD) { + /* + * Don't try for the lock unless + * the list is likely to be nonempty. + * We can't be sure, though, until we lock it. + */ + spin_lock(&free_lock); + t = free_cthreads; + if (t != NO_CTHREAD) + free_cthreads = t->next; + spin_unlock(&free_lock); + } + if (t == NO_CTHREAD) { + /* + * The free list was empty. + * We may have only found this out after + * locking it, which is why this isn't an + * "else" branch of the previous statement. + */ + t = (cthread_t) malloc(sizeof(struct cthread)); + } + *t = initial_cthread; + t->func = func; + t->arg = arg; + return t; +} + +private void +cthread_free(t) + register cthread_t t; +{ + spin_lock(&free_lock); + t->next = free_cthreads; + free_cthreads = t; + spin_unlock(&free_lock); +} + +int +cthread_init() +{ + static int cthreads_started = FALSE; + register cproc_t p; + register cthread_t t; + vm_offset_t stack; + + if (cthreads_started) + return 0; + stack = cproc_init(); + cthread_cprocs = 1; + t = cthread_alloc((cthread_fn_t) 0, (any_t) 0); + +#ifdef cthread_md_init + cthread_md_init(); +#endif + + cthread_cthreads = 1; + t->state |= T_MAIN; + cthread_set_name(t, "main"); + + /* cproc_self() doesn't work yet, because + we haven't yet switched to the new stack. */ + + p = *(cproc_t *)&ur_cthread_ptr(stack); + p->incarnation = t; + mig_init(p); /* enable multi-threaded mig interfaces */ + + cthreads_started = TRUE; + return stack; +} + +/* + * Used for automatic initialization by crt0. + * Cast needed since too many C compilers choke on the type void (*)(). + */ +int (*_cthread_init_routine)() = (int (*)()) cthread_init; + +/* + * Procedure invoked at the base of each cthread. + */ +void +cthread_body(self) + cproc_t self; +{ + register cthread_t t; + + ASSERT(cproc_self() == self); + TRACE(printf("[idle] cthread_body(%x)\n", self)); + mutex_lock(&cthread_lock); + for (;;) { + /* + * Dequeue a thread invocation request. + */ + cthread_queue_deq(&cthreads, cthread_t, t); + if (t != NO_CTHREAD) { + /* + * We have a thread to execute. + */ + mutex_unlock(&cthread_lock); + cthread_assoc(self, t); /* assume thread's identity */ + if (_setjmp(t->catch) == 0) { /* catch for cthread_exit() */ + /* + * Execute the fork request. + */ + t->result = (*(t->func))(t->arg); + } + /* + * Return result from thread. + */ + TRACE(printf("[%s] done()\n", cthread_name(t))); + mutex_lock(&t->lock); + if (t->state & T_DETACHED) { + mutex_unlock(&t->lock); + cthread_free(t); + } else { + t->state |= T_RETURNED; + mutex_unlock(&t->lock); + condition_signal(&t->done); + } + cthread_assoc(self, NO_CTHREAD); + mutex_lock(&cthread_lock); + cthread_cthreads -= 1; + } else { + /* + * Queue is empty. + * Signal that we're idle in case the main thread + * is waiting to exit, then wait for reincarnation. + */ + condition_signal(&cthread_idle); + condition_wait(&cthread_needed, &cthread_lock); + } + } +} + +cthread_t +cthread_fork(func, arg) + cthread_fn_t func; + any_t arg; +{ + register cthread_t t; + + TRACE(printf("[%s] fork()\n", cthread_name(cthread_self()))); + mutex_lock(&cthread_lock); + t = cthread_alloc(func, arg); + cthread_queue_enq(&cthreads, t); + if (++cthread_cthreads > cthread_cprocs && (cthread_max_cprocs == 0 || cthread_cprocs < cthread_max_cprocs)) { + cthread_cprocs += 1; + cproc_create(); + } + mutex_unlock(&cthread_lock); + condition_signal(&cthread_needed); + return t; +} + +void +cthread_detach(t) + cthread_t t; +{ + TRACE(printf("[%s] detach(%s)\n", cthread_name(cthread_self()), cthread_name(t))); + mutex_lock(&t->lock); + if (t->state & T_RETURNED) { + mutex_unlock(&t->lock); + cthread_free(t); + } else { + t->state |= T_DETACHED; + mutex_unlock(&t->lock); + } +} + +any_t +cthread_join(t) + cthread_t t; +{ + any_t result; + + TRACE(printf("[%s] join(%s)\n", cthread_name(cthread_self()), cthread_name(t))); + mutex_lock(&t->lock); + ASSERT(! (t->state & T_DETACHED)); + while (! (t->state & T_RETURNED)) + condition_wait(&t->done, &t->lock); + result = t->result; + mutex_unlock(&t->lock); + cthread_free(t); + return result; +} + +void +cthread_exit(result) + any_t result; +{ + register cthread_t t = cthread_self(); + + TRACE(printf("[%s] exit()\n", cthread_name(t))); + t->result = result; + if (t->state & T_MAIN) { + mutex_lock(&cthread_lock); + while (cthread_cthreads > 1) + condition_wait(&cthread_idle, &cthread_lock); + mutex_unlock(&cthread_lock); + exit((int) result); + } else { + _longjmp(t->catch, TRUE); + } +} + +/* + * Used for automatic finalization by crt0. Cast needed since too many C + * compilers choke on the type void (*)(). + */ +int (*_cthread_exit_routine)() = (int (*)()) cthread_exit; + +void +cthread_set_name(t, name) + cthread_t t; + char *name; +{ + t->name = name; +} + +char * +cthread_name(t) + cthread_t t; +{ + return (t == NO_CTHREAD + ? "idle" + : (t->name == 0 ? "?" : t->name)); +} + +int +cthread_limit() +{ + return cthread_max_cprocs; +} + +void +cthread_set_limit(n) + int n; +{ + cthread_max_cprocs = n; +} + +int +cthread_count() +{ + return cthread_cthreads; +} + +cthread_fork_prepare() +{ + spin_lock(&free_lock); + mutex_lock(&cthread_lock); + malloc_fork_prepare(); + cproc_fork_prepare(); +} + +cthread_fork_parent() +{ + cproc_fork_parent(); + malloc_fork_parent(); + mutex_unlock(&cthread_lock); + spin_unlock(&free_lock); +} + +cthread_fork_child() +{ + cthread_t t; + cproc_t p; + + cproc_fork_child(); + malloc_fork_child(); + mutex_unlock(&cthread_lock); + spin_unlock(&free_lock); + condition_init(&cthread_needed); + condition_init(&cthread_idle); + + cthread_max_cprocs = 0; + + stack_fork_child(); + + while (TRUE) { /* Free cthread runnable list */ + cthread_queue_deq(&cthreads, cthread_t, t); + if (t == NO_CTHREAD) break; + free((char *) t); + } + + while (free_cthreads != NO_CTHREAD) { /* Free cthread free list */ + t = free_cthreads; + free_cthreads = free_cthreads->next; + free((char *) t); + } + + cthread_cprocs = 1; + t = cthread_self(); + cthread_cthreads = 1; + t->state |= T_MAIN; + cthread_set_name(t, "main"); + + p = cproc_self(); + p->incarnation = t; + mig_init(p); /* enable multi-threaded mig interfaces */ +} |