pike.git / src / threads.c

version» Context lines:

pike.git/src/threads.c:1:   #include "global.h" - RCSID("$Id: threads.c,v 1.150 2001/01/24 08:17:28 hubbe Exp $"); + RCSID("$Id: threads.c,v 1.151 2001/02/01 19:58:56 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"
pike.git/src/threads.c:282:    threads_disabled = 1;    } else {    threads_disabled++;    }       THREADS_FPRINTF(0,    (stderr, "low_init_threads_disable(): threads_disabled:%d\n",    threads_disabled));   }    + /*! @decl object(_disable_threads) _disable_threads() +  *! +  *! This function first posts a notice to all threads that it is time to stop. +  *! It then waits until all threads actually *have* stopped, and then then +  *! returns an object. All other threads will be blocked from running until +  *! that object has been freed/destroyed. +  *! +  *! @note +  *! This function can completely block Pike if used incorrectly. +  *! Use with extreme caution. +  */   void init_threads_disable(struct object *o)   {    low_init_threads_disable();       if(live_threads) {    SWAP_OUT_CURRENT_THREAD();    while (live_threads) {    THREADS_FPRINTF(0,    (stderr,    "_disable_threads(): Waiting for %d threads to finish\n",
pike.git/src/threads.c:503:   }      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));    /* See NB in thread_state_for_id. Lifespan of result can be prolonged    by incrementing refcount though. */   }    + /*! @module Thread +  */    -  + /*! @decl array(Thread.Thread) all_threads() +  *! +  *! This function returns an array with the thread ids of all threads. +  *! +  *! @seealso +  *! @[Thread.Thread()] +  */   PMOD_EXPORT void f_all_threads(INT32 args)   {    /* Return an unordered array containing all threads that was running    at the time this function was invoked */       INT32 x;    struct svalue *oldsp;    struct thread_state *s;       pop_n_elems(args);
pike.git/src/threads.c:672:    mt_unlock_interpreter();    th_exit(0);    /* NOT_REACHED, but removes a warning */    return(0);   }      #ifdef UNIX_THREADS   int num_lwps = 1;   #endif    + /*! @class Thread +  */ +  + /* @decl void create(function(mixed...:void) f, mixed ... args) +  *! +  *! This function creates a new thread which will run simultaneously +  *! to the rest of the program. The new thread will call the function +  *! @[f] with the arguments @[args]. When @[f] returns the thread will cease +  *! to exist. +  *! +  *! All Pike functions are 'thread safe' meaning that running +  *! a function at the same time from different threads will not corrupt +  *! any internal data in the Pike process. +  *! +  *! @returns +  *! The returned value will be the same as the return value of +  *! @[Thread.this_thread()] for the new thread. +  *! +  *! @note +  *! This function is only available on systems with POSIX or UNIX or WIN32 +  *! threads support. +  *! +  *! @seealso +  *! @[Mutex], @[Condition], @[this_thread()] +  */   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);    OBJ2THREAD(arg->id)->status=THREAD_RUNNING;   
pike.git/src/threads.c:715:    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);    }   }    + /*! @endclass +  */ +    #ifdef UNIX_THREADS -  + /*! @decl void thread_set_concurrency(int concurrency) +  */   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    -  + /*! @decl Thread.Thread this_thread() +  *! +  *! This function returns the object that identifies this thread. +  *! +  *! @seealso +  *! @[Thread.Thread()] +  */   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))         /* Note:
pike.git/src/threads.c:757:      struct key_storage   {    struct mutex_storage *mut;    struct object *owner;    int initialized;   };      #define OB2KEY(X) ((struct key_storage *)((X)->storage))    + /*! @class Mutex +  *! +  *! @[Thread.Mutex] is a class that implements mutual exclusion locks. +  *! Mutex locks are used to prevent multiple threads from simultaneously +  *! execute sections of code which access or change shared data. The basic +  *! operations for a mutex is locking and unlocking. If a thread attempts +  *! to lock an already locked mutex the thread will sleep until the mutex +  *! is unlocked. +  *! +  *! @note +  *! This class is simulated when Pike is compiled without thread support, +  *! so it's always available. +  *! +  *! In POSIX threads, mutex locks can only be unlocked by the same thread +  *! that locked them. In Pike any thread can unlock a locked mutex. +  */ +  + /*! @decl MutexKey lock() +  *! @decl MutexKey lock(int type) +  *! +  *! This function attempts to lock the mutex. If the mutex is already +  *! locked by a different thread the current thread will sleep until the +  *! mutex is unlocked. The value returned is the 'key' to the lock. When +  *! the key is destructed or has no more references the lock will +  *! automatically be unlocked. The key will also be destructed if the lock +  *! is destructed. +  *! +  *! The @[type] argument specifies what @[lock()] should do if the +  *! mutex is already locked by this thread: +  *! @int +  *! @value 0 (default) +  *! Throw an error. +  *! @value 1 +  *! Sleep until the mutex is unlocked. Useful if some +  *! other thread will unlock it. +  *! @value 2 +  *! Return zero. This allows recursion within a locked region of +  *! code, but in conjunction with other locks it easily leads +  *! to unspecified locking order and therefore a risk for deadlocks. +  *! @endint +  *! +  *! @seealso +  *! @[trylock()] +  */   void f_mutex_lock(INT32 args)   {    struct mutex_storage *m;    struct object *o;    INT_TYPE type;       m=THIS_MUTEX;    if(!args)    type=0;    else
pike.git/src/threads.c:835:       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);   }    + /*! @decl MutexKey trylock() +  *! @decl MutexKey trylock(int type) +  *! +  *! This function performs the same operation as @[lock()], but if the mutex +  *! is already locked, it will return zero instead of sleeping until it's +  *! unlocked. +  *! +  *! @seealso +  *! @[lock()] +  */   void f_mutex_trylock(INT32 args)   {    struct mutex_storage *m;    struct object *o;    INT_TYPE type;    int i=0;       /* No reason to release the interpreter lock here    * since we aren't calling any functions that take time.    */
pike.git/src/threads.c:905:      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);   }    + /*! @endclass +  */ +    #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;    add_ref(THIS_KEY->owner=Pike_interpreter.thread_id);    THIS_KEY->initialized=1;   }   
pike.git/src/threads.c:942:    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 *)(CURRENT_STORAGE)) +  + /*! @class Condition +  *! +  *! Implementation of condition variables. +  *! +  *! Condition variables are used by threaded programs +  *! to wait for events happening in other threads. +  *! +  *! @note +  *! Condition variables are only available on systems with thread +  *! support. The Condition class is not simulated otherwise, since that +  *! can't be done accurately without continuations. +  *! +  *! @seealso +  *! @[Thread.Mutex] +  */ +  + /*! @decl void wait() +  *! @decl void wait(Thread.MutexKey mutex_key) +  *! +  *! Wait for contition. +  *! +  *! This function makes the current thread sleep until the condition +  *! variable is signalled. The optional argument should be the 'key' +  *! to a mutex lock. If present the mutex lock will be unlocked before +  *! waiting for the condition in one atomic operation. After waiting +  *! for the condition the mutex referenced by mutex_key will be re-locked. +  *! +  *! @seealso +  *! @[Thread.Mutex->lock()] +  */   void f_cond_wait(INT32 args)   {    COND_T *c;       if(threads_disabled)    Pike_error("Cannot wait for conditions when threads are disabled!\n");       if(args > 1) {    pop_n_elems(args - 1);    args = 1;
pike.git/src/threads.c:1003:    }    }       SWAP_OUT_CURRENT_THREAD();    co_wait_interpreter(c);    SWAP_IN_CURRENT_THREAD();       pop_n_elems(args);   }    + /*! @decl void signal() +  *! +  *! @[signal()] wakes up one of the threads currently waiting for the +  *! condition. +  *! +  *! @bugs +  *! Sometimes more than one thread is woken up. +  *! +  *! @seealso +  *! @[broadcast()] +  */   void f_cond_signal(INT32 args) { pop_n_elems(args); co_signal(THIS_COND); } -  +  + /*! @decl void broadcast() +  *! +  *! @[broadcast()] wakes up all threads currently waiting for this condition. +  *! +  *! @seealso +  *! @[signal()] +  */   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); }    -  + /*! @endclass +  */ +  + /*! @class Thread +  */ +    /* FIXME: -Hubbe */ -  + /*! @decl array(mixed) backtrace() +  *! +  *! Returns the current call stack for the thread. +  *! +  *! @returns +  *! The result has the same format as for @[predef::backtrace()]. +  *! +  *! @seealso +  *! @[predef::backtrace()] +  */   void f_thread_backtrace(INT32 args)   {    struct thread_state *foo = THIS_THREAD;    struct thread_state *bar = OBJ2THREAD( Pike_interpreter.thread_id );    struct svalue *osp = Pike_sp;    pop_n_elems(args);    if(foo->state.stack_pointer)    {    SWAP_OUT_THREAD(bar);    SWAP_IN_THREAD(foo);
pike.git/src/threads.c:1032:    Pike_sp=foo->state.stack_pointer;    SWAP_OUT_THREAD(foo);    SWAP_IN_THREAD(bar);    Pike_sp=osp;    } else {    push_int(0);    f_allocate(1);    }   }    + /*! @decl int status() +  */   void f_thread_id_status(INT32 args)   {    pop_n_elems(args);    push_int(THIS_THREAD->status);   }    -  + /*! @decl static string _sprintf(int c) +  *! +  *! Returns a string identifing the thread. +  */   void f_thread_id__sprintf (INT32 args)   {    pop_n_elems (args);    push_constant_text ("Thread.Thread(");    push_int64((ptrdiff_t)THIS_THREAD->id);    push_constant_text (")");    f_add (3);   }    -  + /*! @decl mixed result() +  *! +  *! Waits for the thread to complete, and then returns +  *! the value returned from the thread function. +  */   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_interpreter(&th->status_change);       SWAP_IN_CURRENT_THREAD();
pike.git/src/threads.c:1083:   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);   }    + /*! @end class +  */ +    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;
pike.git/src/threads.c:1117: Inside #if defined(PIKE_DEBUG)
   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   }    + /*! @class Local +  *! +  *! Thread local variable storage. +  *! +  *! This class allows you to have variables which are separate for each +  *! thread that uses it. It has two methods: @[get()] and @[set()]. A value +  *! stored in an instance of @[Thread.Local] can only be retrieved by that +  *! same thread. +  *! +  *! @note +  *! This class is simulated when Pike is compiled without thread support, +  *! so it's always available. +  */ +  + /* FIXME: Why not use an init-callback? */   PMOD_EXPORT void f_thread_local(INT32 args)   {    static INT32 thread_local_id = 0;       struct object *loc = clone_object(thread_local_prog,0);    ((struct thread_local *)loc->storage)->id = thread_local_id++;    pop_n_elems(args);    push_object(loc);   }    -  + /*! @decl mixed get() +  *! +  *! Get the thread local value. +  *! +  *! This returns the value prevoiusly stored in the @[Thread.Local] object by +  *! the @[set()] method by this thread. +  *! +  *! @seealso +  *! @[set()] +  */   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;    }   }    -  + /*! @decl mixed set(mixed value) +  *! +  *! Set the thread local value. +  *! +  *! This sets the value returned by the @[get] method. +  *! +  *! Calling this method does not affect the value returned by @[get()] when +  *! it's called by another thread (ie multiple values can be stored at the +  *! same time, but only one value per thread). +  *! +  *! @returns +  *! This function returns its argument. +  *! +  *! @note +  *! Note that the value set can only be retreived by the same thread. +  *! +  *! @seealso +  *! @[get()] +  */   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.git/src/threads.c:1166:    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]);   }    + /*! @endclass +  */ +    /* Thread farm code by Per    *    */   static struct farmer {    struct farmer *neighbour;    void *field;    void (*harvest)(void *);    THREAD_T me;    COND_T harvest_moon;   #ifdef HAVE_BROKEN_LINUX_THREAD_EUID
pike.git/src/threads.c:1342: Inside #if defined(UNIX_THREADS)
     #ifdef UNIX_THREADS      /* function(int:void) */    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);    /* This is needed to allow the gc to find the possible circular reference. -  * It also allows a process to take over ownership of a key. +  * It also allows a thread to take over ownership of a key.    */    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=Pike_compiler->new_program;    add_ref(mutex_key);    end_class("mutex_key", 0);    mutex_key->flags|=PROGRAM_DESTRUCT_IMMEDIATE;   #ifdef PIKE_DEBUG