Branch: Tag:

2015-09-09

2015-09-09 14:20:56 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Threads: Added support for setting a thread time quanta.

This is mostly intended to be used to identify code that
inhibits other threads from running.

59:    */   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
572:    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++;
711:      #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
718:    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)
758:    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);
772:    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)
821:    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);
2025:    }   }    + /*! @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))      
3384:    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);