pike.git / src / threads.c

version» Context lines:

pike.git/src/threads.c:1:   /*   || This file is part of Pike. For copyright information see COPYRIGHT.   || Pike is distributed under GPL, LGPL and MPL. See the file COPYING   || for more information. - || $Id$ +    */      #include "global.h"      /* #define PICKY_MUTEX */      #ifdef _REENTRANT      #include "pike_error.h"      /* Define to get a debug trace of thread operations. Debug levels can be 0-2. */   /* #define VERBOSE_THREADS_DEBUG 1 */      /* #define PROFILE_CHECK_THREADS */      #ifndef CONFIGURE_TEST -  + #define IN_THREAD_CODE   #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"
pike.git/src/threads.c:36:   #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 <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   #include <sys/time.h>   #endif - #ifdef HAVE_TIME_H - #include <time.h> +  + /* This is used for strapping the interpreter before the threads +  * are loaded, and when there's no support for threads. +  */ + static struct Pike_interpreter_struct static_pike_interpreter; +  + static cpu_time_t thread_quanta = 0; +  + PMOD_EXPORT struct Pike_interpreter_struct * + #if defined(__GNUC__) && __GNUC__ >= 3 +  __restrict   #endif -  + Pike_interpreter_pointer = &static_pike_interpreter;    -  + PMOD_EXPORT struct Pike_interpreter_struct * pike_get_interpreter_pointer(void) + { +  return Pike_interpreter_pointer; + }   #else /* CONFIGURE_TEST */   #include "pike_threadlib.h"   #endif      #ifndef VERBOSE_THREADS_DEBUG   #define THREADS_FPRINTF(LEVEL,FPRINTF_ARGS)   #else   #define THREADS_FPRINTF(LEVEL,FPRINTF_ARGS) do { \    if ((VERBOSE_THREADS_DEBUG + 0) >= (LEVEL)) { \    /* E.g. THREADS_DISALLOW is used in numerous places where the */ \
pike.git/src/threads.c:143: Inside #if defined(__NT__)
   unsigned *id)   {    HANDLE h = (HANDLE)_beginthreadex(NULL, Pike_stack_size, fun, arg, 0, id);    if(h)    {    CloseHandle(h);    return 0;    }    else    { -  return 1; +  return errno;    }   }      #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);
pike.git/src/threads.c:277: Inside #if defined(SIMULATE_COND_WITH_EVENT)
   if(t) return -1;    mt_destroy(& c->lock);    return 0;   }      #else /* !SIMULATE_COND_WITH_EVENT */      #ifndef CONFIGURE_TEST   PMOD_EXPORT int co_wait_timeout(COND_T *c, PIKE_MUTEX_T *m, long s, long nanos)   { +  struct timeval ct;   #ifdef POSIX_THREADS    struct timespec timeout;   #ifdef HAVE_PTHREAD_COND_RELTIMEDWAIT_NP    /* Solaris extension: relative timeout. */    timeout.tv_sec = s;    timeout.tv_nsec = nanos;    return pthread_cond_reltimedwait_np(c, m, &timeout);   #else /* !HAVE_PTHREAD_COND_RELTIMEDWAIT_NP */    /* Absolute timeout. */ -  GETTIMEOFDAY(&current_time); -  timeout.tv_sec = current_time.tv_sec + s; -  timeout.tv_nsec = current_time.tv_usec * 1000 + nanos; +  ACCURATE_GETTIMEOFDAY(&ct); +  timeout.tv_sec = ct.tv_sec + s; +  timeout.tv_nsec = ct.tv_usec * 1000 + nanos;    return pthread_cond_timedwait(c, m, &timeout);   #endif /* HAVE_PTHREAD_COND_RELTIMEDWAIT_NP */   #else /* !POSIX_THREADS */   #error co_wait_timeout does not support this thread model.   #endif /* POSIX_THREADS */   }   #endif /* !CONFIGURE_TEST */      #endif /* SIMULATE_COND_WITH_EVENT */   
pike.git/src/threads.c:338: Inside #if defined(POSIX_THREADS)
   pthread_attr_setstacksize(&small_pattr, 4096*sizeof(char *));   #endif    pthread_attr_setdetachstate(&small_pattr, PTHREAD_CREATE_DETACHED);   #endif   }      static void cleanup_thread_state (struct thread_state *th);      #ifndef CONFIGURE_TEST    - #if defined(HAVE_CLOCK) && \ -  (defined (RDTSC) || \ + #if defined (RDTSC) || \    (!defined(HAVE_GETHRTIME) && \ -  !(defined(HAVE_MACH_TASK_INFO_H) && defined(TASK_THREAD_TIMES_INFO)))) +  !(defined(HAVE_MACH_TASK_INFO_H) && defined(TASK_THREAD_TIMES_INFO)))   static clock_t thread_start_clock = 0;   static THREAD_T last_clocked_thread = 0;   #define USE_CLOCK_FOR_SLICES   #ifdef RDTSC   static int use_tsc_for_slices;   #define TSC_START_INTERVAL 100000   static INT64 prev_tsc; /* TSC and */   static clock_t prev_clock; /* clock() at the beg of the last tsc interval */   #endif   #endif      #define THIS_THREAD ((struct thread_state *)CURRENT_STORAGE)      static struct callback *threads_evaluator_callback=0;      PMOD_EXPORT int num_threads = 1;   PMOD_EXPORT int threads_disabled = 0; -  + PMOD_EXPORT cpu_time_t threads_disabled_acc_time = 0; + PMOD_EXPORT cpu_time_t threads_disabled_start = 0;      #ifdef PIKE_DEBUG   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 MUTEX_T interpreter_lock;   static MUTEX_T interpreter_lock_wanted;   MUTEX_T thread_table_lock;   static MUTEX_T interleave_lock;   static struct program *mutex_key = 0;   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 + struct thread_local_var   {    INT32 id;   };      static volatile IMUTEX_T *interleave_list = NULL;      #define THREADSTATE2OBJ(X) ((X)->thread_obj)      #if defined(PIKE_DEBUG)   
pike.git/src/threads.c:413:    self = th_self();    if (!th_equal (debug_locking_thread, self))    pike_fatal_dloc ("Interpreter not locked by this thread.\n");    }   }      static unsigned LONGEST thread_swaps = 0;   static unsigned LONGEST check_threads_calls = 0;   static unsigned LONGEST check_threads_yields = 0;   static unsigned LONGEST check_threads_swaps = 0; - static void f__thread_swaps (INT32 args) + static void f__thread_swaps (INT32 UNUSED(args))    {push_ulongest (thread_swaps);} - static void f__check_threads_calls (INT32 args) + static void f__check_threads_calls (INT32 UNUSED(args))    {push_ulongest (check_threads_calls);} - static void f__check_threads_yields (INT32 args) + static void f__check_threads_yields (INT32 UNUSED(args))    {push_ulongest (check_threads_yields);} - static void f__check_threads_swaps (INT32 args) + static void f__check_threads_swaps (INT32 UNUSED(args))    {push_ulongest (check_threads_swaps);}      #else      #define SET_LOCKING_THREAD 0   #define UNSET_LOCKING_THREAD 0 - static INLINE void check_interpreter_lock (DLOC_DECL) {} +       #endif      PMOD_EXPORT INLINE void pike_low_lock_interpreter (DLOC_DECL)   {    /* The double locking here is to ensure that when a thread releases    * the interpreter lock, a different thread gets it first. Thereby    * we ensure a thread switch in check_threads, if there are other    * threads waiting. */    mt_lock (&interpreter_lock_wanted);    mt_lock (&interpreter_lock);    mt_unlock (&interpreter_lock_wanted);       SET_LOCKING_THREAD; -  +  USE_DLOC_ARGS();    THREADS_FPRINTF (1, (stderr, "Got iplock" DLOC_PF(" @ ",) "\n"    COMMA_DLOC_ARGS_OPT));   }      PMOD_EXPORT INLINE void pike_low_wait_interpreter (COND_T *cond COMMA_DLOC_DECL)   { -  +  USE_DLOC_ARGS();    THREADS_FPRINTF (1, (stderr,    "Waiting on cond %p without iplock" DLOC_PF(" @ ",) "\n",    cond COMMA_DLOC_ARGS_OPT));    UNSET_LOCKING_THREAD;       /* FIXME: Should use interpreter_lock_wanted here as well. The    * problem is that few (if any) thread libs lets us atomically    * unlock a mutex and wait, and then lock a different mutex. */    co_wait (cond, &interpreter_lock);   
pike.git/src/threads.c:468:    THREADS_FPRINTF (1, (stderr,    "Got signal on cond %p with iplock" DLOC_PF(" @ ",) "\n",    cond COMMA_DLOC_ARGS_OPT));   }      PMOD_EXPORT INLINE int pike_low_timedwait_interpreter (COND_T *cond,    long sec, long nsec    COMMA_DLOC_DECL)   {    int res; +  USE_DLOC_ARGS();    THREADS_FPRINTF (1, (stderr,    "Waiting on cond %p without iplock" DLOC_PF(" @ ",) "\n",    cond COMMA_DLOC_ARGS_OPT));    UNSET_LOCKING_THREAD;       /* FIXME: Should use interpreter_lock_wanted here as well. The    * problem is that few (if any) thread libs lets us atomically    * unlock a mutex and wait, and then lock a different mutex. */    res = co_wait_timeout (cond, &interpreter_lock, sec, nsec);       SET_LOCKING_THREAD;    THREADS_FPRINTF (1, (stderr,    "Got signal on cond %p with iplock" DLOC_PF(" @ ",) "\n",    cond COMMA_DLOC_ARGS_OPT));    return res;   }      static void threads_disabled_wait (DLOC_DECL)   {    assert (threads_disabled); -  +  USE_DLOC_ARGS();    do {    THREADS_FPRINTF (1, (stderr,    "Waiting on threads_disabled" DLOC_PF(" @ ",) "\n"    COMMA_DLOC_ARGS_OPT));    UNSET_LOCKING_THREAD;    co_wait (&threads_disabled_change, &interpreter_lock);    SET_LOCKING_THREAD;    } while (threads_disabled);    THREADS_FPRINTF (1, (stderr,    "Continue after threads_disabled" DLOC_PF(" @ ",) "\n"
pike.git/src/threads.c:509:   }      PMOD_EXPORT INLINE void pike_lock_interpreter (DLOC_DECL)   {    pike_low_lock_interpreter (DLOC_ARGS_OPT);    if (threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT);   }      PMOD_EXPORT INLINE void pike_unlock_interpreter (DLOC_DECL)   { +  USE_DLOC_ARGS();    THREADS_FPRINTF (1, (stderr, "Releasing iplock" DLOC_PF(" @ ",) "\n"    COMMA_DLOC_ARGS_OPT));    UNSET_LOCKING_THREAD;    mt_unlock (&interpreter_lock);   }      PMOD_EXPORT INLINE void pike_wait_interpreter (COND_T *cond COMMA_DLOC_DECL)   { -  +  int owner = threads_disabled;    pike_low_wait_interpreter (cond COMMA_DLOC_ARGS_OPT); -  if (threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT); +  if (!owner && threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT);   }      PMOD_EXPORT INLINE 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 (threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT); +  if (!owner && threads_disabled) 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;    ts->state = Pike_interpreter;    Pike_interpreter_pointer = &ts->state;    ts->id = th_self();    ts->status = THREAD_RUNNING;    ts->swapped = 0; -  +  ts->interval_start = get_real_time();   #ifdef PIKE_DEBUG    ts->debug_flags = 0;    thread_swaps++;   #endif   #ifdef USE_CLOCK_FOR_SLICES    /* Initialize thread_start_clock to zero instead of clock() since we    * don't know for how long the thread already has run. */    thread_start_clock = 0;    last_clocked_thread = ts->id;   #ifdef RDTSC
pike.git/src/threads.c:565: Inside #if defined(USE_CLOCK_FOR_SLICES) and #if defined(PROFILE_CHECK_THREADS)
  #ifdef PROFILE_CHECK_THREADS    fprintf (stderr, "[%d:%f] pike_init_thread_state: tsc reset\n",    getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS));   #endif   #endif   }      PMOD_EXPORT void pike_swap_out_thread (struct thread_state *ts    COMMA_DLOC_DECL)   { +  USE_DLOC_ARGS();    THREADS_FPRINTF (2, (stderr, "Swap out %sthread %p" DLOC_PF(" @ ",) "\n",    ts == Pike_interpreter.thread_state ? "current " : "",    ts    COMMA_DLOC_ARGS_OPT));      #ifdef PROFILING    if (!ts->swapped) {    cpu_time_t now = get_cpu_time();   #ifdef PROFILING_DEBUG    fprintf(stderr, "%p: Swap out at: %" PRINT_CPU_TIME
pike.git/src/threads.c:610:   #endif      #ifdef PROFILING    if (ts->swapped) {    cpu_time_t now = get_cpu_time();   #ifdef PROFILING_DEBUG    fprintf(stderr, "%p: Swap in at: %" PRINT_CPU_TIME    " unlocked: %" PRINT_CPU_TIME "\n",    ts, now, ts->state.unlocked_time);   #endif - #ifdef PIKE_DEBUG -  if (now < -Pike_interpreter.unlocked_time) { -  pike_fatal_dloc("Time at swap in is before time at swap out." -  " %" PRINT_CPU_TIME " < %" PRINT_CPU_TIME -  "\n", now, -Pike_interpreter.unlocked_time); -  } - #endif + /* This will not work, since Pike_interpreter_pointer is always null here... */ + /* #ifdef PIKE_DEBUG */ + /* if (now < -Pike_interpreter.unlocked_time) { */ + /* pike_fatal_dloc("Time at swap in is before time at swap out." */ + /* " %" PRINT_CPU_TIME " < %" PRINT_CPU_TIME */ + /* "\n", now, -Pike_interpreter.unlocked_time); */ + /* } */ + /* #endif */    ts->state.unlocked_time += now;    }   #endif       ts->swapped=0;    Pike_interpreter_pointer = &ts->state;   #ifdef PIKE_DEBUG    thread_swaps++;   #endif   
pike.git/src/threads.c:680: Inside #if defined(PIKE_DEBUG)
   "Nested use of ALLOW_THREADS()?\n");    if(ts != Pike_interpreter.thread_state) {    debug_list_all_threads();    Pike_fatal ("Thread states mixed up between threads.\n");    }    check_interpreter_lock (DLOC_ARGS_OPT);   }      #endif /* PIKE_DEBUG */    + /*! @class MasterObject +  */ +  + /*! @decl void thread_quanta_exceeded(Thread.Thread thread, int ns) +  *! +  *! Function called when a thread has exceeded the thread quanta. +  *! +  *! @param thread +  *! Thread that exceeded the thread quanta. +  *! +  *! @param ns +  *! Number of nanoseconds that the thread executed before allowing +  *! other threads to run. +  *! +  *! The default master prints a diagnostic and the thread backtrace +  *! to @[Stdio.stderr]. +  *! +  *! @note +  *! This function runs in a signal handler context, and should thus +  *! avoid handling of mutexes, etc. +  *! +  *! @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. */    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 +  push_int(1000000000 / CPU_TIME_TICKS); +  o_multiply(); + #endif +  SAFE_APPLY_MASTER("thread_quanta_exceeded", 2); +  pop_stack(); +  } +  } +    #ifdef PIKE_DEBUG    pike_debug_check_thread (DLOC_ARGS_OPT);    if (Pike_in_gc > 50 && Pike_in_gc < 300)    pike_fatal_dloc ("Threads allowed during garbage collection (pass %d).\n",    Pike_in_gc);    if (pike_global_buffer.s.str)    pike_fatal_dloc ("Threads allowed while the global dynamic buffer "    "is in use.\n");    ts->debug_flags |= THREAD_DEBUG_LOOSE;   #endif
pike.git/src/threads.c:727: Inside #if defined(DO_PIKE_CLEANUP)
  {   #ifdef DO_PIKE_CLEANUP    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(); +  } +    #ifdef PIKE_DEBUG    ts->debug_flags &= ~THREAD_DEBUG_LOOSE;    pike_debug_check_thread (DLOC_ARGS_OPT);   #endif   }      PMOD_EXPORT void pike_threads_allow_ext (struct thread_state *ts    COMMA_DLOC_DECL)   {   #ifdef DO_PIKE_CLEANUP    /* Might 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 +  push_int(1000000000 / CPU_TIME_TICKS); +  o_multiply(); + #endif +  SAFE_APPLY_MASTER("thread_quanta_exceeded", 2); +  pop_stack(); +  } +  } +    #ifdef PIKE_DEBUG    pike_debug_check_thread (DLOC_ARGS_OPT);    if (Pike_in_gc > 50 && Pike_in_gc < 300)    pike_fatal_dloc ("Threads allowed during garbage collection (pass %d).\n",    Pike_in_gc);    if (pike_global_buffer.s.str)    pike_fatal_dloc ("Threads allowed while the global dynamic buffer "    "is in use.\n");    ts->debug_flags |= THREAD_DEBUG_LOOSE;   #endif
pike.git/src/threads.c:790:    if (ts->swapped) {    pike_low_lock_interpreter (DLOC_ARGS_OPT);    live_threads--;    THREADS_FPRINTF (1, (stderr, "Decreased live threads to %d\n",    live_threads));    co_broadcast (&live_threads_change);    if (threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT);    pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT);    }    +  if (UNLIKELY(thread_quanta)) { +  ts->interval_start = get_real_time(); +  } +    #ifdef PIKE_DEBUG    ts->debug_flags &= ~THREAD_DEBUG_LOOSE;    pike_debug_check_thread (DLOC_ARGS_OPT);   #endif   }      PMOD_EXPORT void pike_lock_imutex (IMUTEX_T *im COMMA_DLOC_DECL)   {    struct thread_state *ts = Pike_interpreter.thread_state;   
pike.git/src/threads.c:815:    mt_lock(&((im)->lock));    pike_threads_disallow (ts COMMA_DLOC_ARGS_OPT);    THREADS_FPRINTF(1, (stderr, "Locked IMutex %p\n", im));   }      PMOD_EXPORT void pike_unlock_imutex (IMUTEX_T *im COMMA_DLOC_DECL)   {    /* If threads are disabled, we already hold the lock. */    if (threads_disabled) return;    +  USE_DLOC_ARGS();    THREADS_FPRINTF(0, (stderr, "Unlocking IMutex %p" DLOC_PF(" @ ",) "\n",    im COMMA_DLOC_ARGS_OPT));    mt_unlock(&(im->lock));   }      /* 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)   {
pike.git/src/threads.c:871:    mt_lock(&(im->lock));       im = im->next;    }    }       THREADS_FPRINTF(0, (stderr,    "low_init_threads_disable(): Disabling threads.\n"));       threads_disabled = 1; +  threads_disabled_start = get_real_time();   #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));
pike.git/src/threads.c:902:    *! all threads.    *!    *! @note    *! You should make sure that the returned object is freed even if    *! some kind of error is thrown. That means in practice that it    *! should only have references (direct or indirect) from function    *! local variables. Also, it shouldn't be referenced from cyclic    *! memory structures, since those are only destructed by the periodic    *! gc. (This advice applies to mutex locks in general, for that    *! matter.) +  *! +  *! @seealso +  *! @[gethrdtime()]    */ - void init_threads_disable(struct object *o) + void init_threads_disable(struct object *UNUSED(o))   {    low_init_threads_disable();       if(live_threads) {    SWAP_OUT_CURRENT_THREAD();    while (live_threads) {    THREADS_FPRINTF(1,    (stderr,    "_disable_threads(): Waiting for %d threads to finish\n",    live_threads));    low_co_wait_interpreter (&live_threads_change);    }    THREADS_FPRINTF(0, (stderr, "_disable_threads(): threads now disabled\n"));    SWAP_IN_CURRENT_THREAD();    }   }    - void exit_threads_disable(struct object *o) + void exit_threads_disable(struct object *UNUSED(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; -  +  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, (stderr,    "exit_threads_disable(): Unlocking IM %p\n", im));    mt_unlock(&(im->lock));    im = im->next;    }       mt_unlock(&interleave_lock);
pike.git/src/threads.c:1197:    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    }   }      PMOD_EXPORT void enable_external_threads(void)   {    num_threads++;   }      PMOD_EXPORT void disable_external_threads(void)   {
pike.git/src/threads.c:1236:    struct thread_state *s;       pop_n_elems(args);    oldsp = Pike_sp;    FOR_EACH_THREAD (s, {    struct object *o = THREADSTATE2OBJ(s);    if (o) {    ref_push_object(o);    }    }); -  f_aggregate(DO_NOT_WARN(Pike_sp - oldsp)); +  f_aggregate(Pike_sp - oldsp);   }      #ifdef PIKE_DEBUG   PMOD_EXPORT void debug_list_all_threads(void)   {    INT32 x;    struct thread_state *s;    THREAD_T self = th_self();       fprintf(stderr,"--Listing all threads--\n");
pike.git/src/threads.c:1284: Inside #if defined(PIKE_DEBUG)
   }    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) + static void check_threads(struct callback *UNUSED(cb), void *UNUSED(arg), void *UNUSED(arg2))   {   #ifdef PROFILE_CHECK_THREADS    static unsigned long calls = 0, yields = 0;    static unsigned long clock_checks = 0, no_clock_advs = 0;   #if 0    static unsigned long slice_int_n = 0; /* Slice interval length. */    static double slice_int_mean = 0.0, slice_int_m2 = 0.0;    static unsigned long tsc_int_n = 0; /* Actual tsc interval length. */    static double tsc_int_mean = 0.0, tsc_int_m2 = 0.0;    static unsigned long tsc_tgt_n = 0; /* Target tsc interval length. */
pike.git/src/threads.c:1380: Inside #if defined(RDTSC) && defined(USE_CLOCK_FOR_SLICES)
   * unsynchronized tsc's between cores, OS tsc resets, etc -    * individual intervals can be off by more than an order of    * magnitude in either direction without affecting the final time    * slice length appreciably.    *    * Note that the real interval lengths will always be longer.    * One reason is that we won't get calls exactly when they run    * out. Another is the often lousy clock(3) resolution. */       if (prev_tsc) { -  clock_t tsc_interval_time = clock_now - prev_clock; -  if (tsc_interval_time > 0) { +  if (clock_now > prev_clock) {    /* Estimate the next interval just by extrapolating the    * tsc/clock ratio of the last one. This adapts very    * quickly but is also very "jumpy". That shouldn't matter    * due to the approach with dividing the time slice into    * ~20 tsc intervals.    *    * Note: The main source of the jumpiness is probably that    * clock(3) has so lousy resolution on many platforms, i.e.    * it may step forward very large intervals very seldom    * (100 times/sec on linux/glibc 2.x). It also has the    * effect that the actual tsc intervals will be closer to    * 1/200 sec. */ -  +  clock_t tsc_interval_time = clock_now - prev_clock;    INT64 new_target_int =    (tsc_elapsed * (CLOCKS_PER_SEC / 400)) / tsc_interval_time;    if (new_target_int < target_int << 2)    target_int = new_target_int;    else {    /* The most likely cause for this is high variance in the    * interval lengths due to low clock(3) resolution. */   #ifdef PROFILE_CHECK_THREADS    fprintf (stderr, "[%d:%f] Capping large TSC interval increase "    "(from %"PRINTINT64"d to %"PRINTINT64"d)\n",
pike.git/src/threads.c:1429: Inside #if defined(RDTSC) && defined(USE_CLOCK_FOR_SLICES) and #if 0
   }   #endif   #endif    }    else {    /* clock(3) can have pretty low resolution and might not    * have advanced during the tsc interval. Just do another    * round on the old estimate, keeping prev_tsc and    * prev_clock fixed to get a longer interval for the next    * measurement. */ -  if (tsc_interval_time < 0) { +  if (clock_now < prev_clock) {    /* clock() wraps around fairly often as well. We still    * keep the old interval but update the baselines in this    * case. */    prev_tsc = tsc_now;    prev_clock = clock_now;    }    target_int += tsc_elapsed;   #ifdef PROFILE_CHECK_THREADS    no_clock_advs++;   #endif
pike.git/src/threads.c:1453: Inside #if defined(RDTSC) && defined(USE_CLOCK_FOR_SLICES) and #if defined(PROFILE_CHECK_THREADS)
  #ifdef PROFILE_CHECK_THREADS    fprintf (stderr, "[%d:%f] Warning: Encountered zero prev_tsc "    "(thread_start_clock: %"PRINTINT64"d, "    "clock_now: %"PRINTINT64"d)\n",    getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS),    (INT64) thread_start_clock, (INT64) clock_now);   #endif    prev_tsc = tsc_now;    }    -  if (clock_now - thread_start_clock < 0) +  if (clock_now < thread_start_clock)    /* clock counter has wrapped since the start of the time    * slice. Let's reset and yield. */    thread_start_clock = 0;    else if (clock_now - thread_start_clock <    (clock_t) (CLOCKS_PER_SEC / 20))    return;    }      #ifdef PROFILE_CHECK_THREADS    {
pike.git/src/threads.c:1494:   #elif defined(HAVE_MACH_TASK_INFO_H) && defined(TASK_THREAD_TIMES_INFO)    {    static struct timeval last_check = { 0, 0 };    task_thread_times_info_data_t info;    mach_msg_type_number_t info_size = TASK_THREAD_TIMES_INFO_COUNT;       /* Before making an expensive call to task_info() we perform a    preliminary check that at least 35 ms real time has passed. If    not yet true we'll postpone the next check a full interval. */    struct timeval tv; -  if (GETTIMEOFDAY(&tv) == 0) { +  ACCURATE_GETTIMEOFDAY(&tv); +  {   #ifdef INT64    static INT64 real_time_last_check = 0;    INT64 real_time_now = tv.tv_sec * 1000000 + tv.tv_usec;    if (real_time_now - real_time_last_check < 35000)    return;    real_time_last_check = real_time_now;   #else    static struct timeval real_time_last_check = { 0, 0 };    struct timeval diff;    timersub(&real_time_now, &real_time_last_check, &diff);
pike.git/src/threads.c:1544:    timersub(&now, &last_check, &diff);    if (diff.tv_usec < 50000 && diff.tv_sec == 0)    return;    last_check = now;   #endif    }    }   #elif defined (USE_CLOCK_FOR_SLICES)    {    clock_t clock_now = clock(); -  if (clock_now - thread_start_clock < 0) +  if (clock_now < thread_start_clock)    /* clock counter has wrapped since the start of the time slice.    * Let's reset and yield. */    thread_start_clock = 0;    else if (clock_now - thread_start_clock < (clock_t) (CLOCKS_PER_SEC / 20))    return;    }   #else    static int div_;    if(div_++ & 255)    return;
pike.git/src/threads.c:1582: Inside #if defined(PROFILE_CHECK_THREADS), #if 0 and #if defined(USE_CLOCK_FOR_SLICES)
   double slice_time =    (double) (clock() - thread_start_clock) / CLOCKS_PER_SEC;    double delta = slice_time - slice_int_mean;    slice_int_n++;    slice_int_mean += delta / slice_int_n;    slice_int_m2 += delta * (slice_time - slice_int_mean);    }   #endif   #endif    -  GETTIMEOFDAY (&now); +  ACCURATE_GETTIMEOFDAY (&now);    if (now.tv_sec > last_time) {    fprintf (stderr, "[%d:%f] check_threads: %lu calls, "    "%lu clocks, %lu no advs, %lu yields"   #if 0    ", slice %.3f:%.1e, tsc int %.2e:%.1e, tsc tgt %.2e:%.1e"   #endif    ", tps %g:%.1e\n",    getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS),    calls, clock_checks, no_clock_advs, yields,   #if 0
pike.git/src/threads.c:1688: Inside #if defined(HAVE_BROKEN_LINUX_THREAD_EUID) and #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
  #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)    /* The sete?id calls will clear the dumpable state that we might    * have set with system.dumpable. */    int current = prctl(PR_GET_DUMPABLE);   #ifdef PIKE_DEBUG    if (current == -1)    fprintf (stderr, "%s:%d: Unexpected error from prctl(2). errno=%d\n",    __FILE__, __LINE__, errno);   #endif   #endif -  setegid(arg.egid); -  seteuid(arg.euid); + #ifdef HAVE_BROKEN_LINUX_THREAD_EUID +  if( setegid(arg.egid) != 0 || seteuid(arg.euid) != 0 ) +  { +  fprintf (stderr, "%s:%d: Unexpected error from setegid(2). errno=%d\n", +  __FILE__, __LINE__, errno); +  } + #endif   #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)    if (current != -1 && prctl(PR_SET_DUMPABLE, current) == -1) {   #if defined(PIKE_DEBUG)    fprintf (stderr, "%s:%d: Unexpected error from prctl(2). errno=%d\n",    __FILE__, __LINE__, errno);   #endif    }   #endif    }   #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */
pike.git/src/threads.c:1740:    /* After signalling the status change to the spawning thread we may    * now wait if threads are disabled. */    if (threads_disabled) {    SWAP_OUT_CURRENT_THREAD();    threads_disabled_wait (DLOC);    SWAP_IN_CURRENT_THREAD();    }       DEBUG_CHECK_THREAD();    -  Pike_interpreter.trace_level = default_t_flag; -  +     THREADS_FPRINTF(0, (stderr,"new_thread_func(): Thread %p inited\n",    arg.thread_state));       if(SETJMP(back))    { -  if(throw_severity <= THROW_ERROR) +  if(throw_severity <= THROW_ERROR) { +  if (thread_state->thread_obj) { +  /* Copy the thrown exit value to the thread_state here, +  * if the thread hasn't been destructed. */ +  assign_svalue(&thread_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);    } -  +  +  thread_state->status = THREAD_ABORTED;    } else {    INT32 args=arg.args->size;    back.severity=THROW_EXIT;    push_array_items(arg.args);    arg.args=0;    f_call_function(args);       /* Copy return value to the thread_state here, if the thread    * object hasn't been destructed. */    if (thread_state->thread_obj)    assign_svalue(&thread_state->result, Pike_sp-1);    pop_stack(); -  +  +  thread_state->status = THREAD_EXITED;    }       UNSETJMP(back);       DEBUG_CHECK_THREAD();    -  if(thread_state->thread_local != NULL) { -  free_mapping(thread_state->thread_local); -  thread_state->thread_local = NULL; +  if(thread_state->thread_locals != NULL) { +  free_mapping(thread_state->thread_locals); +  thread_state->thread_locals = NULL;    }    -  thread_state->status = THREAD_EXITED; +     co_broadcast(&thread_state->status_change);       THREADS_FPRINTF(0, (stderr,"new_thread_func(): Thread %p done\n",    arg.thread_state));       /* This thread is now officially dead. */       while(Pike_fp)    POP_PIKE_FRAME();   
pike.git/src/threads.c:1826:    }      #ifdef INTERNAL_PROFILING    fprintf (stderr, "Thread usage summary:\n");    debug_print_rusage (stderr);   #endif       /* FIXME: What about threads_disable? */    mt_unlock_interpreter();    th_exit(0); -  /* NOT_REACHED, but removes a warning */ -  return 0; +  UNREACHABLE(return 0);   }      #ifdef UNIX_THREADS   int num_lwps = 1;   #endif      /*! @class Thread    */    - /*! @decl void create(function(mixed...:void) f, mixed ... args) + /*! @decl void create(function(mixed...: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.    *!
pike.git/src/threads.c:1866:    *! @seealso    *! @[Mutex], @[Condition], @[this_thread()]    */   void f_thread_create(INT32 args)   {    struct thread_starter arg;    struct thread_state *thread_state =    (struct thread_state *)Pike_fp->current_storage;    int tmp;    +  if (args < 1) { +  SIMPLE_TOO_FEW_ARGS_ERROR("create", 1); +  } +  if (!callablep(Pike_sp - args)) { +  SIMPLE_BAD_ARG_ERROR("create", 1, "function"); +  } +     if (thread_state->status != THREAD_NOT_STARTED) {    Pike_error("Threads can not be restarted (status:%d).\n",    thread_state->status);    }       arg.args = aggregate_array(args);    arg.thread_state = thread_state;       if (low_init_interpreter(&thread_state->state)) {    free_array(arg.args);
pike.git/src/threads.c:1926:    low_co_wait_interpreter (&thread_state->status_change);    THREADS_FPRINTF(0, (stderr, "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, (stderr, "f_thread_create %p done\n", thread_state)); -  push_int(0); +    }      /*! @endclass    */      #ifdef UNIX_THREADS   /*! @decl void thread_set_concurrency(int concurrency)    *!    *! @fixme    *! Document this function
pike.git/src/threads.c:1961:   /*! @decl Thread.Thread this_thread()    *!    *! This function returns the object that identifies this thread.    *!    *! @seealso    *! @[Thread()]    */   PMOD_EXPORT void f_this_thread(INT32 args)   {    pop_n_elems(args); -  if (Pike_interpreter.thread_state) { +  if (Pike_interpreter.thread_state && +  Pike_interpreter.thread_state->thread_obj) {    ref_push_object(Pike_interpreter.thread_state->thread_obj);    } else {    /* Threads not enabled yet/anylonger */    push_undefined();    }   }    -  + /*! @decl int(0..) get_thread_quanta() +  *! +  *! @returns +  *! Returns the current thread quanta in nanoseconds. +  *! +  *! @seealso +  *! @[set_thread_quanta()], @[gethrtime()] +  */ + static void f_get_thread_quanta(INT32 args) + { +  pop_n_elems(args); +  push_int64(thread_quanta); + #ifndef LONG_CPU_TIME_T +  /* Convert from ticks. */ +  push_int(1000000000 / CPU_TIME_TICKS); +  o_multiply(); + #endif + } +  + /*! @decl int(0..) set_thread_quanta(int(0..) ns) +  *! +  *! Set the thread quanta. +  *! +  *! @param ns +  *! New thread quanta in nanoseconds. +  *! A value of zero (default) disables the thread quanta checks. +  *! +  *! When enabled @[MasterObject.thread_quanta_exceeded()] will +  *! be called when a thread has spent more time than the quanta +  *! without allowing another thread to run. +  *! +  *! @note +  *! Setting a non-zero value that is too small to allow for +  *! @[MasterObject.thread_quanta_exceeded()] to run is NOT a +  *! good idea. +  *! +  *! @returns +  *! Returns the previous thread quanta in nanoseconds. +  *! +  *! @seealso +  *! @[set_thread_quanta()], @[gethrtime()] +  */ + static void f_set_thread_quanta(INT32 args) + { +  LONGEST ns = 0; +  + #ifndef LONG_CPU_TIME_T +  /* Convert to ticks. */ +  push_int(1000000000 / CPU_TIME_TICKS); +  o_divide(); + #endif +  get_all_args("set_thread_quanta", args, "%l", &ns); +  pop_n_elems(args); +  +  push_int64(thread_quanta); + #ifndef LONG_CPU_TIME_T +  /* Convert from ticks. */ +  push_int(1000000000 / CPU_TIME_TICKS); +  o_multiply(); + #endif +  +  if (ns <= 0) ns = 0; +  +  thread_quanta = ns; +  +  if (Pike_interpreter.thread_state) { +  Pike_interpreter.thread_state->interval_start = get_real_time(); +  } + } +    #define THIS_MUTEX ((struct mutex_storage *)(CURRENT_STORAGE))         /* Note:    * No reference is kept to the key object, it is destructed if the    * mutex is destructed. The key pointer is set to zero by the    * key object when the key is destructed.    */      struct mutex_storage
pike.git/src/threads.c:2066:    struct mutex_storage *m;    struct object *o;    INT_TYPE type;       DEBUG_CHECK_THREAD();       m=THIS_MUTEX;    if(!args)    type=0;    else -  get_all_args("mutex->lock",args,"%i",&type); +  get_all_args("lock",args,"%i",&type);       switch(type)    {    default: -  bad_arg_error("mutex->lock", Pike_sp-args, args, 2, "int(0..2)", Pike_sp+1-args, +  bad_arg_error("lock", Pike_sp-args, 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,    (stderr, "Recursive LOCK k:%p, m:%p(%p), t:%p\n",    OB2KEY(m->key), m, OB2KEY(m->key)->mut,
pike.git/src/threads.c:2168:       /* 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("mutex->trylock",args,"%i",&type); +  get_all_args("trylock",args,"%i",&type);       switch(type)    {    default: -  bad_arg_error("mutex->trylock", Pike_sp-args, args, 2, "int(0..2)", Pike_sp+1-args, +  bad_arg_error("trylock", Pike_sp-args, 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:2248:    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) + void init_mutex_obj(struct object *UNUSED(o))   {    co_init(& THIS_MUTEX->condition);    THIS_MUTEX->key=0;    THIS_MUTEX->num_waiting = 0;   }    - void exit_mutex_obj(struct object *o) + void exit_mutex_obj(struct object *UNUSED(o))   {    struct mutex_storage *m = THIS_MUTEX;    struct object *key = m->key;       THREADS_FPRINTF(1, (stderr, "DESTROYING MUTEX m:%p\n", THIS_MUTEX));      #ifndef PICKY_MUTEX    if (key) {    /* The last key will destroy m->condition in its exit hook. */    THREADS_FPRINTF(1, (stderr, "Destructed mutex is in use - delaying cleanup\n"));
pike.git/src/threads.c:2293:    * lock we need to double it to a broadcast. The last thread    * that stops waiting will destroy m->condition. */    co_broadcast (&m->condition);    }    }    else    co_destroy(& m->condition);   #endif   }    - void exit_mutex_obj_compat_7_4(struct object *o) + 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, (stderr, "DESTROYING MUTEX m:%p\n", THIS_MUTEX));       if(key) {    m->key=0;    destruct(key); /* Will destroy m->condition if m->num_waiting is zero. */    }
pike.git/src/threads.c:2331:    *!    *! As long as they are held, the corresponding mutex will be locked.    *!    *! The corresponding mutex will be unlocked when the object    *! 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 *o) + void init_mutex_key_obj(struct object *UNUSED(o))   {    THREADS_FPRINTF(1, (stderr, "KEY k:%p, t:%p\n",    THIS_KEY, Pike_interpreter.thread_state));    THIS_KEY->mut=0;    THIS_KEY->mutex_obj = NULL;    THIS_KEY->owner = Pike_interpreter.thread_state; -  add_ref(THIS_KEY->owner_obj = Pike_interpreter.thread_state->thread_obj); +  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 *o) + void exit_mutex_key_obj(struct object *DEBUGUSED(o))   {    THREADS_FPRINTF(1, (stderr, "UNLOCK k:%p m:(%p) t:%p o:%p\n",    THIS_KEY, THIS_KEY->mut,    Pike_interpreter.thread_state, THIS_KEY->owner));    if(THIS_KEY->mut)    {    struct mutex_storage *mut = THIS_KEY->mut;    struct object *mutex_obj;      #ifdef PIKE_DEBUG
pike.git/src/threads.c:2446:    *!    *! @note    *! The support for timeouts was added in Pike 7.8.121, which was    *! after the first public release of Pike 7.8.    *!    *! @note    *! Note that the timeout is approximate (best effort), and may    *! be exceeded if eg the mutex is busy after the timeout.    *!    *! @note -  *! In Pike 7.2 and earlier it was possible to call @[wait()] -  *! without arguments. This possibility was removed in later -  *! versions since it unavoidably leads to programs with races -  *! and/or deadlocks. -  *! -  *! @note +     *! Note also that any threads waiting on the condition will be    *! woken up when it gets destructed.    *!    *! @seealso    *! @[Mutex->lock()]    */   void f_cond_wait(INT32 args)   {    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("condition->wait", args, "%o.%F", &key, &fsecs); +  get_all_args("wait", args, "%o.%F", &key, &fsecs);    seconds = (INT_TYPE) fsecs;    nanos = (INT_TYPE)((fsecs - seconds)*1000000000);    } else {    /* FIXME: Support bignum nanos. */ -  get_all_args("condition->wait", args, "%o%i%i", &key, &seconds, &nanos); +  get_all_args("wait", args, "%o%i%i", &key, &seconds, &nanos);    }       if ((key->prog != mutex_key) ||    (!(OB2KEY(key)->initialized)) ||    (!(mut = OB2KEY(key)->mut))) { -  Pike_error("Bad argument 1 to condition->wait()\n"); +  Pike_error("Bad argument 1 to wait()\n");    }       if(args > 1) {    pop_n_elems(args - 1);    args = 1;    }       c = THIS_COND;       /* Unlock mutex */
pike.git/src/threads.c:2545:    *!    *! @[signal()] wakes up one of the threads currently waiting for the    *! condition.    *!    *! @note    *! Sometimes more than one thread is woken up.    *!    *! @seealso    *! @[broadcast()]    */ - void f_cond_signal(INT32 args) + void f_cond_signal(INT32 UNUSED(args))   { -  pop_n_elems(args); +     co_signal(&(THIS_COND->cond));   }      /*! @decl void broadcast()    *!    *! @[broadcast()] wakes up all threads currently waiting for this condition.    *!    *! @seealso    *! @[signal()]    */ - void f_cond_broadcast(INT32 args) + void f_cond_broadcast(INT32 UNUSED(args))   { -  pop_n_elems(args); +     co_broadcast(&(THIS_COND->cond));   }    - void init_cond_obj(struct object *o) + void init_cond_obj(struct object *UNUSED(o))   {    co_init(&(THIS_COND->cond));    THIS_COND->wait_count = 0;   }    - void exit_cond_obj(struct object *o) + void exit_cond_obj(struct object *UNUSED(o))   {    /* Wake up any threads that might be waiting on this cond.    *    * Note that we are already destructed (o->prog == NULL),    * so wait_count can't increase.    *    * FIXME: This code wouldn't be needed if exit callbacks were called    * only when the ref count reaches zero.    * /grubba 2006-01-29    */
pike.git/src/threads.c:2642:      /*! @decl int status()    *!    *! Returns the status of the thread.    *!    *! @returns    *! @int    *! @value Thread.THREAD_NOT_STARTED    *! @value Thread.THREAD_RUNNING    *! @value Thread.THREAD_EXITED +  *! @value Thread.THREAD_ABORTED    *! @endint    */   void f_thread_id_status(INT32 args)   {    pop_n_elems(args);    push_int(THIS_THREAD->status);   }      /*! @decl protected string _sprintf(int c)    *!
pike.git/src/threads.c:2664:   void f_thread_id__sprintf (INT32 args)   {    int c = 0;    if(args>0 && TYPEOF(Pike_sp[-args]) == 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_static_text ("Thread.Thread(");    push_int64(PTR_TO_INT(THREAD_T_TO_PTR(THIS_THREAD->id))); -  push_constant_text (")"); +  push_static_text (")");    f_add (3);   }      /*! @decl protected int id_number()    *!    *! Returns an id number identifying the thread. -  *! -  *! @note -  *! This function was added in Pike 7.2.204. +     */   void f_thread_id_id_number(INT32 args)   {    pop_n_elems(args);    push_int64(PTR_TO_INT(THREAD_T_TO_PTR(THIS_THREAD->id)));   }      /*! @decl mixed wait()    *!    *! Waits for the thread to complete, and then returns    *! the value returned from the thread function. -  +  *! +  *! @throws +  *! Rethrows the error thrown by the thread if it exited by +  *! throwing an error.    */ - static void f_thread_id_result(INT32 args) + static void f_thread_id_result(INT32 UNUSED(args))   {    struct thread_state *th=THIS_THREAD; -  +  int th_status;       if (threads_disabled) {    Pike_error("Cannot wait for threads when threads are disabled!\n");    }       th->waiting++;       THREADS_FPRINTF(0, (stderr, "Thread->wait(): Waiting for thread_state %p "    "(state:%d).\n", th, th->status)); -  while(th->status != THREAD_EXITED) { +  while(th->status < THREAD_EXITED) {    SWAP_OUT_CURRENT_THREAD();    co_wait_interpreter(&th->status_change);    SWAP_IN_CURRENT_THREAD();    check_threads_etc();    THREADS_FPRINTF(0,    (stderr, "Thread->wait(): Waiting for thread_state %p "    "(state:%d).\n", th, th->status));    }    -  +  th_status = th->status; +     assign_svalue_no_free(Pike_sp, &th->result);    Pike_sp++;    dmalloc_touch_svalue(Pike_sp-1);       th->waiting--;       if (!th->thread_obj)    /* Do this only if exit_thread_obj already has run. */    cleanup_thread_state (th); -  +  +  if (th_status == THREAD_ABORTED) f_throw(1);   }      static int num_pending_interrupts = 0;   static struct callback *thread_interrupt_callback = NULL;      static void check_thread_interrupt(struct callback *foo, -  void *bar, void *gazonk) +  void *UNUSED(bar), void *UNUSED(gazonk))   { -  +  if (Pike_interpreter.thread_state->flags & THREAD_FLAG_INHIBIT) { +  return; +  }    if (Pike_interpreter.thread_state->flags & THREAD_FLAG_SIGNAL_MASK) {    if (Pike_interpreter.thread_state->flags & THREAD_FLAG_TERM) {    throw_severity = THROW_THREAD_EXIT;    } else {    throw_severity = THROW_ERROR;    }    Pike_interpreter.thread_state->flags &= ~THREAD_FLAG_SIGNAL_MASK;    if (!--num_pending_interrupts) {    remove_callback(foo);    thread_interrupt_callback = NULL;
pike.git/src/threads.c:2765:    *!    *! @note    *! Interrupts are asynchronous, and are currently not queued.    */   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)) { -  THIS_THREAD->flags |= THREAD_FLAG_INTR; +     num_pending_interrupts++;    if (!thread_interrupt_callback) {    thread_interrupt_callback =    add_to_callback(&evaluator_callbacks, check_thread_interrupt, 0, 0);    }    /* FIXME: Actually interrupt the thread. */    } -  +  THIS_THREAD->flags |= THREAD_FLAG_INTR;    push_int(0);   }      static void low_thread_kill (struct thread_state *th)   {    if (!(th->flags & THREAD_FLAG_SIGNAL_MASK)) {    num_pending_interrupts++;    if (!thread_interrupt_callback) {    thread_interrupt_callback =    add_to_callback(&evaluator_callbacks, check_thread_interrupt, 0, 0);
pike.git/src/threads.c:2803:    *! @note    *! Interrupts are asynchronous, and are currently not queued.    */   static void f_thread_id_kill(INT32 args)   {    pop_n_elems(args);    low_thread_kill (THIS_THREAD);    push_int(0);   }    - void init_thread_obj(struct object *o) + void init_thread_obj(struct object *UNUSED(o))   { -  MEMSET(&THIS_THREAD->state, 0, sizeof(struct Pike_interpreter_struct)); +  memset(&THIS_THREAD->state, 0, sizeof(struct Pike_interpreter_struct));    THIS_THREAD->thread_obj = Pike_fp->current_object;    THIS_THREAD->swapped = 0;    THIS_THREAD->status=THREAD_NOT_STARTED;    THIS_THREAD->flags = 0;    THIS_THREAD->waiting = 0;    SET_SVAL(THIS_THREAD->result, T_INT, NUMBER_UNDEFINED, integer, 0);    co_init(& THIS_THREAD->status_change); -  THIS_THREAD->thread_local=NULL; +  THIS_THREAD->thread_locals=NULL;   #ifdef CPU_TIME_MIGHT_BE_THREAD_LOCAL    THIS_THREAD->auto_gc_time = 0;   #endif   }      static void cleanup_thread_state (struct thread_state *th)   {   #ifdef PIKE_DEBUG    if (th->thread_obj) Pike_fatal ("Thread state still active.\n");   #endif
pike.git/src/threads.c:2842:    if (!--num_pending_interrupts) {    remove_callback(thread_interrupt_callback);    thread_interrupt_callback = NULL;    }    }       co_destroy(& THIS_THREAD->status_change);    th_destroy(& THIS_THREAD->id);   }    - void exit_thread_obj(struct object *o) + void exit_thread_obj(struct object *UNUSED(o))   {    THIS_THREAD->thread_obj = NULL;       cleanup_thread_state (THIS_THREAD);    -  if(THIS_THREAD->thread_local != NULL) { -  free_mapping(THIS_THREAD->thread_local); -  THIS_THREAD->thread_local = NULL; +  if(THIS_THREAD->thread_locals != NULL) { +  free_mapping(THIS_THREAD->thread_locals); +  THIS_THREAD->thread_locals = NULL;    }   }      /*! @endclass    */    - static void thread_was_recursed(struct object *o) + static void thread_was_recursed(struct object *UNUSED(o))   {    struct thread_state *tmp=THIS_THREAD; -  if(tmp->thread_local != NULL) -  gc_recurse_mapping(tmp->thread_local); +  if(tmp->thread_locals != NULL) +  gc_recurse_mapping(tmp->thread_locals);   }    - static void thread_was_checked(struct object *o) + static void thread_was_checked(struct object *UNUSED(o))   {    struct thread_state *tmp=THIS_THREAD; -  if(tmp->thread_local != NULL) -  debug_gc_check (tmp->thread_local, +  if(tmp->thread_locals != NULL) +  debug_gc_check (tmp->thread_locals,    " as mapping for thread local values in thread");      #ifdef PIKE_DEBUG    if(tmp->swapped)    gc_mark_stack_external (tmp->state.frame_pointer, tmp->state.stack_pointer,    tmp->state.evaluator_stack);   #endif   }      /*! @class Local
pike.git/src/threads.c:2893:    *! thread that uses it. It has two methods: @[get()] and @[set()]. A value    *! stored in an instance of @[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()? */ - void f_thread_local_create( INT32 args ) + void f_thread_local_create( INT32 UNUSED(args) )   {    static INT32 thread_local_id = 0; -  ((struct thread_local *)CURRENT_STORAGE)->id = +  ((struct thread_local_var *)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);   }      /*! @decl mixed get()
pike.git/src/threads.c:2924:    *! the @[set()] method by this thread.    *!    *! @seealso    *! @[set()]    */   void f_thread_local_get(INT32 args)   {    struct svalue key;    struct mapping *m;    SET_SVAL(key, T_INT, NUMBER_NUMBER, integer, -  ((struct thread_local *)CURRENT_STORAGE)->id); +  ((struct thread_local_var *)CURRENT_STORAGE)->id);    pop_n_elems(args);    if(Pike_interpreter.thread_state != NULL && -  (m = Pike_interpreter.thread_state->thread_local) != NULL) +  (m = Pike_interpreter.thread_state->thread_locals) != NULL)    mapping_index_no_free(Pike_sp++, m, &key);    else {    push_undefined();    }   }      /*! @decl mixed set(mixed value)    *!    *! Set the thread local value.    *!
pike.git/src/threads.c:2958:    *! 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;    SET_SVAL(key, T_INT, NUMBER_NUMBER, integer, -  ((struct thread_local *)CURRENT_STORAGE)->id); +  ((struct thread_local_var *)CURRENT_STORAGE)->id);    if(args>1)    pop_n_elems(args-1);    else if(args<1)    SIMPLE_TOO_FEW_ARGS_ERROR("Thread.Local.set", 1);       if(Pike_interpreter.thread_state == NULL)    Pike_error("Trying to set Thread.Local without thread!\n");    -  if((m = Pike_interpreter.thread_state->thread_local) == NULL) -  m = Pike_interpreter.thread_state->thread_local = +  if((m = Pike_interpreter.thread_state->thread_locals) == NULL) +  m = Pike_interpreter.thread_state->thread_locals =    allocate_mapping(4);       mapping_insert(m, &key, &Pike_sp[-1]);   }      #ifdef PIKE_DEBUG - void gc_check_thread_local (struct object *o) + void gc_check_thread_local (struct object *UNUSED(o))   {    /* Only used by with locate_references. */    if (Pike_in_gc == GC_PASS_LOCATE) {    struct svalue key, *val;    struct thread_state *s;       SET_SVAL(key, T_INT, NUMBER_NUMBER, integer, -  ((struct thread_local *)CURRENT_STORAGE)->id); +  ((struct thread_local_var *)CURRENT_STORAGE)->id);       FOR_EACH_THREAD (s, { -  if (s->thread_local && -  (val = low_mapping_lookup(s->thread_local, &key))) +  if (s->thread_locals && +  (val = low_mapping_lookup(s->thread_locals, &key)))    debug_gc_check_svalues (val, 1,    " as thread local value in Thread.Local object"    " (indirect ref)");    });    }   }   #endif      /*! @endclass    */
pike.git/src/threads.c:3035: Inside #if defined(HAVE_BROKEN_LINUX_THREAD_EUID)
  #ifdef HAVE_BROKEN_LINUX_THREAD_EUID    /* Work-around for Linux's pthreads not propagating the    * effective uid & gid.    */    if (!geteuid()) {   #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)    /* The sete?id calls will clear the dumpable state that we might    * have set with system.dumpable. */    int current = prctl(PR_GET_DUMPABLE);   #endif -  setegid(me->egid); -  seteuid(me->euid); + #ifdef HAVE_BROKEN_LINUX_THREAD_EUID +  if( setegid(arg.egid) != 0 || seteuid(arg.euid) != 0 ) +  { +  fprintf (stderr, "%s:%d: Unexpected error from setegid(2). errno=%d\n", +  __FILE__, __LINE__, errno); +  } + #endif   #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)    if (prctl(PR_SET_DUMPABLE, current) == -1)    Pike_fatal ("Didn't expect prctl to go wrong. errno=%d\n", errno);   #endif    }   #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */       do    {   /* if(farmers == me) Pike_fatal("Ouch!\n"); */
pike.git/src/threads.c:3069:    return 0;    }    me->neighbour = farmers;    farmers = me;   /* fprintf(stderr, "farm_wait %p\n", me); */    while(!me->harvest) co_wait( &me->harvest_moon, &rosie );    --_num_idle_farmers;    mt_unlock( &rosie );   /* fprintf(stderr, "farm_endwait %p\n", me); */    } while(1); -  /* NOT_REACHED */ -  return 0;/* Keep the compiler happy. */ +  UNREACHABLE(return 0);   }      int th_num_idle_farmers(void)   {    return _num_idle_farmers;   }         int th_num_farmers(void)   {
pike.git/src/threads.c:3154:    mt_init( & interleave_lock);    mt_init( & rosie);    co_init( & live_threads_change);    co_init( & threads_disabled_change);    thread_table_init();      #if defined(RDTSC) && defined(USE_CLOCK_FOR_SLICES)    {    INT32 cpuid[4];    x86_get_cpuid (1, cpuid); -  /* fprintf (stderr, "cpuid 1: %x\n", cpuid[2]); */ +     use_tsc_for_slices = cpuid[2] & 0x10; /* TSC exists */   #if 0    /* Skip tsc invariant check - the current tsc interval method    * should be robust enough to cope with variable tsc rates. */    if (use_tsc_for_slices) {    x86_get_cpuid (0x80000007, cpuid); -  /* fprintf (stderr, "cpuid 0x80000007: %x\n", cpuid[2]); */ +     use_tsc_for_slices = cpuid[2] & 0x100; /* TSC is invariant */    }   #endif -  /* fprintf (stderr, "use tsc: %d\n", use_tsc_for_slices); */ +     }   #endif       th_running = 1;   }      static struct object *backend_thread_obj = NULL;      static struct Pike_interpreter_struct *original_interpreter = NULL;   
pike.git/src/threads.c:3267:    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_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);   #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(tNone,tMixed,tVoid), +  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("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);
pike.git/src/threads.c:3317:    add_global_program("thread_create", thread_id_prog);       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_EFUN("get_thread_quanta", f_get_thread_quanta, +  tFunc(tNone, tInt), +  OPT_EXTERNAL_DEPEND); +  +  ADD_EFUN("set_thread_quanta", f_set_thread_quanta, +  tFunc(tInt, tInt), +  OPT_EXTERNAL_DEPEND); +     /* Some constants... */    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); -  +  add_integer_constant("THREAD_ABORTED", THREAD_ABORTED, 0);       original_interpreter = Pike_interpreter_pointer;    backend_thread_obj = fast_clone_object(thread_id_prog);    INIT_THREAD_STATE((struct thread_state *)(backend_thread_obj->storage +    thread_storage_offset));    thread_table_insert(Pike_interpreter.thread_state);   }      #ifdef DO_PIKE_CLEANUP   void cleanup_all_other_threads (void)