|
|
|
|
|
|
|
#include "global.h" |
RCSID("$Id: threads.c,v 1.193 2002/12/07 14:15:18 grubba Exp $"); |
|
PMOD_EXPORT int num_threads = 1; |
PMOD_EXPORT int threads_disabled = 0; |
|
#ifdef _REENTRANT |
#include "threads.h" |
#include "array.h" |
#include "mapping.h" |
#include "object.h" |
#include "pike_macros.h" |
#include "callback.h" |
#include "builtin_functions.h" |
#include "constants.h" |
#include "program.h" |
#include "program_id.h" |
#include "gc.h" |
#include "main.h" |
#include "module_support.h" |
#include "pike_types.h" |
#include "operators.h" |
#include "bignum.h" |
#include "signal_handler.h" |
#include "pike_rusage.h" |
|
#include <errno.h> |
|
PMOD_EXPORT int live_threads = 0, disallow_live_threads = 0; |
PMOD_EXPORT COND_T live_threads_change; |
PMOD_EXPORT COND_T threads_disabled_change; |
PMOD_EXPORT size_t thread_stack_size=256 * 1204; |
|
|
int __thread_sys_behavior = 1; |
|
#if !defined(HAVE_PTHREAD_ATFORK) && !defined(th_atfork) |
#include "callback.h" |
#define PIKE_USE_OWN_ATFORK |
|
|
static struct callback_list atfork_prepare_callback; |
static struct callback_list atfork_parent_callback; |
static struct callback_list atfork_child_callback; |
|
int th_atfork(void (*prepare)(void),void (*parent)(void),void (*child)(void)) |
{ |
if(prepare) |
add_to_callback(&atfork_prepare_callback, (callback_func) prepare, 0, 0); |
if(parent) |
add_to_callback(&atfork_parent_callback, (callback_func) parent, 0, 0); |
if(child) |
add_to_callback(&atfork_child_callback, (callback_func) child, 0, 0); |
return 0; |
} |
void th_atfork_prepare(void) |
{ |
call_callback(& atfork_prepare_callback, 0); |
} |
void th_atfork_parent(void) |
{ |
call_callback(& atfork_parent_callback, 0); |
} |
void th_atfork_child(void) |
{ |
call_callback(& atfork_child_callback, 0); |
} |
#endif |
|
#ifdef __NT__ |
|
int low_nt_create_thread(unsigned Pike_stack_size, |
unsigned (TH_STDCALL *fun)(void *), |
void *arg, |
unsigned *id) |
{ |
HANDLE h = (HANDLE)_beginthreadex(NULL, Pike_stack_size, fun, arg, 0, id); |
if(h) |
{ |
CloseHandle(h); |
return 0; |
} |
else |
{ |
return 1; |
} |
} |
|
|
#ifdef PIKE_DEBUG |
PMOD_EXPORT HANDLE CheckValidHandle(HANDLE h); |
#endif |
|
#endif |
|
#ifdef SIMULATE_COND_WITH_EVENT |
PMOD_EXPORT int co_wait(COND_T *c, MUTEX_T *m) |
{ |
struct cond_t_queue me; |
event_init(&me.event); |
me.next=0; |
mt_lock(& c->lock); |
|
if(c->tail) |
{ |
c->tail->next=&me; |
c->tail=&me; |
}else{ |
c->head=c->tail=&me; |
} |
|
mt_unlock(& c->lock); |
mt_unlock(m); |
event_wait(&me.event); |
mt_lock(m); |
|
event_destroy(& me.event); |
|
|
#ifdef PIKE_DEBUG |
if(me.next) |
Pike_fatal("Wait on event return prematurely!!\n"); |
#endif |
|
return 0; |
} |
|
PMOD_EXPORT int co_signal(COND_T *c) |
{ |
struct cond_t_queue *t; |
mt_lock(& c->lock); |
if((t=c->head)) |
{ |
c->head=t->next; |
t->next=0; |
if(!c->head) c->tail=0; |
} |
mt_unlock(& c->lock); |
if(t) |
event_signal(& t->event); |
return 0; |
} |
|
PMOD_EXPORT int co_broadcast(COND_T *c) |
{ |
struct cond_t_queue *t,*n; |
mt_lock(& c->lock); |
n=c->head; |
c->head=c->tail=0; |
mt_unlock(& c->lock); |
|
while((t=n)) |
{ |
n=t->next; |
t->next=0; |
event_signal(& t->event); |
} |
|
return 0; |
} |
|
PMOD_EXPORT int co_destroy(COND_T *c) |
{ |
struct cond_t_queue *t; |
mt_lock(& c->lock); |
t=c->head; |
mt_unlock(& c->lock); |
if(t) return -1; |
mt_destroy(& c->lock); |
return 0; |
} |
|
#endif |
|
|
#define THIS_THREAD ((struct thread_state *)CURRENT_STORAGE) |
|
static struct callback *threads_evaluator_callback=0; |
int thread_id_result_variable; |
|
int th_running = 0; |
#ifdef PIKE_DEBUG |
int debug_interpreter_is_locked = 0; |
THREAD_T debug_locking_thread; |
THREAD_T threads_disabled_thread = 0; |
#endif |
#ifdef INTERNAL_PROFILING |
PMOD_EXPORT unsigned long thread_yields = 0; |
#endif |
PMOD_EXPORT MUTEX_T interpreter_lock; |
MUTEX_T thread_table_lock, interleave_lock; |
struct program *mutex_key = 0; |
PMOD_EXPORT struct program *thread_id_prog = 0; |
struct program *thread_local_prog = 0; |
#ifdef POSIX_THREADS |
pthread_attr_t pattr; |
pthread_attr_t small_pattr; |
#endif |
PMOD_EXPORT ptrdiff_t thread_storage_offset; |
#ifdef USE_CLOCK_FOR_SLICES |
PMOD_EXPORT clock_t thread_start_clock = 0; |
#endif |
|
struct thread_starter |
{ |
struct object *id; |
struct array *args; |
#ifdef HAVE_BROKEN_LINUX_THREAD_EUID |
int euid, egid; |
#endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ |
}; |
|
struct thread_local |
{ |
INT32 id; |
}; |
|
static volatile IMUTEX_T *interleave_list = NULL; |
|
|
|
|
void low_init_threads_disable(void) |
{ |
|
|
if (!threads_disabled) { |
THREADS_FPRINTF(0, |
(stderr, "low_init_threads_disable(): Locking IM's...\n")); |
|
if (Pike_interpreter.thread_id) { |
|
|
IMUTEX_T *im; |
|
THREADS_ALLOW(); |
|
|
mt_lock(&interleave_lock); |
|
im = (IMUTEX_T *)interleave_list; |
|
while(im) { |
mt_lock(&(im->lock)); |
|
im = im->next; |
} |
|
THREADS_DISALLOW(); |
} else { |
|
|
IMUTEX_T *im; |
|
|
mt_lock(&interleave_lock); |
|
im = (IMUTEX_T *)interleave_list; |
|
while(im) { |
mt_lock(&(im->lock)); |
|
im = im->next; |
} |
} |
|
THREADS_FPRINTF(0, (stderr, |
"low_init_threads_disable(): Disabling threads.\n")); |
|
threads_disabled = 1; |
#ifdef PIKE_DEBUG |
threads_disabled_thread = th_self(); |
#endif |
} else { |
threads_disabled++; |
} |
|
THREADS_FPRINTF(0, |
(stderr, "low_init_threads_disable(): threads_disabled:%d\n", |
threads_disabled)); |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void init_threads_disable(struct object *o) |
{ |
disallow_live_threads = 1; |
|
if(live_threads) { |
SWAP_OUT_CURRENT_THREAD(); |
while (live_threads) { |
THREADS_FPRINTF(0, |
(stderr, |
"_disable_threads(): Waiting for %d threads to finish\n", |
live_threads)); |
low_co_wait_interpreter(&live_threads_change); |
} |
SWAP_IN_CURRENT_THREAD(); |
} |
|
low_init_threads_disable(); |
} |
|
void exit_threads_disable(struct object *o) |
{ |
THREADS_FPRINTF(0, (stderr, "exit_threads_disable(): threads_disabled:%d\n", |
threads_disabled)); |
if(threads_disabled) { |
if(!--threads_disabled) { |
IMUTEX_T *im = (IMUTEX_T *)interleave_list; |
|
|
while(im) { |
THREADS_FPRINTF(0, |
(stderr, |
"exit_threads_disable(): Unlocking IM 0x%p\n", im)); |
mt_unlock(&(im->lock)); |
im = im->next; |
} |
|
mt_unlock(&interleave_lock); |
|
THREADS_FPRINTF(0, (stderr, "_exit_threads_disable(): Wake up!\n")); |
disallow_live_threads = 0; |
co_broadcast(&threads_disabled_change); |
#ifdef PIKE_DEBUG |
threads_disabled_thread = 0; |
#endif |
} |
#ifdef PIKE_DEBUG |
} else { |
Pike_fatal("exit_threads_disable() called too many times!\n"); |
#endif /* PIKE_DEBUG */ |
} |
} |
|
void init_interleave_mutex(IMUTEX_T *im) |
{ |
mt_init(&(im->lock)); |
|
THREADS_FPRINTF(0, (stderr, |
"init_interleave_mutex(): init_threads_disable()\n")); |
|
init_threads_disable(NULL); |
|
THREADS_FPRINTF(0, (stderr, |
"init_interleave_mutex(): Locking IM 0x%p\n", im)); |
|
|
mt_lock(&(im->lock)); |
|
im->next = (IMUTEX_T *)interleave_list; |
if (interleave_list) { |
interleave_list->prev = im; |
} |
interleave_list = im; |
im->prev = NULL; |
|
THREADS_FPRINTF(0, (stderr, |
"init_interleave_mutex(): exit_threads_disable()\n")); |
|
exit_threads_disable(NULL); |
} |
|
void exit_interleave_mutex(IMUTEX_T *im) |
{ |
init_threads_disable(NULL); |
|
if (im->prev) { |
im->prev->next = im->next; |
} else { |
interleave_list = im->next; |
} |
if (im->next) { |
im->next->prev = im->prev; |
} |
|
|
mt_unlock(&(im->lock)); |
|
exit_threads_disable(NULL); |
} |
|
|
|
#define THREAD_TABLE_SIZE 127 /* Totally arbitrary prime */ |
|
static struct thread_state *thread_table_chains[THREAD_TABLE_SIZE]; |
static int num_pike_threads=0; |
|
void thread_table_init(void) |
{ |
INT32 x; |
for(x=0; x<THREAD_TABLE_SIZE; x++) |
thread_table_chains[x] = NULL; |
} |
|
unsigned INT32 thread_table_hash(THREAD_T *tid) |
{ |
return th_hash(*tid) % THREAD_TABLE_SIZE; |
} |
|
#ifdef PIKE_DEBUG |
void dumpmem(char *desc, void *x, int size) |
{ |
int e; |
unsigned char *tmp=(unsigned char *)x; |
fprintf(stderr,"%s: ",desc); |
for(e=0;e<size;e++) |
fprintf(stderr,"%02x",tmp[e]); |
fprintf(stderr,"\n"); |
} |
#endif |
|
|
PMOD_EXPORT void thread_table_insert(struct object *o) |
{ |
struct thread_state *s = OBJ2THREAD(o); |
unsigned INT32 h = thread_table_hash(&s->id); |
#ifdef PIKE_DEBUG |
if(h>=THREAD_TABLE_SIZE) |
Pike_fatal("thread_table_hash failed miserably!\n"); |
if(thread_state_for_id(s->id)) |
{ |
if(thread_state_for_id(s->id) == s) |
Pike_fatal("Registring thread twice!\n"); |
else |
Pike_fatal("Forgot to unregister thread!\n"); |
} |
|
#endif |
mt_lock( & thread_table_lock ); |
num_pike_threads++; |
if((s->hashlink = thread_table_chains[h]) != NULL) |
s->hashlink->backlink = &s->hashlink; |
thread_table_chains[h] = s; |
s->backlink = &thread_table_chains[h]; |
mt_unlock( & thread_table_lock ); |
} |
|
PMOD_EXPORT void thread_table_delete(struct object *o) |
{ |
struct thread_state *s = OBJ2THREAD(o); |
|
mt_lock( & thread_table_lock ); |
num_pike_threads--; |
if(s->hashlink != NULL) |
s->hashlink->backlink = s->backlink; |
*(s->backlink) = s->hashlink; |
mt_unlock( & thread_table_lock ); |
} |
|
PMOD_EXPORT struct thread_state *thread_state_for_id(THREAD_T tid) |
{ |
unsigned INT32 h = thread_table_hash(&tid); |
struct thread_state *s = NULL; |
#if 0 |
if(num_threads>1) |
dumpmem("thread_state_for_id: ",&tid,sizeof(tid)); |
#endif |
#ifdef PIKE_DEBUG |
if(h>=THREAD_TABLE_SIZE) |
Pike_fatal("thread_table_hash failed miserably!\n"); |
#endif |
mt_lock( & thread_table_lock ); |
if(thread_table_chains[h] == NULL) |
{ |
|
} |
else if(th_equal((s=thread_table_chains[h])->id, tid)) |
{ |
|
} |
else |
{ |
while((s = s->hashlink) != NULL) |
if(th_equal(s->id, tid)) |
break; |
if(s != NULL) { |
|
|
|
|
if(s->hashlink != NULL) |
s->hashlink->backlink = s->backlink; |
*(s->backlink) = s->hashlink; |
|
if((s->hashlink = thread_table_chains[h]) != NULL) |
s->hashlink->backlink = &s->hashlink; |
thread_table_chains[h] = s; |
s->backlink = &thread_table_chains[h]; |
} |
} |
mt_unlock( & thread_table_lock ); |
#if 0 |
if(num_threads>1 && s) |
dumpmem("thread_state_for_id return value: ",&s->id,sizeof(tid)); |
#endif |
return s; |
|
|
} |
|
struct thread_state *gdb_thread_state_for_id(THREAD_T tid) |
|
{ |
unsigned INT32 h = thread_table_hash(&tid); |
struct thread_state *s; |
for (s = thread_table_chains[h]; s != NULL; s = s->hashlink) |
if(th_equal(s->id, tid)) |
break; |
return s; |
} |
|
INT32 gdb_next_thread_state(INT32 prev, struct thread_state **ts) |
|
{ |
if (!*ts || !(*ts)->hashlink) { |
if (!*ts) prev = -1; |
while (++prev < THREAD_TABLE_SIZE) |
if ((*ts = thread_table_chains[prev])) |
return prev; |
*ts = NULL; |
return 0; |
} |
*ts = (*ts)->hashlink; |
return prev; |
} |
|
PMOD_EXPORT struct object *thread_for_id(THREAD_T tid) |
{ |
struct thread_state *s = thread_state_for_id(tid); |
return (s == NULL? NULL : THREADSTATE2OBJ(s)); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
PMOD_EXPORT void f_all_threads(INT32 args) |
{ |
|
|
|
INT32 x; |
struct svalue *oldsp; |
struct thread_state *s; |
|
pop_n_elems(args); |
oldsp = Pike_sp; |
mt_lock( & thread_table_lock ); |
for(x=0; x<THREAD_TABLE_SIZE; x++) |
for(s=thread_table_chains[x]; s; s=s->hashlink) { |
struct object *o = THREADSTATE2OBJ(s); |
if (o) { |
ref_push_object(o); |
} |
} |
mt_unlock( & thread_table_lock ); |
f_aggregate(DO_NOT_WARN(Pike_sp - oldsp)); |
} |
|
#ifdef PIKE_DEBUG |
void debug_list_all_threads(void) |
{ |
INT32 x; |
struct thread_state *s; |
THREAD_T self = th_self(); |
|
fprintf(stderr,"--Listing all threads--\n"); |
dumpmem("Current thread: ",&self, sizeof(self)); |
fprintf(stderr,"Current thread obj: %p\n",Pike_interpreter.thread_id); |
fprintf(stderr,"Current thread hash: %d\n",thread_table_hash(&self)); |
fprintf(stderr,"Current stack pointer: %p\n",&self); |
for(x=0; x<THREAD_TABLE_SIZE; x++) |
{ |
for(s=thread_table_chains[x]; s; s=s->hashlink) { |
struct object *o = THREADSTATE2OBJ(s); |
fprintf(stderr,"ThTab[%d]: %p (stackbase=%p)",x,o,s->state.stack_top); |
dumpmem(" ",&s->id, sizeof(s->id)); |
} |
} |
fprintf(stderr,"-----------------------\n"); |
} |
#endif |
|
PMOD_EXPORT int count_pike_threads(void) |
{ |
return num_pike_threads; |
} |
|
static void check_threads(struct callback *cb, void *arg, void * arg2) |
{ |
#ifndef HAVE_NO_YIELD |
|
* that a thread switch will take place only occasionally in the |
* window below. */ |
static int div_; |
if(div_++ & 255) |
return; |
#ifdef HAVE_GETHRTIME |
{ |
static hrtime_t last_ = 0; |
hrtime_t now = gethrtime(); |
if( now-last_ < 50000000 ) |
return; |
last_ = now; |
} |
#elif defined (USE_CLOCK_FOR_SLICES) |
if (clock() - thread_start_clock < (clock_t) (CLOCKS_PER_SEC / 20)) |
return; |
#endif |
#endif |
|
#ifdef DEBUG |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} |
|
if(Pike_interpreter.backlink != OBJ2THREAD(Pike_interpreter.thread_id)) |
Pike_fatal("Hashlink is wrong!\n"); |
#endif |
|
THREADS_ALLOW(); |
|
th_yield(); |
THREADS_DISALLOW(); |
|
#ifdef USE_CLOCK_FOR_SLICES |
|
* thread local time. */ |
thread_start_clock = clock(); |
#endif |
|
DO_IF_DEBUG( |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} ) |
} |
|
TH_RETURN_TYPE new_thread_func(void * data) |
{ |
struct thread_starter arg = *(struct thread_starter *)data; |
JMP_BUF back; |
INT32 tmp; |
|
THREADS_FPRINTF(0, (stderr,"THREADS_DISALLOW() Thread %08x created...\n", |
(unsigned int)arg.id)); |
|
#ifdef HAVE_BROKEN_LINUX_THREAD_EUID |
|
* effective uid & gid. |
*/ |
if (!geteuid()) { |
setegid(arg.egid); |
seteuid(arg.euid); |
} |
#endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ |
|
if((tmp=mt_lock_interpreter())) |
Pike_fatal("Failed to lock interpreter, return value=%d, errno=%d\n",tmp, |
#ifdef __NT__ |
GetLastError() |
#else |
errno |
#endif |
); |
SWAP_IN_THREAD(OBJ2THREAD(arg.id)); |
init_interpreter(); |
Pike_interpreter.thread_id=arg.id; |
#ifdef PROFILING |
Pike_interpreter.stack_bottom=((char *)&data); |
#endif |
Pike_interpreter.stack_top=((char *)&data)+ (thread_stack_size-16384) * STACK_DIRECTION; |
Pike_interpreter.recoveries = NULL; |
SWAP_OUT_THREAD(OBJ2THREAD(Pike_interpreter.thread_id)); |
OBJ2THREAD(Pike_interpreter.thread_id)->swapped=0; |
|
#if defined(PIKE_DEBUG) |
if(d_flag) |
{ |
THREAD_T self = th_self(); |
|
if( Pike_interpreter.thread_id && !th_equal( OBJ2THREAD(Pike_interpreter.thread_id)->id, self) ) |
Pike_fatal("Current thread is wrong. %lx %lx\n", |
(long)OBJ2THREAD(Pike_interpreter.thread_id)->id, (long)self); |
|
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} |
} |
#endif |
|
#ifdef THREAD_TRACE |
{ |
t_flag = default_t_flag; |
} |
#endif /* THREAD_TRACE */ |
|
THREADS_FPRINTF(0, (stderr,"THREAD %08x INITED\n",(unsigned int)Pike_interpreter.thread_id)); |
|
DO_IF_DEBUG( |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} ) |
|
|
if(SETJMP(back)) |
{ |
if(throw_severity < THROW_EXIT) |
call_handle_error(); |
if(throw_severity == THROW_EXIT) |
{ |
free((char *) data); |
pike_do_exit(throw_value.u.integer); |
} |
} else { |
INT32 args=arg.args->size; |
back.severity=THROW_EXIT; |
push_array_items(arg.args); |
arg.args=0; |
f_call_function(args); |
|
|
object_low_set_index(Pike_interpreter.thread_id, |
thread_id_result_variable, |
Pike_sp-1); |
pop_stack(); |
} |
|
DO_IF_DEBUG( |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} ) |
|
|
if(OBJ2THREAD(Pike_interpreter.thread_id)->thread_local != NULL) { |
free_mapping(OBJ2THREAD(Pike_interpreter.thread_id)->thread_local); |
OBJ2THREAD(Pike_interpreter.thread_id)->thread_local = NULL; |
} |
|
OBJ2THREAD(Pike_interpreter.thread_id)->status=THREAD_EXITED; |
co_broadcast(& OBJ2THREAD(Pike_interpreter.thread_id)->status_change); |
|
free((char *)data); |
UNSETJMP(back); |
|
THREADS_FPRINTF(0, (stderr,"THREADS_ALLOW() Thread %08x done\n", |
(unsigned int)Pike_interpreter.thread_id)); |
|
cleanup_interpret(); |
DO_IF_DMALLOC( |
SWAP_OUT_THREAD(OBJ2THREAD(Pike_interpreter.thread_id)); |
OBJ2THREAD(Pike_interpreter.thread_id)->swapped=0; |
) |
thread_table_delete(Pike_interpreter.thread_id); |
free_object(Pike_interpreter.thread_id); |
Pike_interpreter.thread_id=0; |
num_threads--; |
if(!num_threads && threads_evaluator_callback) |
{ |
remove_callback(threads_evaluator_callback); |
threads_evaluator_callback=0; |
} |
|
#ifdef INTERNAL_PROFILING |
fprintf (stderr, "Thread usage summary:\n"); |
debug_print_rusage (stderr); |
#endif |
|
|
mt_unlock_interpreter(); |
th_exit(0); |
|
return(0); |
} |
|
#ifdef UNIX_THREADS |
int num_lwps = 1; |
#endif |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void f_thread_create(INT32 args) |
{ |
struct thread_starter *arg; |
int tmp; |
arg = ALLOC_STRUCT(thread_starter); |
arg->args=aggregate_array(args); |
arg->id=clone_object(thread_id_prog,0); |
OBJ2THREAD(arg->id)->status=THREAD_RUNNING; |
|
#ifdef HAVE_BROKEN_LINUX_THREAD_EUID |
arg->euid = geteuid(); |
arg->egid = getegid(); |
#endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ |
|
do { |
tmp = th_create(& OBJ2THREAD(arg->id)->id, |
new_thread_func, |
arg); |
if (tmp == EINTR) check_threads_etc(); |
} while( tmp == EINTR ); |
|
if(!tmp) |
{ |
num_threads++; |
thread_table_insert(arg->id); |
|
if(!threads_evaluator_callback) |
{ |
threads_evaluator_callback=add_to_callback(&evaluator_callbacks, |
check_threads, 0,0); |
dmalloc_accept_leak(threads_evaluator_callback); |
} |
ref_push_object(arg->id); |
THREADS_FPRINTF(0, (stderr, "THREAD_CREATE -> t:%08x\n", |
(unsigned int)arg->id)); |
} else { |
free_object(arg->id); |
free_array(arg->args); |
free((char *)arg); |
Pike_error("Failed to create thread (errno = %d).\n",tmp); |
} |
} |
|
|
|
|
#ifdef UNIX_THREADS |
|
*! |
*! @fixme |
*! Document this function |
*/ |
void f_thread_set_concurrency(INT32 args) |
{ |
int c=1; |
if(args) c=Pike_sp[-args].u.integer; |
else Pike_error("No argument to thread_set_concurrency(int concurrency);\n"); |
pop_n_elems(args); |
num_lwps=c; |
th_setconcurrency(c); |
} |
#endif |
|
|
|
|
|
|
|
|
PMOD_EXPORT void f_this_thread(INT32 args) |
{ |
pop_n_elems(args); |
ref_push_object(Pike_interpreter.thread_id); |
} |
|
#define THIS_MUTEX ((struct mutex_storage *)(CURRENT_STORAGE)) |
|
|
|
|
|
|
|
|
struct mutex_storage |
{ |
COND_T condition; |
struct object *key; |
}; |
|
struct key_storage |
{ |
struct mutex_storage *mut; |
struct object *mutex_obj; |
struct object *owner; |
int initialized; |
}; |
|
#define OB2KEY(X) ((struct key_storage *)((X)->storage)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void f_mutex_lock(INT32 args) |
{ |
struct mutex_storage *m; |
struct object *o; |
INT_TYPE type; |
|
DO_IF_DEBUG( |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} ) |
|
m=THIS_MUTEX; |
if(!args) |
type=0; |
else |
get_all_args("mutex->lock",args,"%i",&type); |
|
switch(type) |
{ |
default: |
bad_arg_error("mutex->lock", Pike_sp-args, args, 2, "int(0..2)", Pike_sp+1-args, |
"Unknown mutex locking style: %d\n",type); |
|
|
case 0: |
case 2: |
if(m->key && OB2KEY(m->key)->owner == Pike_interpreter.thread_id) |
{ |
THREADS_FPRINTF(0, |
(stderr, "Recursive LOCK k:%08x, m:%08x(%08x), t:%08x\n", |
(unsigned int)OB2KEY(m->key), |
(unsigned int)m, |
(unsigned int)OB2KEY(m->key)->mut, |
(unsigned int) Pike_interpreter.thread_id)); |
|
if(type==0) Pike_error("Recursive mutex locks!\n"); |
|
pop_n_elems(args); |
push_int(0); |
return; |
} |
case 1: |
break; |
} |
|
|
|
|
o=fast_clone_object(mutex_key,0); |
|
DO_IF_DEBUG( |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} ) |
|
if(m->key) |
{ |
if(threads_disabled) |
{ |
free_object(o); |
Pike_error("Cannot wait for mutexes when threads are disabled!\n"); |
} |
do |
{ |
SWAP_OUT_CURRENT_THREAD(); |
THREADS_FPRINTF(1, (stderr,"WAITING TO LOCK m:%08x\n",(unsigned int)m)); |
co_wait_interpreter(& m->condition); |
SWAP_IN_CURRENT_THREAD(); |
check_threads_etc(); |
}while(m->key); |
} |
m->key=o; |
OB2KEY(o)->mut=m; |
add_ref (OB2KEY(o)->mutex_obj = Pike_fp->current_object); |
|
DO_IF_DEBUG( |
if(thread_for_id(th_self()) != Pike_interpreter.thread_id) { |
debug_list_all_threads(); |
Pike_fatal("thread_for_id() (or Pike_interpreter.thread_id) failed! %p != %p\n",thread_for_id(th_self()),Pike_interpreter.thread_id) ; |
} ) |
|
THREADS_FPRINTF(1, (stderr, "LOCK k:%08x, m:%08x(%08x), t:%08x\n", |
(unsigned int)OB2KEY(o), |
(unsigned int)m, |
(unsigned int)OB2KEY(m->key)->mut, |
(unsigned int)Pike_interpreter.thread_id)); |
pop_n_elems(args); |
push_object(o); |
} |
|
|
|
|
|
|
|
|
|
|
|
void f_mutex_trylock(INT32 args) |
{ |
struct mutex_storage *m; |
struct object *o; |
INT_TYPE type; |
int i=0; |
|
|
|
|
|
m=THIS_MUTEX; |
|
if(!args) |
type=0; |
else |
get_all_args("mutex->trylock",args,"%i",&type); |
|
switch(type) |
{ |
default: |
bad_arg_error("mutex->trylock", Pike_sp-args, args, 2, "int(0..2)", Pike_sp+1-args, |
"Unknown mutex locking style: %d\n",type); |
|
case 0: |
if(m->key && OB2KEY(m->key)->owner == Pike_interpreter.thread_id) |
{ |
Pike_error("Recursive mutex locks!\n"); |
} |
|
case 2: |
case 1: |
break; |
} |
|
o=clone_object(mutex_key,0); |
|
if(!m->key) |
{ |
OB2KEY(o)->mut=m; |
add_ref (OB2KEY(o)->mutex_obj = Pike_fp->current_object); |
m->key=o; |
i=1; |
} |
|
pop_n_elems(args); |
if(i) |
{ |
push_object(o); |
} else { |
destruct(o); |
free_object(o); |
push_int(0); |
} |
} |
|
|
|
|
|
|
|
|
|
PMOD_EXPORT void f_mutex_locking_thread(INT32 args) |
{ |
struct mutex_storage *m = THIS_MUTEX; |
|
pop_n_elems(args); |
|
if (m->key && OB2KEY(m->key)->owner) |
ref_push_object(OB2KEY(m->key)->owner); |
else |
push_int(0); |
} |
|
|
|
|
|
|
|
|
|
PMOD_EXPORT void f_mutex_locking_key(INT32 args) |
{ |
struct mutex_storage *m = THIS_MUTEX; |
|
pop_n_elems(args); |
|
if (m->key) |
ref_push_object(m->key); |
else |
push_int(0); |
} |
|
void init_mutex_obj(struct object *o) |
{ |
co_init(& THIS_MUTEX->condition); |
THIS_MUTEX->key=0; |
} |
|
void exit_mutex_obj(struct object *o) |
{ |
THREADS_FPRINTF(1, (stderr, "DESTROYING MUTEX m:%08x\n", |
(unsigned int)THIS_MUTEX)); |
if(THIS_MUTEX->key) destruct(THIS_MUTEX->key); |
THIS_MUTEX->key=0; |
co_destroy(& THIS_MUTEX->condition); |
} |
|
|
|
|
#define THIS_KEY ((struct key_storage *)(CURRENT_STORAGE)) |
void init_mutex_key_obj(struct object *o) |
{ |
THREADS_FPRINTF(1, (stderr, "KEY k:%08x, o:%08x\n", |
(unsigned int)THIS_KEY, (unsigned int)Pike_interpreter.thread_id)); |
THIS_KEY->mut=0; |
THIS_KEY->mutex_obj = NULL; |
add_ref(THIS_KEY->owner=Pike_interpreter.thread_id); |
THIS_KEY->initialized=1; |
} |
|
void exit_mutex_key_obj(struct object *o) |
{ |
THREADS_FPRINTF(1, (stderr, "UNLOCK k:%08x m:(%08x) t:%08x o:%08x\n", |
(unsigned int)THIS_KEY, |
(unsigned int)THIS_KEY->mut, |
(unsigned int)Pike_interpreter.thread_id, |
(unsigned int)THIS_KEY->owner)); |
if(THIS_KEY->mut) |
{ |
struct mutex_storage *mut = THIS_KEY->mut; |
|
#ifdef PIKE_DEBUG |
if(mut->key != o) |
Pike_fatal("Mutex unlock from wrong key %p != %p!\n",THIS_KEY->mut->key,o); |
#endif |
mut->key=0; |
if (THIS_KEY->owner) { |
free_object(THIS_KEY->owner); |
THIS_KEY->owner=0; |
} |
free_object (THIS_KEY->mutex_obj); |
THIS_KEY->mut=0; |
THIS_KEY->mutex_obj = NULL; |
THIS_KEY->initialized=0; |
co_signal(& mut->condition); |
} |
} |
|
#define THIS_COND ((COND_T *)(CURRENT_STORAGE)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void f_cond_wait(INT32 args) |
{ |
struct object *key; |
struct mutex_storage *mut; |
COND_T *c; |
|
if(threads_disabled) |
Pike_error("Cannot wait for conditions when threads are disabled!\n"); |
|
get_all_args("condition->wait", args, "%o", &key); |
|
if ((key->prog != mutex_key) || |
(!(OB2KEY(key)->initialized)) || |
(!(mut = OB2KEY(key)->mut))) { |
Pike_error("Bad argument 1 to condition->wait()\n"); |
} |
|
if(args > 1) { |
pop_n_elems(args - 1); |
args = 1; |
} |
|
c = THIS_COND; |
|
|
mut->key=0; |
OB2KEY(key)->mut=0; |
co_signal(& mut->condition); |
|
|
SWAP_OUT_CURRENT_THREAD(); |
co_wait_interpreter(c); |
SWAP_IN_CURRENT_THREAD(); |
|
|
while(mut->key) { |
SWAP_OUT_CURRENT_THREAD(); |
co_wait_interpreter(& mut->condition); |
SWAP_IN_CURRENT_THREAD(); |
check_threads_etc(); |
} |
mut->key=key; |
OB2KEY(key)->mut=mut; |
|
pop_stack(); |
return; |
} |
|
|
|
|
|
|
|
|
|
|
|
|
void f_cond_signal(INT32 args) { pop_n_elems(args); co_signal(THIS_COND); } |
|
|
|
|
|
|
|
|
void f_cond_broadcast(INT32 args) { pop_n_elems(args); co_broadcast(THIS_COND); } |
|
void init_cond_obj(struct object *o) { co_init(THIS_COND); } |
void exit_cond_obj(struct object *o) { co_destroy(THIS_COND); } |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void f_thread_backtrace(INT32 args) |
{ |
void low_backtrace(struct Pike_interpreter *); |
struct thread_state *foo = THIS_THREAD; |
|
pop_n_elems(args); |
|
if(foo->state.thread_id == Pike_interpreter.thread_id) |
{ |
f_backtrace(0); |
} |
else if(foo->state.stack_pointer) |
{ |
low_backtrace(& foo->state); |
} |
else |
{ |
push_int(0); |
f_allocate(1); |
} |
} |
|
|
|
void f_thread_id_status(INT32 args) |
{ |
pop_n_elems(args); |
push_int(THIS_THREAD->status); |
} |
|
|
|
|
|
void f_thread_id__sprintf (INT32 args) |
{ |
int c = 0; |
if(args>0 && Pike_sp[-args].type == PIKE_T_INT) |
c = Pike_sp[-args].u.integer; |
pop_n_elems (args); |
if(c != 'O') { |
push_undefined(); |
return; |
} |
push_constant_text ("Thread.Thread("); |
push_int64((ptrdiff_t)THIS_THREAD->id); |
push_constant_text (")"); |
f_add (3); |
} |
|
|
|
|
|
|
|
|
void f_thread_id_id_number(INT32 args) |
{ |
pop_n_elems(args); |
push_int64((ptrdiff_t)THIS_THREAD->id); |
} |
|
|
|
|
|
|
static void f_thread_id_result(INT32 args) |
{ |
struct thread_state *th=THIS_THREAD; |
|
if (threads_disabled) { |
Pike_error("Cannot wait for threads when threads are disabled!\n"); |
} |
|
while(th->status != THREAD_EXITED) { |
SWAP_OUT_CURRENT_THREAD(); |
co_wait_interpreter(&th->status_change); |
SWAP_IN_CURRENT_THREAD(); |
check_threads_etc(); |
} |
|
low_object_index_no_free(Pike_sp, |
Pike_fp->current_object, |
thread_id_result_variable); |
Pike_sp++; |
} |
|
void init_thread_obj(struct object *o) |
{ |
MEMSET(&THIS_THREAD->state, 0, sizeof(struct Pike_interpreter)); |
THIS_THREAD->swapped = 0; |
THIS_THREAD->status=THREAD_NOT_STARTED; |
co_init(& THIS_THREAD->status_change); |
THIS_THREAD->thread_local=NULL; |
} |
|
|
void exit_thread_obj(struct object *o) |
{ |
if(THIS_THREAD->thread_local != NULL) { |
free_mapping(THIS_THREAD->thread_local); |
THIS_THREAD->thread_local = NULL; |
} |
co_destroy(& THIS_THREAD->status_change); |
th_destroy(& THIS_THREAD->id); |
} |
|
|
|
|
static void thread_was_recursed(struct object *o) |
{ |
struct thread_state *tmp=THIS_THREAD; |
if(tmp->thread_local != NULL) |
gc_recurse_mapping(tmp->thread_local); |
} |
|
static void thread_was_checked(struct object *o) |
{ |
struct thread_state *tmp=THIS_THREAD; |
if(tmp->thread_local != NULL) |
debug_gc_check2(tmp->thread_local, T_OBJECT, o, |
" as mapping for thread local values in thread"); |
|
#ifdef PIKE_DEBUG |
if(tmp->swapped) |
{ |
struct pike_frame *f; |
debug_malloc_touch(o); |
debug_gc_xmark_svalues(tmp->state.evaluator_stack, |
tmp->state.stack_pointer-tmp->state.evaluator_stack-1, |
" in idle thread stack"); |
|
for(f=tmp->state.frame_pointer;f;f=f->next) |
{ |
debug_malloc_touch(f); |
if(f->context.parent) |
gc_external_mark2(f->context.parent,0," in Pike_fp->context.parent of idle thread"); |
gc_external_mark2(f->current_object,0," in Pike_fp->current_object of idle thread"); |
gc_external_mark2(f->context.prog,0," in Pike_fp->context.prog of idle thread"); |
} |
} |
#endif |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void f_thread_local_create( INT32 args ) |
{ |
static INT32 thread_local_id = 0; |
((struct thread_local *)CURRENT_STORAGE)->id = |
thread_local_id++; |
pop_n_elems(args); |
push_int(0); |
} |
|
PMOD_EXPORT void f_thread_local(INT32 args) |
{ |
struct object *loc = clone_object(thread_local_prog,0); |
pop_n_elems(args); |
push_object(loc); |
} |
|
|
|
|
|
|
|
|
|
|
|
void f_thread_local_get(INT32 args) |
{ |
struct svalue key; |
struct mapping *m; |
key.u.integer = ((struct thread_local *)CURRENT_STORAGE)->id; |
key.type = T_INT; |
key.subtype = NUMBER_NUMBER; |
pop_n_elems(args); |
if(Pike_interpreter.thread_id != NULL && |
(m = OBJ2THREAD(Pike_interpreter.thread_id)->thread_local) != NULL) |
mapping_index_no_free(Pike_sp++, m, &key); |
else { |
push_int(0); |
Pike_sp[-1].subtype=NUMBER_UNDEFINED; |
} |
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void f_thread_local_set(INT32 args) |
{ |
struct svalue key; |
struct mapping *m; |
key.u.integer = ((struct thread_local *)CURRENT_STORAGE)->id; |
key.type = T_INT; |
key.subtype = NUMBER_NUMBER; |
if(args>1) |
pop_n_elems(args-1); |
else if(args<1) |
Pike_error("Too few arguments to Thread.Local.set()\n"); |
|
if(Pike_interpreter.thread_id == NULL) |
Pike_error("Trying to set Thread.Local without thread!\n"); |
|
if((m = OBJ2THREAD(Pike_interpreter.thread_id)->thread_local) == NULL) |
m = OBJ2THREAD(Pike_interpreter.thread_id)->thread_local = |
allocate_mapping(4); |
|
mapping_insert(m, &key, &Pike_sp[-1]); |
} |
|
#ifdef PIKE_DEBUG |
void gc_check_thread_local (struct object *o) |
{ |
|
if (Pike_in_gc == GC_PASS_LOCATE) { |
struct svalue key, *val; |
INT32 x; |
struct thread_state *s; |
|
key.u.integer = ((struct thread_local *)CURRENT_STORAGE)->id; |
key.type = T_INT; |
key.subtype = NUMBER_NUMBER; |
|
|
|
|
for(x=0; x<THREAD_TABLE_SIZE; x++) |
for(s=thread_table_chains[x]; s; s=s->hashlink) { |
if (s->thread_local && |
(val = low_mapping_lookup(s->thread_local, &key))) |
debug_gc_check_svalues2(val, 1, T_OBJECT, o, |
" as thread local value in Thread.Local object" |
" (indirect ref)"); |
} |
|
} |
} |
#endif |
|
|
|
|
|
|
|
|
|
|
static struct farmer { |
struct farmer *neighbour; |
void *field; |
void (*harvest)(void *); |
THREAD_T me; |
COND_T harvest_moon; |
#ifdef HAVE_BROKEN_LINUX_THREAD_EUID |
int euid, egid; |
#endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ |
} *farmers; |
|
static MUTEX_T rosie; |
|
|
static int _num_farmers, _num_idle_farmers; |
|
static TH_RETURN_TYPE farm(void *_a) |
{ |
struct farmer *me = (struct farmer *)_a; |
|
#ifdef HAVE_BROKEN_LINUX_THREAD_EUID |
|
* effective uid & gid. |
*/ |
if (!geteuid()) { |
setegid(me->egid); |
seteuid(me->euid); |
} |
#endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ |
|
do |
{ |
|
|
me->harvest( me->field ); |
|
|
me->harvest = 0; |
mt_lock( &rosie ); |
if( ++_num_idle_farmers > 16 ) |
{ |
--_num_idle_farmers; |
--_num_farmers; |
mt_unlock( &rosie ); |
free( me ); |
return 0; |
} |
me->neighbour = farmers; |
farmers = me; |
|
while(!me->harvest) co_wait( &me->harvest_moon, &rosie ); |
--_num_idle_farmers; |
mt_unlock( &rosie ); |
|
} while(1); |
|
return 0; |
} |
|
int th_num_idle_farmers(void) |
{ |
return _num_idle_farmers; |
} |
|
|
int th_num_farmers(void) |
{ |
return _num_farmers; |
} |
|
static struct farmer *new_farmer(void (*fun)(void *), void *args) |
{ |
struct farmer *me = malloc(sizeof(struct farmer)); |
|
if (!me) { |
|
Pike_fatal("new_farmer(): Out of memory!\n"); |
} |
|
dmalloc_accept_leak(me); |
|
_num_farmers++; |
me->neighbour = 0; |
me->field = args; |
me->harvest = fun; |
co_init( &me->harvest_moon ); |
|
#ifdef HAVE_BROKEN_LINUX_THREAD_EUID |
me->euid = geteuid(); |
me->egid = getegid(); |
#endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ |
|
th_create_small(&me->me, farm, me); |
return me; |
} |
|
PMOD_EXPORT void th_farm(void (*fun)(void *), void *here) |
{ |
if(!fun) Pike_fatal("The farmers don't known how to handle empty fields\n"); |
mt_lock( &rosie ); |
if(farmers) |
{ |
struct farmer *f = farmers; |
farmers = f->neighbour; |
f->field = here; |
f->harvest = fun; |
mt_unlock( &rosie ); |
co_signal( &f->harvest_moon ); |
return; |
} |
mt_unlock( &rosie ); |
new_farmer( fun, here ); |
} |
|
|
|
|
|
void low_th_init(void) |
{ |
#ifdef SGI_SPROC_THREADS |
#error /* Need to specify a filename */ |
us_cookie = usinit(""); |
#endif /* SGI_SPROC_THREADS */ |
|
THREADS_FPRINTF(0, (stderr, "THREADS_DISALLOW() Initializing threads.\n")); |
|
#ifdef POSIX_THREADS |
#ifdef HAVE_PTHREAD_INIT |
pthread_init(); |
#endif /* HAVE_PTHREAD_INIT */ |
#endif /* POSIX_THREADS */ |
|
mt_init( & interpreter_lock); |
low_mt_lock_interpreter(); |
mt_init( & thread_table_lock); |
mt_init( & interleave_lock); |
mt_init( & rosie); |
co_init( & live_threads_change); |
co_init( & threads_disabled_change); |
|
thread_table_init(); |
#ifdef POSIX_THREADS |
pthread_attr_init(&pattr); |
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE |
pthread_attr_setstacksize(&pattr, thread_stack_size); |
#endif |
pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED); |
|
pthread_attr_init(&small_pattr); |
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE |
pthread_attr_setstacksize(&small_pattr, 4096*sizeof(char *)); |
#endif |
pthread_attr_setdetachstate(&small_pattr, PTHREAD_CREATE_DETACHED); |
|
th_running = 1; |
#endif |
} |
|
void th_init(void) |
{ |
ptrdiff_t mutex_key_offset; |
|
#ifdef UNIX_THREADS |
|
|
ADD_EFUN("thread_set_concurrency",f_thread_set_concurrency,tFunc(tInt,tVoid), OPT_SIDE_EFFECT); |
#endif |
|
START_NEW_PROGRAM_ID(THREAD_MUTEX_KEY); |
mutex_key_offset = ADD_STORAGE(struct key_storage); |
|
|
|
PIKE_MAP_VARIABLE("_owner", mutex_key_offset + OFFSETOF(key_storage, owner), |
tObjIs_THREAD_ID, T_OBJECT, 0); |
PIKE_MAP_VARIABLE("_mutex", mutex_key_offset + OFFSETOF(key_storage, mutex_obj), |
tObjIs_THREAD_MUTEX, T_OBJECT, ID_STATIC|ID_PRIVATE); |
set_init_callback(init_mutex_key_obj); |
set_exit_callback(exit_mutex_key_obj); |
mutex_key=Pike_compiler->new_program; |
add_ref(mutex_key); |
end_class("mutex_key", 0); |
mutex_key->flags|=PROGRAM_DESTRUCT_IMMEDIATE; |
#ifdef PIKE_DEBUG |
if(!mutex_key) |
Pike_fatal("Failed to initialize mutex_key program!\n"); |
#endif |
|
START_NEW_PROGRAM_ID(THREAD_MUTEX); |
ADD_STORAGE(struct mutex_storage); |
|
ADD_FUNCTION("lock",f_mutex_lock, |
tFunc(tOr(tInt02,tVoid),tObjIs_THREAD_MUTEX_KEY),0); |
|
ADD_FUNCTION("trylock",f_mutex_trylock, |
tFunc(tOr(tInt02,tVoid),tObjIs_THREAD_MUTEX_KEY),0); |
|
ADD_FUNCTION("current_locking_thread",f_mutex_locking_thread, |
tFunc(tNone,tObjIs_THREAD_ID), 0); |
|
ADD_FUNCTION("current_locking_key",f_mutex_locking_key, |
tFunc(tNone,tObjIs_THREAD_MUTEX_KEY), 0); |
set_init_callback(init_mutex_obj); |
set_exit_callback(exit_mutex_obj); |
end_class("mutex", 0); |
|
START_NEW_PROGRAM_ID(THREAD_CONDITION); |
ADD_STORAGE(COND_T); |
|
ADD_FUNCTION("wait",f_cond_wait, |
tFunc(tObjIs_THREAD_MUTEX_KEY,tVoid),0); |
|
ADD_FUNCTION("signal",f_cond_signal,tFunc(tNone,tVoid),0); |
|
ADD_FUNCTION("broadcast",f_cond_broadcast,tFunc(tNone,tVoid),0); |
set_init_callback(init_cond_obj); |
set_exit_callback(exit_cond_obj); |
end_class("condition", 0); |
|
{ |
struct program *tmp; |
START_NEW_PROGRAM_ID(THREAD_DISABLE_THREADS); |
set_init_callback(init_threads_disable); |
set_exit_callback(exit_threads_disable); |
tmp = Pike_compiler->new_program; |
add_ref(tmp); |
end_class("threads_disabled", 0); |
tmp->flags|=PROGRAM_DESTRUCT_IMMEDIATE; |
add_global_program("_disable_threads", tmp); |
free_program(tmp); |
} |
|
START_NEW_PROGRAM_ID(THREAD_LOCAL); |
ADD_STORAGE(struct thread_local); |
ADD_FUNCTION("get",f_thread_local_get,tFunc(tNone,tMix),0); |
ADD_FUNCTION("set",f_thread_local_set,tFunc(tSetvar(1,tMix),tVar(1)),0); |
ADD_FUNCTION("create", f_thread_local_create, |
tFunc(tVoid,tVoid), ID_STATIC); |
#ifdef PIKE_DEBUG |
set_gc_check_callback(gc_check_thread_local); |
#endif |
thread_local_prog=Pike_compiler->new_program; |
add_ref(thread_local_prog); |
end_class("thread_local", 0); |
if(!thread_local_prog) |
Pike_fatal("Failed to initialize thread_local program!\n"); |
ADD_EFUN("thread_local", f_thread_local, |
tFunc(tNone,tObjIs_THREAD_LOCAL), |
OPT_EXTERNAL_DEPEND); |
|
START_NEW_PROGRAM_ID(THREAD_ID); |
thread_storage_offset=ADD_STORAGE(struct thread_state); |
thread_id_result_variable=simple_add_variable("result","mixed",0); |
|
ADD_FUNCTION("backtrace",f_thread_backtrace,tFunc(tNone,tArray),0); |
|
ADD_FUNCTION("wait",f_thread_id_result,tFunc(tNone,tMix),0); |
|
ADD_FUNCTION("status",f_thread_id_status,tFunc(tNone,tInt),0); |
ADD_FUNCTION("_sprintf",f_thread_id__sprintf,tFunc(tNone,tStr),0); |
ADD_FUNCTION("id_number",f_thread_id_id_number,tFunc(tNone,tInt),0); |
set_gc_recurse_callback(thread_was_recursed); |
set_gc_check_callback(thread_was_checked); |
set_init_callback(init_thread_obj); |
set_exit_callback(exit_thread_obj); |
thread_id_prog=Pike_compiler->new_program; |
thread_id_prog->flags |= PROGRAM_NO_EXPLICIT_DESTRUCT; |
add_ref(thread_id_prog); |
end_class("thread_id", 0); |
|
|
ADD_EFUN("thread_create",f_thread_create, |
tFuncV(tNone,tMixed,tObjIs_THREAD_ID), |
OPT_SIDE_EFFECT); |
|
|
ADD_EFUN("this_thread",f_this_thread, |
tFunc(tNone,tObjIs_THREAD_ID), |
OPT_EXTERNAL_DEPEND); |
|
|
ADD_EFUN("all_threads",f_all_threads, |
tFunc(tNone,tArr(tObjIs_THREAD_ID)), |
OPT_EXTERNAL_DEPEND); |
|
|
add_integer_constant("THREAD_NOT_STARTED", THREAD_NOT_STARTED, 0); |
add_integer_constant("THREAD_RUNNING", THREAD_RUNNING, 0); |
add_integer_constant("THREAD_EXITED", THREAD_EXITED, 0); |
|
if(!mutex_key) |
Pike_fatal("Failed to initialize thread program!\n"); |
|
Pike_interpreter.thread_id=clone_object(thread_id_prog,0); |
SWAP_OUT_THREAD(OBJ2THREAD(Pike_interpreter.thread_id)); |
OBJ2THREAD(Pike_interpreter.thread_id)->id=th_self(); |
OBJ2THREAD(Pike_interpreter.thread_id)->swapped=0; |
thread_table_insert(Pike_interpreter.thread_id); |
} |
|
void th_cleanup(void) |
{ |
th_running = 0; |
|
if(Pike_interpreter.thread_id) |
{ |
thread_table_delete(Pike_interpreter.thread_id); |
destruct(Pike_interpreter.thread_id); |
free_object(Pike_interpreter.thread_id); |
Pike_interpreter.thread_id=0; |
destruct_objects_to_destruct_cb(); |
} |
|
if(mutex_key) |
{ |
free_program(mutex_key); |
mutex_key=0; |
} |
|
if(thread_local_prog) |
{ |
free_program(thread_local_prog); |
thread_local_prog=0; |
} |
|
if(thread_id_prog) |
{ |
free_program(thread_id_prog); |
thread_id_prog=0; |
} |
|
#ifdef PIKE_USE_OWN_ATFORK |
free_callback_list(&atfork_prepare_callback); |
free_callback_list(&atfork_parent_callback); |
free_callback_list(&atfork_child_callback); |
#endif |
} |
|
#endif |
|
|