pike.git / src / threads.c

version» Context lines:

pike.git/src/threads.c:32:   #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 "backend.h"   #include "pike_rusage.h"   #include "pike_cpulib.h" + #include "pike_compiler.h"      #include <errno.h>   #include <math.h>   #include <time.h>      #ifdef HAVE_SYS_PRCTL_H   #include <sys/prctl.h>   #endif /* HAVE_SYS_PRCTL_H */      #ifdef HAVE_SYS_TIME_H
pike.git/src/threads.c:387:   static THREAD_T threads_disabled_thread = 0;   #endif   #ifdef INTERNAL_PROFILING   PMOD_EXPORT unsigned long thread_yields = 0;   #endif   static int th_running = 0;   static PIKE_MUTEX_T interpreter_lock;   static PIKE_MUTEX_T interpreter_lock_wanted;   PIKE_MUTEX_T thread_table_lock;   static PIKE_MUTEX_T interleave_lock; + static struct program *mutex_program = NULL;   static struct program *mutex_key = 0; -  + static struct program *condition_program = NULL;   PMOD_EXPORT struct program *thread_id_prog = 0;   static struct program *thread_local_prog = 0;   PMOD_EXPORT ptrdiff_t thread_storage_offset;   static int live_threads = 0;   static COND_T live_threads_change;   static COND_T threads_disabled_change;      struct thread_local_var   {    INT32 id;
pike.git/src/threads.c:425:    if (th_running) {    THREAD_T self;    if (!debug_is_locked)    pike_fatal_dloc ("Interpreter not locked.\n");    self = th_self();    if (!th_equal (debug_locking_thread, self))    pike_fatal_dloc ("Interpreter not locked by this thread.\n");    }   }    - static unsigned INT64 thread_swaps = 0; - static unsigned INT64 check_threads_calls = 0; - static unsigned INT64 check_threads_yields = 0; - static unsigned INT64 check_threads_swaps = 0; + static UINT64 thread_swaps = 0; + static UINT64 check_threads_calls = 0; + static UINT64 check_threads_yields = 0; + static UINT64 check_threads_swaps = 0;   static void f__thread_swaps (INT32 UNUSED(args))    {push_ulongest (thread_swaps);}   static void f__check_threads_calls (INT32 UNUSED(args))    {push_ulongest (check_threads_calls);}   static void f__check_threads_yields (INT32 UNUSED(args))    {push_ulongest (check_threads_yields);}   static void f__check_threads_swaps (INT32 UNUSED(args))    {push_ulongest (check_threads_swaps);}      #else
pike.git/src/threads.c:531:    USE_DLOC_ARGS();    THREADS_FPRINTF (1, "Releasing iplock @ %s:%d\n", DLOC_ARGS_OPT);    UNSET_LOCKING_THREAD;    mt_unlock (&interpreter_lock);   }      PMOD_EXPORT void pike_wait_interpreter (COND_T *cond COMMA_DLOC_DECL)   {    int owner = threads_disabled;    pike_low_wait_interpreter (cond COMMA_DLOC_ARGS_OPT); -  if (!owner && threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT); +  if (!owner && threads_disabled) { +  /* Some other thread has disabled threads while we were waiting +  * for the cond var. We must wait for threads to be reenabled +  * before proceeding. +  */ +  threads_disabled_wait (DLOC_ARGS_OPT);    } -  + }      PMOD_EXPORT int pike_timedwait_interpreter (COND_T *cond,    long sec, long nsec    COMMA_DLOC_DECL)   {    int owner = threads_disabled;    int res = pike_low_timedwait_interpreter (cond, sec, nsec    COMMA_DLOC_ARGS_OPT); -  if (!owner && threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT); +  if (!owner && threads_disabled) { +  /* Some other thread has disabled threads while we were waiting +  * for the cond var. We must wait for threads to be reenabled +  * before proceeding. +  */ +  threads_disabled_wait (DLOC_ARGS_OPT); +  }    return res;   }      PMOD_EXPORT void pike_init_thread_state (struct thread_state *ts)   {    /* NB: Assumes that there's a temporary Pike_interpreter_struct    * in Pike_interpreter_pointer, which we copy, and replace    * with ourselves.    */    Pike_interpreter.thread_state = ts;
pike.git/src/threads.c:715:    *!    *! @seealso    *! @[get_thread_quanta()], @[set_thread_quanta()]    */      /*! @endclass    */      PMOD_EXPORT void pike_threads_allow (struct thread_state *ts COMMA_DLOC_DECL)   { - #ifdef DO_PIKE_CLEANUP -  /* Might get here after th_cleanup() when reporting leaks. */ +  /* May get here after th_cleanup() when reporting leaks. */    if (!ts) return; - #endif +        if (UNLIKELY(thread_quanta > 0)) {    cpu_time_t now = get_real_time();       if (UNLIKELY((now - ts->interval_start) > thread_quanta) &&    LIKELY(ts->thread_obj)) {    ref_push_object(ts->thread_obj);    push_int64(now - ts->interval_start);    ts->interval_start = now;   #ifndef LONG_CPU_TIME
pike.git/src/threads.c:764: Inside #if defined (PIKE_DEBUG)
   pike_fatal_dloc ("Threads allowed from a different thread "    "while threads are disabled. "    "(self: %"PRINTSIZET"x, disabler: %"PRINTSIZET"x)\n",    (size_t) self, (size_t) threads_disabled_thread);    }   #endif   }      PMOD_EXPORT void pike_threads_disallow (struct thread_state *ts COMMA_DLOC_DECL)   { - #ifdef DO_PIKE_CLEANUP +  /* May get here if early init code throws errors. */    if (!ts) return; - #endif +        if (ts->swapped) {    pike_lock_interpreter (DLOC_ARGS_OPT);    pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT);    }       if (UNLIKELY(thread_quanta)) {    ts->interval_start = get_real_time();    }   
pike.git/src/threads.c:896:   /* This is a variant of init_threads_disable that blocks all other    * threads that might run pike code, but still doesn't block the    * THREADS_ALLOW_UID threads. */   void low_init_threads_disable(void)   {    /* Serious black magic to avoid dead-locks */       if (!threads_disabled) {    THREADS_FPRINTF(0, "low_init_threads_disable(): Locking IM's...\n");    +  lock_pike_compiler(); +     if (Pike_interpreter.thread_state) {    /* Threads have been enabled. */       IMUTEX_T *im;       THREADS_ALLOW();       /* Keep this the entire session. */    mt_lock(&interleave_lock);   
pike.git/src/threads.c:1004:    IMUTEX_T *im = (IMUTEX_T *)interleave_list;    threads_disabled_acc_time += get_real_time() - threads_disabled_start;       /* Order shouldn't matter for unlock, so no need to do it backwards. */    while(im) {    THREADS_FPRINTF(0, "exit_threads_disable(): Unlocking IM %p\n", im);    mt_unlock(&(im->lock));    im = im->next;    }    +  unlock_pike_compiler(); +     mt_unlock(&interleave_lock);       THREADS_FPRINTF(0, "exit_threads_disable(): Wake up!\n");    co_broadcast(&threads_disabled_change);   #ifdef PIKE_DEBUG    threads_disabled_thread = 0;   #endif    }   #ifdef PIKE_DEBUG    } else {
pike.git/src/threads.c:1190:   }      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. */   }    + static inline void CALL_WITH_ERROR_HANDLING(struct thread_state *state, +  void (*func)(void *ctx), +  void *ctx) + { +  JMP_BUF back; +  +  if(SETJMP(back)) +  { +  if(throw_severity <= THROW_ERROR) { +  if (state->thread_obj) { +  /* Copy the thrown exit value to the thread_state here, +  * if the thread hasn't been destructed. */ +  assign_svalue(&state->result, &throw_value); +  } +  +  call_handle_error(); +  } +  +  if(throw_severity == THROW_EXIT) +  { +  /* This is too early to get a clean exit if DO_PIKE_CLEANUP is +  * active. Otoh it cannot be done later since it requires the +  * evaluator stacks in the gc calls. It's difficult to solve +  * without handing over the cleanup duty to the main thread. */ +  pike_do_exit(throw_value.u.integer); +  } +  } else { +  back.severity=THROW_EXIT; +  func(ctx); +  } +  +  UNSETJMP(back); + } +    PMOD_EXPORT void call_with_interpreter(void (*func)(void *ctx), void *ctx)   {    struct thread_state *state;       if((state = thread_state_for_id(th_self()))!=NULL) {    /* This is a pike thread. Do we have the interpreter lock? */    if(!state->swapped) {    /* Yes. Go for it... */   #ifdef PIKE_DEBUG    /* Ensure that the thread isn't loose. */    int is_loose = state->debug_flags & THREAD_DEBUG_LOOSE;    state->debug_flags &= ~THREAD_DEBUG_LOOSE;   #endif    -  func(ctx); +  CALL_WITH_ERROR_HANDLING(state, func, ctx);      #ifdef PIKE_DEBUG    /* Restore the looseness property of the thread. */    state->debug_flags |= is_loose;   #endif    } else {    /* Nope, let's get it... */    mt_lock_interpreter();    SWAP_IN_THREAD(state);    DO_IF_DEBUG(state->debug_flags &= ~THREAD_DEBUG_LOOSE;)    -  func(ctx); +  CALL_WITH_ERROR_HANDLING(state, func, ctx);       /* Restore */    DO_IF_DEBUG(state->debug_flags |= THREAD_DEBUG_LOOSE;)    SWAP_OUT_THREAD(state);    mt_unlock_interpreter();    }    } else {    /* Not a pike thread. Create a temporary thread_id... */    struct object *thread_obj;    struct Pike_interpreter_struct new_interpreter;
pike.git/src/threads.c:1239:    Pike_interpreter_pointer = &new_interpreter;    init_interpreter();    Pike_interpreter.stack_top=((char *)&state)+ (thread_stack_size-16384) * STACK_DIRECTION;    Pike_interpreter.recoveries = NULL;    thread_obj = fast_clone_object(thread_id_prog);    INIT_THREAD_STATE((struct thread_state *)(thread_obj->storage +    thread_storage_offset));    num_threads++;    thread_table_insert(Pike_interpreter.thread_state);    -  func(ctx); +  CALL_WITH_ERROR_HANDLING(Pike_interpreter.thread_state, func, ctx);       cleanup_interpret(); /* Must be done before EXIT_THREAD_STATE */    Pike_interpreter.thread_state->status=THREAD_EXITED;    co_signal(&Pike_interpreter.thread_state->status_change);    thread_table_delete(Pike_interpreter.thread_state);    EXIT_THREAD_STATE(Pike_interpreter.thread_state);    Pike_interpreter.thread_state=NULL;    free_object(thread_obj);    thread_obj = NULL;    num_threads--; -  mt_unlock_interpreter(); +    #ifdef PIKE_DEBUG    Pike_interpreter_pointer = NULL;   #endif -  +  mt_unlock_interpreter();    }   }      PMOD_EXPORT void enable_external_threads(void)   {    num_threads++;   }      PMOD_EXPORT void disable_external_threads(void)   {
pike.git/src/threads.c:1605: Inside #if defined(PROFILE_CHECK_THREADS)
   tps_int_mean,    tps_int_n > 1 ? sqrt (tps_int_m2 / (tps_int_n - 1)) : 0.0);    last_time = (unsigned long) now.tv_sec;    calls = yields = clock_checks = no_clock_advs = 0;    }    }   #endif       {   #ifdef PIKE_DEBUG -  unsigned INT64 old_thread_swaps = thread_swaps; +  UINT64 old_thread_swaps = thread_swaps;   #endif    pike_thread_yield();   #ifdef PIKE_DEBUG    if (thread_swaps != old_thread_swaps)    check_threads_swaps++;   #endif    }   }      PMOD_EXPORT void pike_thread_yield(void)
pike.git/src/threads.c:1927:    *    * The main race is the one on current_object,    * since it at this point only has one reference.    *    * We also want the stuff in arg to be copied properly    * before we exit the function...    */    SWAP_OUT_CURRENT_THREAD();    THREADS_FPRINTF(0, "f_thread_create %p waiting...\n", thread_state);    while (thread_state->status == THREAD_NOT_STARTED) -  low_co_wait_interpreter (&thread_state->status_change); +  co_wait_interpreter (&thread_state->status_change);    THREADS_FPRINTF(0, "f_thread_create %p continue\n", thread_state);    SWAP_IN_CURRENT_THREAD();    } else {    low_cleanup_interpret(&thread_state->state);    free_array(arg.args);    Pike_error("Failed to create thread (errno = %d).\n", tmp);    }       THREADS_FPRINTF(0, "f_thread_create %p done\n", thread_state);   }
pike.git/src/threads.c:2147:    struct mutex_storage *m;    struct object *o;    INT_TYPE type;       DEBUG_CHECK_THREAD();       m=THIS_MUTEX;    if(!args)    type=0;    else -  get_all_args("lock",args,"%i",&type); +  get_all_args(NULL, args, "%i", &type);       switch(type)    {    default: -  bad_arg_error("lock", Pike_sp-args, args, 2, "int(0..2)", Pike_sp+1-args, +  bad_arg_error("lock", args, 2, "int(0..2)", Pike_sp+1-args,    "Unknown mutex locking style: %"PRINTPIKEINT"d\n",type);          case 0:    case 2:    if(m->key && OB2KEY(m->key)->owner == Pike_interpreter.thread_state)    {    THREADS_FPRINTF(0,    "Recursive LOCK k:%p, m:%p(%p), t:%p\n",    OB2KEY(m->key), m, OB2KEY(m->key)->mut,
pike.git/src/threads.c:2250:       /* No reason to release the interpreter lock here    * since we aren't calling any functions that take time.    */       m=THIS_MUTEX;       if(!args)    type=0;    else -  get_all_args("trylock",args,"%i",&type); +  get_all_args(NULL, args, "%i", &type);       switch(type)    {    default: -  bad_arg_error("trylock", Pike_sp-args, args, 2, "int(0..2)", Pike_sp+1-args, +  bad_arg_error("trylock", args, 2, "int(0..2)", Pike_sp+1-args,    "Unknown mutex locking style: %"PRINTPIKEINT"d\n",type);       case 0:    if(m->key && OB2KEY(m->key)->owner == Pike_interpreter.thread_state)    {    Pike_error("Recursive mutex locks!\n");    }       case 2:    case 1:
pike.git/src/threads.c:2357:    push_static_text("Thread.Mutex(/*locked by 0x");    push_int64(PTR_TO_INT(THREAD_T_TO_PTR(OB2KEY(m->key)->owner->id)));    f_int2hex(1);    push_static_text("*/)");    f_add(3);    } else {    push_static_text("Thread.Mutex()");    }   }    + /*! @decl Condition cond() +  *! +  *! Create a @[Condition]. +  *! +  *! This function is equivalent to +  *! @code +  *! Thread.Condition(mux); +  *! @endcode +  *! +  *! @seealso +  *! @[Condition] +  */ + void f_mutex_cond(INT32 args) + { +  ref_push_object_inherit(Pike_fp->current_object, +  Pike_fp->context - +  Pike_fp->current_program->inherits); +  push_object(clone_object(condition_program, 1)); + } +    void init_mutex_obj(struct object *UNUSED(o))   {    co_init(& THIS_MUTEX->condition); -  + #ifdef PIKE_NULL_IS_SPECIAL    THIS_MUTEX->key=0;    THIS_MUTEX->num_waiting = 0; -  + #endif   }      void exit_mutex_obj(struct object *UNUSED(o))   {    struct mutex_storage *m = THIS_MUTEX;    struct object *key = m->key;       THREADS_FPRINTF(1, "DESTROYING MUTEX m:%p\n", THIS_MUTEX);      #ifndef PICKY_MUTEX
pike.git/src/threads.c:2390: Inside #if undefined(PICKY_MUTEX)
  #endif    co_destroy(& m->condition);    }   #else    if(key) {    m->key=0;    destruct(key); /* Will destroy m->condition if m->num_waiting is zero. */    if(m->num_waiting)    {    THREADS_FPRINTF(1, "Destructed mutex is being waited on.\n"); +  THREADS_ALLOW();    /* exit_mutex_key_obj has already signalled, but since the    * waiting threads will throw an error instead of making a new    * lock we need to double it to a broadcast. The last thread    * that stops waiting will destroy m->condition. */    co_broadcast (&m->condition); -  +  +  /* Try to wake up the waiting thread(s) immediately +  * in an attempt to avoid starvation. +  */ + #ifdef HAVE_NO_YIELD +  sleep(0); + #else /* HAVE_NO_YIELD */ +  th_yield(); + #endif /* HAVE_NO_YIELD */ +  THREADS_DISALLOW();    }    }    else {    co_destroy(& m->condition);    }   #endif   }    - void exit_mutex_obj_compat_7_4(struct object *UNUSED(o)) - { -  struct mutex_storage *m = THIS_MUTEX; -  struct object *key = m->key; -  -  THREADS_FPRINTF(1, "DESTROYING MUTEX m:%p\n", THIS_MUTEX); -  -  if(key) { -  m->key=0; -  destruct(key); /* Will destroy m->condition if m->num_waiting is zero. */ -  } -  else { - #ifdef PIKE_DEBUG -  if (m->num_waiting) -  Pike_error ("key/num_waiting confusion.\n"); - #endif -  co_destroy(& m->condition); -  } - } -  +    /*! @endclass    */      /*! @class MutexKey    *!    *! Objects of this class are returned by @[Mutex()->lock()]    *! and @[Mutex()->trylock()]. They are also passed as arguments    *! to @[Condition()->wait()].    *!    *! As long as they are held, the corresponding mutex will be locked.
pike.git/src/threads.c:2445:    *! is destructed (eg by not having any references left).    *!    *! @seealso    *! @[Mutex], @[Condition]    */   #define THIS_KEY ((struct key_storage *)(CURRENT_STORAGE))   void init_mutex_key_obj(struct object *UNUSED(o))   {    THREADS_FPRINTF(1, "KEY k:%p, t:%p\n",    THIS_KEY, Pike_interpreter.thread_state); + #ifdef PIKE_NULL_IS_SPECIAL    THIS_KEY->mut=0;    THIS_KEY->mutex_obj = NULL; -  + #endif    THIS_KEY->owner = Pike_interpreter.thread_state;    THIS_KEY->owner_obj = Pike_interpreter.thread_state->thread_obj;    if (THIS_KEY->owner_obj)    add_ref(THIS_KEY->owner_obj);    THIS_KEY->initialized=1;   }      void exit_mutex_key_obj(struct object *DEBUGUSED(o))   {    THREADS_FPRINTF(1, "UNLOCK k:%p m:(%p) t:%p o:%p\n",
pike.git/src/threads.c:2484:    THIS_KEY->owner = NULL;    }    if (THIS_KEY->owner_obj) {    free_object(THIS_KEY->owner_obj);    THIS_KEY->owner_obj=0;    }    THIS_KEY->mut=0;    THIS_KEY->initialized=0;    mutex_obj = THIS_KEY->mutex_obj;    THIS_KEY->mutex_obj = NULL; -  if (mut->num_waiting) +  +  if (mut->num_waiting) { +  THREADS_ALLOW();    co_signal(&mut->condition); -  else if (mutex_obj && !mutex_obj->prog) { +  +  /* Try to wake up the waiting thread(s) immediately +  * in an attempt to avoid starvation. +  */ + #ifdef HAVE_NO_YIELD +  sleep(0); + #else /* HAVE_NO_YIELD */ +  th_yield(); + #endif /* HAVE_NO_YIELD */ +  THREADS_DISALLOW(); +  } else if (mutex_obj && !mutex_obj->prog) {    co_destroy (&mut->condition);    } -  +     if (mutex_obj)    free_object(mutex_obj);    }   }      /*! @endclass    */      /*! @class Condition    *!
pike.git/src/threads.c:2516:    *! support. The Condition class is not simulated otherwise, since that    *! can't be done accurately without continuations.    *!    *! @seealso    *! @[Mutex]    */      struct pike_cond {    COND_T cond;    int wait_count; +  struct object *mutex_obj;   };      #define THIS_COND ((struct pike_cond *)(CURRENT_STORAGE))    -  + /*! @decl void create(Thread.Mutex|void mutex) +  *! +  *! @param mutex +  *! Optional @[Mutex] that protects the resource that the +  *! condition signals for. +  */ + static void f_cond_create(INT32 args) + { +  struct pike_cond *cond = THIS_COND; +  struct object *mux = NULL; +  get_all_args("create", args, ".%O", &mux); +  if (cond->mutex_obj) { +  free_object(cond->mutex_obj); +  cond->mutex_obj = NULL; +  } +  if (mux) { +  struct program *prog = mux->prog; +  if (prog) { +  prog = prog->inherits[SUBTYPEOF(Pike_sp[-args])].prog; +  if (prog == mutex_program) { +  cond->mutex_obj = mux; +  add_ref(mux); +  } +  } +  } + } +    /*! @decl void wait(Thread.MutexKey mutex_key)    *! @decl void wait(Thread.MutexKey mutex_key, int(0..)|float seconds)    *! @decl void wait(Thread.MutexKey mutex_key, int(0..) seconds, @    *! int(0..999999999) nanos)    *!    *! Wait for condition.    *!    *! This function makes the current thread sleep until the condition    *! variable is signalled or the timeout is reached.    *!
pike.git/src/threads.c:2577:    struct object *key, *mutex_obj;    struct mutex_storage *mut;    struct pike_cond *c;    INT_TYPE seconds = 0, nanos = 0;       if(threads_disabled)    Pike_error("Cannot wait for conditions when threads are disabled!\n");       if (args <= 2) {    FLOAT_TYPE fsecs = 0.0; -  get_all_args("wait", args, "%o.%F", &key, &fsecs); +  get_all_args(NULL, args, "%o.%F", &key, &fsecs);    seconds = (INT_TYPE) fsecs;    nanos = (INT_TYPE)((fsecs - seconds)*1000000000);    } else {    /* FIXME: Support bignum nanos. */ -  get_all_args("wait", args, "%o%i%i", &key, &seconds, &nanos); +  get_all_args(NULL, args, "%o%i%i", &key, &seconds, &nanos);    }    -  +  c = THIS_COND; +     if ((key->prog != mutex_key) ||    (!(OB2KEY(key)->initialized)) || -  (!(mut = OB2KEY(key)->mut))) { +  (!(mut = OB2KEY(key)->mut)) || +  (c->mutex_obj && OB2KEY(key)->mutex_obj && +  (OB2KEY(key)->mutex_obj != c->mutex_obj))) {    Pike_error("Bad argument 1 to wait()\n");    }       if(args > 1) {    pop_n_elems(args - 1);    args = 1;    }    -  c = THIS_COND; -  +     /* Unlock mutex */    mutex_obj = OB2KEY(key)->mutex_obj;    mut->key=0;    OB2KEY(key)->mut=0;    OB2KEY(key)->mutex_obj = NULL;    co_signal(& mut->condition);       /* Wait and allow mutex operations */    SWAP_OUT_CURRENT_THREAD();    c->wait_count++;
pike.git/src/threads.c:2694:       THREADS_ALLOW();   #ifdef HAVE_NO_YIELD    sleep(0);   #else /* HAVE_NO_YIELD */    th_yield();   #endif /* HAVE_NO_YIELD */    THREADS_DISALLOW();    }    co_destroy(&(THIS_COND->cond)); +  +  if (THIS_COND->mutex_obj) { +  free_object(THIS_COND->mutex_obj); +  THIS_COND->mutex_obj = NULL;    } -  + }      /*! @endclass    */      /*! @class Thread    */      /*! @decl array(mixed) backtrace()    *!    *! Returns the current call stack for the thread.
pike.git/src/threads.c:2865:    pike_throw();    }    }   }      /*! @decl void interrupt()    *! @decl void interrupt(string msg)    *!    *! Interrupt the thread with the message @[msg].    *! +  *! This function causes the thread to throw an error +  *! where the message defaults to @expr{"Interrupted.\n"@}. +  *!    *! @fixme    *! The argument @[msg] is currently ignored.    *!    *! @note    *! Interrupts are asynchronous, and are currently not queued. -  +  *! +  *! @note +  *! Due to the asynchronous nature of interrupts, it may take +  *! some time before the thread reacts to the interrupt.    */   static void f_thread_id_interrupt(INT32 args)   {    /* FIXME: The msg argument is not supported yet. */    pop_n_elems(args);       if (!(THIS_THREAD->flags & THREAD_FLAG_SIGNAL_MASK)) {    num_pending_interrupts++;    if (!thread_interrupt_callback) {    thread_interrupt_callback =
pike.git/src/threads.c:2906:    }    th->flags |= THREAD_FLAG_TERM;   }      /*! @decl void kill()    *!    *! Interrupt the thread, and terminate it.    *!    *! @note    *! Interrupts are asynchronous, and are currently not queued. +  *! +  *! @note +  *! Due to the asynchronous nature of interrupts, it may take +  *! some time before the thread reacts to the interrupt.    */   static void f_thread_id_kill(INT32 args)   {    pop_n_elems(args);    low_thread_kill (THIS_THREAD);   }      void init_thread_obj(struct object *UNUSED(o))   {    memset(&THIS_THREAD->state, 0, sizeof(struct Pike_interpreter_struct));
pike.git/src/threads.c:3327:    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); -  ADD_FUNCTION("_sprintf",f_mutex__sprintf,tFunc(tNone,tStr),0); +  ADD_FUNCTION("_sprintf",f_mutex__sprintf,tFunc(tInt,tStr),0); +  ADD_FUNCTION("condition", f_mutex_cond, +  tFunc(tNone, tObjIs_THREAD_CONDITION), 0);    set_init_callback(init_mutex_obj);    set_exit_callback(exit_mutex_obj); -  +  mutex_program = Pike_compiler->new_program; +  add_ref(mutex_program);    end_class("mutex", 0);    -  START_NEW_PROGRAM_ID(THREAD_MUTEX_COMPAT_7_4); -  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_compat_7_4); -  end_class("mutex_compat_7_4", 0); -  +     START_NEW_PROGRAM_ID(THREAD_CONDITION);    ADD_STORAGE(struct pike_cond); -  +  PIKE_MAP_VARIABLE("_mutex", OFFSETOF(pike_cond, mutex_obj), +  tObjIs_THREAD_MUTEX, T_OBJECT, ID_PROTECTED|ID_PRIVATE); +  ADD_FUNCTION("create", f_cond_create, +  tFunc(tOr(tObjIs_THREAD_MUTEX, tVoid), tVoid), +  ID_PROTECTED);    ADD_FUNCTION("wait",f_cond_wait,    tOr(tFunc(tObjIs_THREAD_MUTEX_KEY tOr3(tVoid, tIntPos, tFloat),    tVoid),    tFunc(tObjIs_THREAD_MUTEX_KEY tIntPos tIntPos, 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); -  +  condition_program = Pike_compiler->new_program; +  add_ref(condition_program);    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_var);    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_PROTECTED); +  tFunc(tNone,tVoid), ID_PROTECTED);   #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);    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);    PIKE_MAP_VARIABLE("result", OFFSETOF(thread_state, result),    tMix, T_MIXED, 0);    ADD_FUNCTION("create",f_thread_create,    tFuncV(tMixed,tMixed,tVoid),    ID_PROTECTED);    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("_sprintf",f_thread_id__sprintf,tFunc(tInt,tStr),0);    ADD_FUNCTION("id_number",f_thread_id_id_number,tFunc(tNone,tInt),0);    ADD_FUNCTION("interrupt", f_thread_id_interrupt,    tFunc(tOr(tVoid,tStr), tVoid), 0);    ADD_FUNCTION("kill", f_thread_id_kill, tFunc(tNone, tVoid), 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;
pike.git/src/threads.c:3501:       destruct(backend_thread_obj);    free_object(backend_thread_obj);    backend_thread_obj = NULL;       Pike_interpreter_pointer = original_interpreter;       destruct_objects_to_destruct_cb();    }    +  if(condition_program) +  { +  free_program(condition_program); +  condition_program = NULL; +  } +  +  if(mutex_program) +  { +  free_program(mutex_program); +  mutex_program = NULL; +  } +     if(mutex_key)    {    free_program(mutex_key);    mutex_key=0;    }       if(thread_local_prog)    {    free_program(thread_local_prog);    thread_local_prog=0;