#include "global.h" |
RCSID("$Id: threads.c,v 1.49 1998/01/02 08:18:43 hubbe Exp $"); |
|
int num_threads = 1; |
int threads_disabled = 0; |
|
#ifdef _REENTRANT |
#include "threads.h" |
#include "array.h" |
#include "object.h" |
#include "pike_macros.h" |
#include "callback.h" |
#include "builtin_functions.h" |
#include "constants.h" |
#include "program.h" |
#include "gc.h" |
|
#define THIS_THREAD ((struct thread_state *)fp->current_storage) |
|
struct object *thread_id; |
static struct callback *threads_evaluator_callback=0; |
int thread_id_result_variable; |
|
MUTEX_T interpreter_lock; |
struct program *mutex_key = 0; |
struct program *thread_id_prog = 0; |
#ifdef POSIX_THREADS |
pthread_attr_t pattr; |
#endif |
|
struct thread_starter |
{ |
struct object *id; |
struct array *args; |
}; |
|
static void check_threads(struct callback *cb, void *arg, void * arg2) |
{ |
static int div_; |
if(div_++ & 255) return; |
|
THREADS_ALLOW(); |
|
|
|
THREADS_DISALLOW(); |
} |
|
void *new_thread_func(void * data) |
{ |
struct thread_starter arg = *(struct thread_starter *)data; |
JMP_BUF back; |
INT32 tmp; |
|
THREADS_FPRINTF((stderr,"THREADS_DISALLOW() Thread %08x created...\n", |
(unsigned int)arg.id)); |
|
if((tmp=mt_lock( & interpreter_lock))) |
fatal("Failed to lock interpreter, errno %d\n",tmp); |
init_interpreter(); |
thread_id=arg.id; |
SWAP_OUT_THREAD((struct thread_state *)thread_id->storage); |
((struct thread_state *)thread_id->storage)->swapped=0; |
|
THREADS_FPRINTF((stderr,"THREAD %08x INITED\n",(unsigned int)thread_id)); |
if(SETJMP(back)) |
{ |
ONERROR tmp; |
SET_ONERROR(tmp,exit_on_error,"Error in handle_error in master object!"); |
assign_svalue_no_free(sp++, & throw_value); |
APPLY_MASTER("handle_error", 1); |
pop_stack(); |
UNSET_ONERROR(tmp); |
} else { |
INT32 args=arg.args->size; |
push_array_items(arg.args); |
arg.args=0; |
f_call_function(args); |
|
|
object_low_set_index(thread_id, |
thread_id_result_variable, |
sp-1); |
pop_stack(); |
} |
|
((struct thread_state *)(thread_id->storage))->status=THREAD_EXITED; |
co_signal(& ((struct thread_state *)(thread_id->storage))->status_change); |
|
free((char *)data); |
UNSETJMP(back); |
|
destruct(thread_id); |
THREADS_FPRINTF((stderr,"THREADS_ALLOW() Thread %08x done\n", |
(unsigned int)thread_id)); |
|
free_object(thread_id); |
thread_id=0; |
cleanup_interpret(); |
num_threads--; |
if(!num_threads && threads_evaluator_callback) |
{ |
remove_callback(threads_evaluator_callback); |
threads_evaluator_callback=0; |
} |
mt_unlock(& interpreter_lock); |
th_exit(0); |
|
return(NULL); |
} |
|
#ifdef UNIX_THREADS |
int num_lwps = 1; |
#endif |
|
void f_thread_create(INT32 args) |
{ |
THREAD_T dummy; |
struct thread_starter *arg; |
int tmp; |
arg=ALLOC_STRUCT(thread_starter); |
arg->args=aggregate_array(args); |
arg->id=clone_object(thread_id_prog,0); |
((struct thread_state *)arg->id->storage)->status=THREAD_RUNNING; |
|
tmp=th_create(&dummy,new_thread_func,arg); |
|
if(!tmp) |
{ |
num_threads++; |
|
if(!threads_evaluator_callback) |
{ |
threads_evaluator_callback=add_to_callback(&evaluator_callbacks, |
check_threads, 0,0); |
} |
#ifdef UNIX_THREADS |
if((num_lwps==1) || num_threads/3 > num_lwps) |
th_setconcurrency(++num_lwps); |
#endif |
push_object(arg->id); |
arg->id->refs++; |
THREADS_FPRINTF((stderr,"THREAD_CREATE -> t:%08x\n",(unsigned int)arg->id)); |
} else { |
free_object(arg->id); |
free_array(arg->args); |
free((char *)arg); |
error("Failed to create thread.\n"); |
} |
} |
|
#ifdef UNIX_THREADS |
void f_thread_set_concurrency(INT32 args) |
{ |
int c=1; |
if(args) c=sp[-args].u.integer; |
else error("No argument to thread_set_concurrency(int concurrency);\n"); |
pop_n_elems(args); |
num_lwps=c; |
th_setconcurrency(c); |
} |
#endif |
|
void f_this_thread(INT32 args) |
{ |
pop_n_elems(args); |
push_object(thread_id); |
thread_id->refs++; |
} |
|
#define THIS_MUTEX ((struct mutex_storage *)(fp->current_storage)) |
|
|
|
|
|
|
|
|
struct mutex_storage |
{ |
COND_T condition; |
struct object *key; |
}; |
|
struct key_storage |
{ |
struct mutex_storage *mut; |
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; |
|
m=THIS_MUTEX; |
|
|
|
o=clone_object(mutex_key,0); |
if(!args || IS_ZERO(sp-args)) |
{ |
if(m->key && OB2KEY(m->key)->owner == thread_id) |
{ |
THREADS_FPRINTF((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) thread_id)); |
free_object(o); |
error("Recursive mutex locks!\n"); |
} |
} |
|
if(m->key) |
{ |
SWAP_OUT_CURRENT_THREAD(); |
do |
{ |
THREADS_FPRINTF((stderr,"WAITING TO LOCK m:%08x\n",(unsigned int)m)); |
co_wait(& m->condition, & interpreter_lock); |
}while(m->key); |
SWAP_IN_CURRENT_THREAD(); |
} |
m->key=o; |
OB2KEY(o)->mut=m; |
|
THREADS_FPRINTF((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)thread_id)); |
pop_n_elems(args); |
push_object(o); |
} |
|
void f_mutex_trylock(INT32 args) |
{ |
struct mutex_storage *m; |
struct object *o; |
int i=0; |
|
o=clone_object(mutex_key,0); |
m=THIS_MUTEX; |
|
|
|
|
|
if(!args || IS_ZERO(sp-args)) |
{ |
if(m->key && OB2KEY(m->key)->owner == thread_id) |
{ |
free_object(o); |
error("Recursive mutex locks!\n"); |
} |
} |
|
if(!m->key) |
{ |
OB2KEY(o)->mut=m; |
m->key=o; |
i=1; |
} |
|
pop_n_elems(args); |
if(i) |
{ |
push_object(o); |
} else { |
destruct(o); |
free_object(o); |
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((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 *)(fp->current_storage)) |
void init_mutex_key_obj(struct object *o) |
{ |
THREADS_FPRINTF((stderr, "KEY k:%08x, o:%08x\n", |
(unsigned int)THIS_KEY, (unsigned int)thread_id)); |
THIS_KEY->mut=0; |
THIS_KEY->owner=thread_id; |
thread_id->refs++; |
THIS_KEY->initialized=1; |
} |
|
void exit_mutex_key_obj(struct object *o) |
{ |
THREADS_FPRINTF((stderr, "UNLOCK k:%08x m:(%08x) t:%08x o:%08x\n", |
(unsigned int)THIS_KEY, |
(unsigned int)THIS_KEY->mut, |
(unsigned int)thread_id, |
(unsigned int)THIS_KEY->owner)); |
if(THIS_KEY->mut) |
{ |
struct mutex_storage *mut = THIS_KEY->mut; |
|
#ifdef DEBUG |
if(mut->key != o) |
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; |
} |
THIS_KEY->mut=0; |
THIS_KEY->initialized=0; |
co_signal(& mut->condition); |
} |
} |
|
#define THIS_COND ((COND_T *)(fp->current_storage)) |
void f_cond_wait(INT32 args) |
{ |
COND_T *c; |
struct object *key; |
|
if(args > 1) pop_n_elems(args - 1); |
|
c=THIS_COND; |
|
if(args > 0) |
{ |
struct mutex_storage *mut; |
|
if(sp[-1].type != T_OBJECT) |
error("Bad argument 1 to condition->wait()\n"); |
|
key=sp[-1].u.object; |
|
if(key->prog != mutex_key) |
error("Bad argument 1 to condition->wait()\n"); |
|
mut=OB2KEY(key)->mut; |
if(!mut) error("Bad argument 1 to condition->wait()\n"); |
|
|
mut->key=0; |
OB2KEY(key)->mut=0; |
co_signal(& mut->condition); |
|
|
SWAP_OUT_CURRENT_THREAD(); |
co_wait(c, &interpreter_lock); |
|
if(OB2KEY(key)->initialized) |
{ |
|
while(mut->key) co_wait(& mut->condition, &interpreter_lock); |
mut->key=key; |
OB2KEY(key)->mut=mut; |
} |
SWAP_IN_CURRENT_THREAD(); |
pop_stack(); |
} else { |
SWAP_OUT_CURRENT_THREAD(); |
co_wait(c, &interpreter_lock); |
SWAP_IN_CURRENT_THREAD(); |
} |
} |
|
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) |
{ |
struct thread_state *foo = (struct thread_state *)fp->current_object->storage; |
struct thread_state *bar = (struct thread_state *)thread_id->storage; |
struct svalue *osp = sp; |
pop_n_elems(args); |
if(foo->sp) |
{ |
SWAP_OUT_THREAD(bar); |
SWAP_IN_THREAD(foo); |
sp=osp; |
f_backtrace(0); |
osp=sp; |
sp=foo->sp; |
SWAP_OUT_THREAD(foo); |
SWAP_IN_THREAD(bar); |
sp=osp; |
} else { |
push_int(0); |
f_allocate(1); |
} |
} |
|
void f_thread_id_status(INT32 args) |
{ |
pop_n_elems(args); |
push_int(THIS_THREAD->status); |
} |
|
static void f_thread_id_result(INT32 args) |
{ |
struct thread_state *th=THIS_THREAD; |
|
SWAP_OUT_CURRENT_THREAD(); |
|
while(th->status != THREAD_EXITED) |
co_wait(&th->status_change, &interpreter_lock); |
|
SWAP_IN_CURRENT_THREAD(); |
|
low_object_index_no_free(sp, |
fp->current_object, |
thread_id_result_variable); |
sp++; |
} |
|
void init_thread_obj(struct object *o) |
{ |
MEMSET(o->storage, 0, sizeof(struct thread_state)); |
THIS_THREAD->status=THREAD_NOT_STARTED; |
co_init(& THIS_THREAD->status_change); |
} |
|
|
void exit_thread_obj(struct object *o) |
{ |
co_destroy(& THIS_THREAD->status_change); |
} |
|
#ifdef DEBUG |
static void thread_was_marked(struct object *o) |
{ |
struct thread_state *tmp=(struct thread_state *)(o->storage); |
if(tmp->swapped) |
{ |
debug_gc_xmark_svalues(tmp->evaluator_stack,tmp->sp-tmp->evaluator_stack-1,"idle thread stack"); |
} |
} |
#endif |
|
void th_init(void) |
{ |
struct program *tmp; |
INT32 mutex_key_offset; |
|
#ifdef SGI_SPROC_THREADS |
#error /* Need to specify a filename */ |
us_cookie = usinit(""); |
#endif /* SGI_SPROC_THREADS */ |
|
THREADS_FPRINTF((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); |
mt_lock( & interpreter_lock); |
#ifdef POSIX_THREADS |
pthread_attr_init(&pattr); |
#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE |
pthread_attr_setstacksize(&pattr, 2 * 1024 * 1204); |
#endif |
pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED); |
#endif |
|
add_efun("thread_create",f_thread_create,"function(mixed ...:object)", |
OPT_SIDE_EFFECT); |
#ifdef UNIX_THREADS |
add_efun("thread_set_concurrency",f_thread_set_concurrency, |
"function(int:void)", OPT_SIDE_EFFECT); |
#endif |
add_efun("this_thread",f_this_thread,"function(:object)", |
OPT_EXTERNAL_DEPEND); |
|
start_new_program(); |
add_storage(sizeof(struct mutex_storage)); |
add_function("lock",f_mutex_lock,"function(int|void:object)",0); |
add_function("trylock",f_mutex_trylock,"function(int|void:object)",0); |
set_init_callback(init_mutex_obj); |
set_exit_callback(exit_mutex_obj); |
end_class("mutex", 0); |
|
start_new_program(); |
mutex_key_offset = add_storage(sizeof(struct key_storage)); |
|
|
|
map_variable("_owner", "object", 0, |
mutex_key_offset + OFFSETOF(key_storage, owner), T_OBJECT); |
set_init_callback(init_mutex_key_obj); |
set_exit_callback(exit_mutex_key_obj); |
mutex_key=end_program(); |
mutex_key->flags|=PROG_DESTRUCT_IMMEDIATE; |
if(!mutex_key) |
fatal("Failed to initialize mutex_key program!\n"); |
|
start_new_program(); |
add_storage(sizeof(COND_T)); |
add_function("wait",f_cond_wait,"function(void|object:void)",0); |
add_function("signal",f_cond_signal,"function(:void)",0); |
add_function("broadcast",f_cond_broadcast,"function(:void)",0); |
set_init_callback(init_cond_obj); |
set_exit_callback(exit_cond_obj); |
end_class("condition", 0); |
|
start_new_program(); |
add_storage(sizeof(struct thread_state)); |
thread_id_result_variable=simple_add_variable("result","mixed",0); |
add_function("backtrace",f_thread_backtrace,"function(:array)",0); |
add_function("wait",f_thread_id_result,"function(:mixed)",0); |
add_function("status",f_thread_id_status,"function(:int)",0); |
#ifdef DEBUG |
set_gc_mark_callback(thread_was_marked); |
#endif |
set_init_callback(init_thread_obj); |
set_exit_callback(exit_thread_obj); |
thread_id_prog=end_program(); |
if(!mutex_key) |
fatal("Failed to initialize thread program!\n"); |
|
thread_id=clone_object(thread_id_prog,0); |
SWAP_OUT_THREAD((struct thread_state *)thread_id->storage); |
((struct thread_state *)thread_id->storage)->swapped=0; |
} |
|
void th_cleanup(void) |
{ |
if(mutex_key) |
{ |
free_program(mutex_key); |
mutex_key=0; |
} |
|
if(thread_id_prog) |
{ |
free_program(thread_id_prog); |
thread_id_prog=0; |
} |
|
if(thread_id) |
{ |
destruct(thread_id); |
free_object(thread_id); |
thread_id=0; |
} |
} |
|
#endif |
|
|