diff options
Diffstat (limited to 'libthreads/cthreads.h')
-rw-r--r-- | libthreads/cthreads.h | 710 |
1 files changed, 0 insertions, 710 deletions
diff --git a/libthreads/cthreads.h b/libthreads/cthreads.h deleted file mode 100644 index d937dcca..00000000 --- a/libthreads/cthreads.h +++ /dev/null @@ -1,710 +0,0 @@ -/* - * Mach Operating System - * Copyright (c) 1993,1992,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 - * 20-Oct-93 Tero Kivinen (kivinen) at Helsinki University of Technology - * Renamed cthread_t->catch to to cthread_t->catch_exit, because - * catch is reserved word in c++. - * - * 12-Oct-93 Johannes Helander (jvh) at Helsinki University of Technology - * Added CONDITION_NAMED_INITIALIZER and MUTEX_NAMED_INITIALIZER1 - * macros. They take one argument: a name string. - * - * $Log: cthreads.h,v $ - * Revision 1.19 2002/05/28 23:55:58 roland - * 2002-05-28 Roland McGrath <roland@frob.com> - * - * * cthreads.h (hurd_condition_wait, condition_implies, - * condition_unimplies): Restore decls lost in merge. - * (mutex_clear): Define as mutex_init instead of bogon (lost in merge). - * - * Revision 1.18 2002/05/27 02:50:10 roland - * 2002-05-26 Roland McGrath <roland@frob.com> - * - * Changes merged from CMU MK83a version: - * * cthreads.h, options.h: Various cleanups. - * * call.c, cthread_data.c, sync.c, mig_support.c: Likewise. - * * i386/cthreads.h, i386/thread.c, i386/lock.s: Likewise. - * * cthread_internals.h: Add decls for internal functions. - * (struct cproc): Use vm_offset_t for stack_base and stack_size members. - * Use natural_t for context member. - * * cprocs.c: Use prototypes for all defns. - * * cthreads.c: Likewise. - * (cthread_exit): Cast any_t to integer_t before int. - * - * Revision 2.17 93/05/10 19:43:11 rvb - * Removed include of stdlib.h and just define malloc - * [93/04/27 mrt] - * - * Revision 2.16 93/05/10 17:51:26 rvb - * Just imagine how much more useful TWO special/fast lookup - * variables could be. (Actually, I am planning on using this - * for bsdss -- for multiple threads per task. If I don't, I'll - * remove the feature.) - * [93/05/10 rvb] - * Big mistake here! CTHREAD_DATA must always be set TRUE. - * cthreads.h is included by import_mach.h by lots of files - * that are not compiled with -DCTHREAD_DATA. This means - * they see a different structure for cthread_t than the - * cthread library -- which is compiled with CTHREAD_DATA. - * Also, make cthread_set_data and cthread_data macros. - * [93/05/06 rvb] - * Flush stdlib - * [93/05/05 rvb] - * - * Revision 2.15 93/01/27 09:03:32 danner - * Updated include of mach/mach.h to mach.h - * - * - * Revision 2.14 93/01/24 13:24:50 danner - * Get MACRO_BEGIN, MACRO_END, NEVER, ... from sys/macro_help.h - * why define it here. - * [92/10/20 rvb] - * - * Revision 2.13 93/01/14 18:05:04 danner - * Added MACRO_BEGIN and MACRO_END to definition of spin_lock. - * Fixed return value of cthread_set_data. - * Added prototypes for other miscellaneous functions. - * [92/12/18 pds] - * Converted file to ANSI C. - * Added declarations of cthread_fork_{prepare,parent,child}. - * Added include of <sys/macro_help.h>. - * [92/12/13 pds] - * - * Replaced calloc declaration with an include of stdlib.h. - * [92/06/15 pds] - * 64bit cleanup. - * [92/12/02 af] - * - * Revision 2.12 92/05/22 18:38:36 jfriedl - * From Mike Kupfer <kupfer@sprite.Berkeley.EDU>: - * Add declaration for cthread_wire(). - * Merge in Jonathan Chew's changes for thread-local data. - * Use MACRO_BEGIN and MACRO_END. - * - * Revision 1.8 91/03/25 14:14:49 jjc - * For compatibility with cthread_data: - * 1) Added private_data field to cthread structure - * for use by POSIX thread specific data routines. - * 2) Conditionalized old data field used by cthread_data - * under CTHREAD_DATA for binary compatibility. - * 3) Changed macros, cthread_set_data and cthread_data, - * into routines which use the POSIX routines for - * source compatibility. - * Also, conditionalized under CTHREAD_DATA. - * [91/03/18 jjc] - * Added support for multiplexing the thread specific global - * variable, cthread_data, using the POSIX threads interface - * for thread private data. - * [91/03/14 jjc] - * - * Revision 2.11 91/08/03 18:20:15 jsb - * Removed the infamous line 122. - * [91/08/01 22:40:24 jsb] - * - * Revision 2.10 91/07/31 18:35:42 dbg - * Fix the standard-C conditional: it's __STDC__. - * - * Allow for macro-redefinition of cthread_sp, spin_try_lock, - * spin_unlock (from machine/cthreads.h). - * [91/07/30 17:34:28 dbg] - * - * Revision 2.9 91/05/14 17:56:42 mrt - * Correcting copyright - * - * Revision 2.8 91/02/14 14:19:52 mrt - * Added new Mach copyright - * [91/02/13 12:41:15 mrt] - * - * Revision 2.7 90/11/05 14:37:12 rpd - * Include machine/cthreads.h. Added spin_lock_t. - * [90/10/31 rwd] - * - * Revision 2.6 90/10/12 13:07:24 rpd - * Channge to allow for positive stack growth. - * [90/10/10 rwd] - * - * Revision 2.5 90/09/09 14:34:56 rpd - * Remove mutex_special and debug_mutex. - * [90/08/24 rwd] - * - * Revision 2.4 90/08/07 14:31:14 rpd - * Removed RCS keyword nonsense. - * - * Revision 2.3 90/01/19 14:37:18 rwd - * Add back pointer to cthread structure. - * [90/01/03 rwd] - * Change definition of cthread_init and change ur_cthread_self macro - * to reflect movement of self pointer on stack. - * [89/12/18 19:18:34 rwd] - * - * Revision 2.2 89/12/08 19:53:49 rwd - * Change spin_try_lock to int. - * [89/11/30 rwd] - * Changed mutex macros to deal with special mutexs - * [89/11/26 rwd] - * Make mutex_{set,clear}_special routines instead of macros. - * [89/11/25 rwd] - * Added mutex_special to specify a need to context switch on this - * mutex. - * [89/11/21 rwd] - * - * Made mutex_lock a macro trying to grab the spin_lock first. - * [89/11/13 rwd] - * Removed conditionals. Mutexes are more like conditions now. - * Changed for limited kernel thread version. - * [89/10/23 rwd] - * - * Revision 2.1 89/08/03 17:09:40 rwd - * Created. - * - * - * 28-Oct-88 Eric Cooper (ecc) at Carnegie Mellon University - * Implemented spin_lock() as test and test-and-set logic - * (using mutex_try_lock()) in sync.c. Changed ((char *) 0) - * to 0, at Mike Jones's suggestion, and turned on ANSI-style - * declarations in either C++ or _STDC_. - * - * 29-Sep-88 Eric Cooper (ecc) at Carnegie Mellon University - * Changed NULL to ((char *) 0) to avoid dependency on <stdio.h>, - * at Alessandro Forin's suggestion. - * - * 08-Sep-88 Alessandro Forin (af) at Carnegie Mellon University - * Changed queue_t to cthread_queue_t and string_t to char * - * to avoid conflicts. - * - * 01-Apr-88 Eric Cooper (ecc) at Carnegie Mellon University - * Changed compound statement macros to use the - * do { ... } while (0) trick, so that they work - * in all statement contexts. - * - * 19-Feb-88 Eric Cooper (ecc) at Carnegie Mellon University - * Made spin_unlock() and mutex_unlock() into procedure calls - * rather than macros, so that even smart compilers can't reorder - * the clearing of the lock. Suggested by Jeff Eppinger. - * Removed the now empty <machine>/cthreads.h. - * - * 01-Dec-87 Eric Cooper (ecc) at Carnegie Mellon University - * Changed cthread_self() to mask the current SP to find - * the self pointer stored at the base of the stack. - * - * 22-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University - * Fixed bugs in mutex_set_name and condition_set_name - * due to bad choice of macro formal parameter name. - * - * 21-Jul-87 Eric Cooper (ecc) at Carnegie Mellon University - * Moved #include <machine/cthreads.h> to avoid referring - * to types before they are declared (required by C++). - * - * 9-Jul-87 Michael Jones (mbj) at Carnegie Mellon University - * Added conditional type declarations for C++. - * Added _cthread_init_routine and _cthread_exit_routine variables - * for automatic initialization and finalization by crt0. - */ -/* - * File: cthreads.h - * Author: Eric Cooper, Carnegie Mellon University - * Date: Jul, 1987 - * - * Definitions for the C Threads package. - * - */ - - -#ifndef _CTHREADS_ -#define _CTHREADS_ 1 - -#if 0 -/* This is CMU's machine-dependent file. In GNU all of the machine - dependencies are dealt with in libc. */ -#include <machine/cthreads.h> -#include <mach.h> -#include <sys/macro_help.h> -#include <mach/machine/vm_param.h> - -#ifdef __STDC__ -extern void *malloc(); -#else -extern char *malloc(); -#endif - -#else /* GNU */ -# include <stdlib.h> -# include <mach.h> -# include <mach/machine/vm_param.h> -# include <machine-sp.h> -# define cthread_sp() ((vm_address_t) __thread_stack_pointer ()) -# define MACRO_BEGIN __extension__ ({ -# define MACRO_END 0; }) -#endif - -typedef void *any_t; /* XXX - obsolete, should be deleted. */ - -#if defined(TRUE) -#else /* not defined(TRUE) */ -#define TRUE 1 -#define FALSE 0 -#endif - -/* Enable mutex holder debugging */ -/* #define WAIT_DEBUG */ -/* Record function name instead of thread pointer */ -/* #define WAIT_FUNC_DEBUG */ - -/* - * C Threads package initialization. - */ - -extern vm_offset_t cthread_init(void); - - -/* - * Queues. - */ -typedef struct cthread_queue { - struct cthread_queue_item *head; - struct cthread_queue_item *tail; -} *cthread_queue_t; - -typedef struct cthread_queue_item { - struct cthread_queue_item *next; -} *cthread_queue_item_t; - -#define NO_QUEUE_ITEM ((cthread_queue_item_t) 0) - -#define QUEUE_INITIALIZER { NO_QUEUE_ITEM, NO_QUEUE_ITEM } - -#define cthread_queue_alloc() ((cthread_queue_t) calloc(1, sizeof(struct cthread_queue))) -#define cthread_queue_init(q) ((q)->head = (q)->tail = 0) -#define cthread_queue_free(q) free((q)) - -#define cthread_queue_enq(q, x) \ - MACRO_BEGIN \ - (x)->next = 0; \ - if ((q)->tail == 0) \ - (q)->head = (cthread_queue_item_t) (x); \ - else \ - (q)->tail->next = (cthread_queue_item_t) (x); \ - (q)->tail = (cthread_queue_item_t) (x); \ - MACRO_END - -#define cthread_queue_preq(q, x) \ - MACRO_BEGIN \ - if ((q)->tail == 0) \ - (q)->tail = (cthread_queue_item_t) (x); \ - ((cthread_queue_item_t) (x))->next = (q)->head; \ - (q)->head = (cthread_queue_item_t) (x); \ - MACRO_END - -#define cthread_queue_head(q, t) ((t) ((q)->head)) - -#define cthread_queue_deq(q, t, x) \ - MACRO_BEGIN \ - if (((x) = (t) ((q)->head)) != 0 && \ - ((q)->head = (cthread_queue_item_t) ((x)->next)) == 0) \ - (q)->tail = 0; \ - MACRO_END - -#define cthread_queue_map(q, t, f) \ - MACRO_BEGIN \ - register cthread_queue_item_t x, next; \ - for (x = (cthread_queue_item_t) ((q)->head); x != 0; x = next){\ - next = x->next; \ - (*(f))((t) x); \ - } \ - MACRO_END - -#if 1 - -/* In GNU, spin locks are implemented in libc. - Just include its header file. */ -#include <spin-lock.h> - -#else /* Unused CMU code. */ - -/* - * Spin locks. - */ -extern void spin_lock_solid(spin_lock_t *_lock); - -#if defined(spin_unlock) -#else /* not defined(spin_unlock) */ -extern void spin_unlock(spin_lock_t *_lock); -#endif - -#if defined(spin_try_lock) -#else /* not defined(spin_try_lock) */ -extern boolean_t spin_try_lock(spin_lock_t *_lock); -#endif - -#define spin_lock(p) \ - MACRO_BEGIN \ - if (!spin_try_lock(p)) { \ - spin_lock_solid(p); \ - } \ - MACRO_END - -#endif /* End unused CMU code. */ - -/* - * Mutex objects. - */ -typedef struct mutex { - /* The `held' member must be first in GNU. The GNU C library relies on - being able to cast a `struct mutex *' to a `spin_lock_t *' (which is - kosher if it is the first member) and spin_try_lock that address to - see if it gets the mutex. */ - spin_lock_t held; - spin_lock_t lock; - const char *name; - struct cthread_queue queue; - /* holder is for WAIT_DEBUG. Not ifdeffed to keep size constant. */ -#ifdef WAIT_FUNC_DEBUG - const char *fname; -#else /* WAIT_FUNC_DEBUG */ - struct cthread *holder; -#endif /* WAIT_FUNC_DEBUG */ -} *mutex_t; - -#ifdef WAIT_DEBUG -#ifdef WAIT_FUNC_DEBUG -#define WAIT_CLEAR_DEBUG(m) (m)->fname = 0 -#define WAIT_SET_DEBUG(m) (m)->fname = __FUNCTION__ -#else /* WAIT_FUNC_DEBUG */ -#define WAIT_CLEAR_DEBUG(m) (m)->holder = 0 -#define WAIT_SET_DEBUG(m) (m)->holder = cthread_self() -#endif /* WAIT_FUNC_DEBUG */ -#else /* WAIT_DEBUG */ -#define WAIT_CLEAR_DEBUG(m) (void) 0 -#define WAIT_SET_DEBUG(m) (void) 0 -#endif /* WAIT_DEBUG */ - -/* Rearranged accordingly for GNU: */ -#define MUTEX_INITIALIZER { SPIN_LOCK_INITIALIZER, SPIN_LOCK_INITIALIZER, 0, QUEUE_INITIALIZER, } -#define MUTEX_NAMED_INITIALIZER(Name) { SPIN_LOCK_INITIALIZER, SPIN_LOCK_INITIALIZER, Name, QUEUE_INITIALIZER, } - -#define mutex_alloc() ((mutex_t) calloc(1, sizeof(struct mutex))) -#define mutex_init(m) \ - MACRO_BEGIN \ - spin_lock_init(&(m)->lock); \ - cthread_queue_init(&(m)->queue); \ - spin_lock_init(&(m)->held); \ - WAIT_CLEAR_DEBUG(m); \ - MACRO_END -#define mutex_set_name(m, x) ((m)->name = (x)) -#define mutex_name(m) ((m)->name != 0 ? (m)->name : "?") -#define mutex_clear(m) mutex_init(m) -#define mutex_free(m) free((m)) - -#define mutex_try_lock(m) (spin_try_lock(&(m)->held) ? WAIT_SET_DEBUG(m), 1 : 0) -#define mutex_lock(m) \ - MACRO_BEGIN \ - if (!spin_try_lock(&(m)->held)) { \ - __mutex_lock_solid(m); \ - } \ - WAIT_SET_DEBUG(m); \ - MACRO_END -#define mutex_unlock(m) \ - MACRO_BEGIN \ - if (spin_unlock(&(m)->held), \ - cthread_queue_head(&(m)->queue, vm_offset_t) != 0) { \ - __mutex_unlock_solid(m); \ - } \ - WAIT_CLEAR_DEBUG(m); \ - MACRO_END -/* - * Condition variables. - */ -typedef struct condition { - spin_lock_t lock; - struct cthread_queue queue; - const char *name; - struct cond_imp *implications; -} *condition_t; - -struct cond_imp -{ - struct condition *implicatand; - struct cond_imp *next; -}; - -#define CONDITION_INITIALIZER { SPIN_LOCK_INITIALIZER, QUEUE_INITIALIZER, 0, 0 } -#define CONDITION_NAMED_INITIALIZER(Name) { SPIN_LOCK_INITIALIZER, QUEUE_INITIALIZER, Name, 0 } - -#define condition_alloc() \ - ((condition_t) calloc(1, sizeof(struct condition))) -#define condition_init(c) \ - MACRO_BEGIN \ - spin_lock_init(&(c)->lock); \ - cthread_queue_init(&(c)->queue); \ - (c)->name = 0; \ - (c)->implications = 0; \ - MACRO_END -#define condition_set_name(c, x) ((c)->name = (x)) -#define condition_name(c) ((c)->name != 0 ? (c)->name : "?") -#define condition_clear(c) \ - MACRO_BEGIN \ - condition_broadcast(c); \ - spin_lock(&(c)->lock); \ - MACRO_END -#define condition_free(c) \ - MACRO_BEGIN \ - condition_clear(c); \ - free((c)); \ - MACRO_END - -#define condition_signal(c) \ - MACRO_BEGIN \ - if ((c)->queue.head || (c)->implications) { \ - cond_signal(c); \ - } \ - MACRO_END - -#define condition_broadcast(c) \ - MACRO_BEGIN \ - if ((c)->queue.head || (c)->implications) { \ - cond_broadcast(c); \ - } \ - MACRO_END - -extern int cond_signal(condition_t _cond); - -extern void cond_broadcast(condition_t _cond); - -extern void condition_wait(condition_t _cond, mutex_t _mutex); -extern int hurd_condition_wait(condition_t _cond, mutex_t _mutex); - -extern void condition_implies(condition_t _implicator, - condition_t _implicatand); -extern void condition_unimplies(condition_t _implicator, - condition_t _implicatand); - -/* - * Threads. - */ - -typedef void * (*cthread_fn_t)(void *arg); - -#include <setjmp.h> - -typedef struct cthread { - struct cthread *next; - struct mutex lock; - struct condition done; - int state; - jmp_buf catch_exit; - cthread_fn_t func; - void *arg; - void *result; - const char *name; - void *data; - void *ldata; - void *private_data; - struct ur_cthread *ur; -} *cthread_t; - -#define NO_CTHREAD ((cthread_t) 0) - -extern cthread_t cthread_fork(cthread_fn_t _func, void *_arg); - -extern void cthread_detach(cthread_t _thread); - -extern any_t cthread_join(cthread_t _thread); - -extern void cthread_yield(void); - -extern void cthread_exit(void *_result); - -/* - * This structure must agree with struct cproc in cthread_internals.h - */ -typedef struct ur_cthread { - struct ur_cthread *next; - cthread_t incarnation; -} *ur_cthread_t; - -#ifndef cthread_sp -extern vm_offset_t -cthread_sp(void); -#endif - -extern vm_offset_t cthread_stack_mask; - -#if defined(STACK_GROWTH_UP) -#define ur_cthread_ptr(sp) \ - (* (ur_cthread_t *) ((sp) & cthread_stack_mask)) -#else /* not defined(STACK_GROWTH_UP) */ -#define ur_cthread_ptr(sp) \ - (* (ur_cthread_t *) ( ((sp) | cthread_stack_mask) + 1 \ - - sizeof(ur_cthread_t *)) ) -#endif /* defined(STACK_GROWTH_UP) */ - -#define ur_cthread_self() (ur_cthread_ptr(cthread_sp())) - -#define cthread_assoc(id, t) ((((ur_cthread_t) (id))->incarnation = (t)), \ - ((t) ? ((t)->ur = (ur_cthread_t)(id)) : 0)) -#define cthread_self() (ur_cthread_self()->incarnation) - -extern void cthread_set_name(cthread_t _thread, const char *_name); - -extern const char * cthread_name(cthread_t _thread); - -extern int cthread_count(void); - -extern void cthread_set_limit(int _limit); - -extern int cthread_limit(void); - -extern void cthread_set_kernel_limit(int _n); - -extern int cthread_kernel_limit(void); - -extern void cthread_wire(void); - -extern void cthread_unwire(void); - -extern void cthread_msg_busy(mach_port_t _port, int _min, int _max); - -extern void cthread_msg_active(mach_port_t _prt, int _min, int _max); - -extern mach_msg_return_t cthread_mach_msg(mach_msg_header_t *_header, - mach_msg_option_t _option, - mach_msg_size_t _send_size, - mach_msg_size_t _rcv_size, - mach_port_t _rcv_name, - mach_msg_timeout_t _timeout, - mach_port_t _notify, - int _min, int _max); - -extern void cthread_fork_prepare(void); - -extern void cthread_fork_parent(void); - -extern void cthread_fork_child(void); - -#if defined(THREAD_CALLS) -/* - * Routines to replace thread_*. - */ -extern kern_return_t cthread_get_state(cthread_t _thread); - -extern kern_return_t cthread_set_state(cthread_t _thread); - -extern kern_return_t cthread_abort(cthread_t _thread); - -extern kern_return_t cthread_resume(cthread_t _thread); - -extern kern_return_t cthread_suspend(cthread_t _thread); - -extern kern_return_t cthread_call_on(cthread_t _thread); -#endif /* defined(THREAD_CALLS) */ - -#if defined(CTHREAD_DATA_XX) -/* - * Set or get thread specific "global" variable - * - * The thread given must be the calling thread (ie. thread_self). - * XXX This is for compatibility with the old cthread_data. XXX - */ -extern int cthread_set_data(cthread_t _thread, void *_val); - -extern void * cthread_data(cthread_t _thread); -#else /* defined(CTHREAD_DATA_XX) */ - -#define cthread_set_data(_thread, _val) ((_thread)->data) = (void *)(_val); -#define cthread_data(_thread) ((_thread)->data) - -#define cthread_set_ldata(_thread, _val) ((_thread)->ldata) = (void *)(_val); -#define cthread_ldata(_thread) ((_thread)->ldata) - -#endif /* defined(CTHREAD_DATA_XX) */ - - -/* - * Support for POSIX thread specific data - * - * Multiplexes a thread specific "global" variable - * into many thread specific "global" variables. - */ -#define CTHREAD_DATA_VALUE_NULL (void *)0 -#define CTHREAD_KEY_INVALID (cthread_key_t)-1 - -typedef int cthread_key_t; - -/* - * Create key to private data visible to all threads in task. - * Different threads may use same key, but the values bound to the key are - * maintained on a thread specific basis. - */ -extern int cthread_keycreate(cthread_key_t *_key); - -/* - * Get value currently bound to key for calling thread - */ -extern int cthread_getspecific(cthread_key_t _key, void **_value); - -/* - * Bind value to given key for calling thread - */ -extern int cthread_setspecific(cthread_key_t _key, void *_value); - -/* - * Debugging support. - */ -#if defined(DEBUG) - -#if defined(ASSERT) -#else /* not defined(ASSERT) */ -/* - * Assertion macro, similar to <assert.h> - */ -#include <stdio.h> -#define ASSERT(p) \ - MACRO_BEGIN \ - if (!(p)) { \ - fprintf(stderr, \ - "File %s, line %d: assertion p failed.\n", \ - __FILE__, __LINE__); \ - abort(); \ - } \ - MACRO_END - -#endif /* defined(ASSERT) */ - -#define SHOULDNT_HAPPEN 0 - -extern int cthread_debug; - -#else /* not defined(DEBUG) */ - -#if defined(ASSERT) -#else /* not defined(ASSERT) */ -#define ASSERT(p) -#endif /* defined(ASSERT) */ - -#endif /* defined(DEBUG) */ - -#endif /* not defined(_CTHREADS_) */ |