e576bb2002-10-11Martin Nilsson /* || 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. */
1b10db2002-10-08Martin Nilsson 
07513e1996-10-04Fredrik Hübinette (Hubbe) #include "global.h"
a29e021996-10-15Fredrik Hübinette (Hubbe) 
2702952004-04-21Martin Stjernholm /* #define PICKY_MUTEX */
c7f2712007-06-09Martin Stjernholm #include "pike_error.h"
91518c2009-03-15Martin Stjernholm /* Define to get a debug trace of thread operations. Debug levels can be 0-2. */ /* #define VERBOSE_THREADS_DEBUG 1 */
83b1842003-02-08Martin Stjernholm 
d0aa0d2011-04-02Martin Stjernholm /* #define PROFILE_CHECK_THREADS */
91518c2009-03-15Martin Stjernholm #ifndef CONFIGURE_TEST
8a26932014-07-21Per Hedbor #define IN_THREAD_CODE
07513e1996-10-04Fredrik Hübinette (Hubbe) #include "threads.h" #include "array.h"
d86cd71998-08-24Marcus Comstedt #include "mapping.h"
07513e1996-10-04Fredrik Hübinette (Hubbe) #include "object.h"
bb55f81997-03-16Fredrik Hübinette (Hubbe) #include "pike_macros.h"
a29e021996-10-15Fredrik Hübinette (Hubbe) #include "callback.h"
9c6f7d1997-04-15Fredrik Hübinette (Hubbe) #include "builtin_functions.h" #include "constants.h"
be478c1997-08-30Henrik Grubbström (Grubba) #include "program.h"
3f87be1999-12-14Martin Stjernholm #include "program_id.h"
c5b96b1997-11-11Henrik Grubbström (Grubba) #include "gc.h"
3c0c281998-01-26Fredrik Hübinette (Hubbe) #include "main.h"
a49ee61999-04-02Fredrik Hübinette (Hubbe) #include "module_support.h"
52e4c61999-12-13Martin Stjernholm #include "pike_types.h"
9a0d422000-02-06Martin Stjernholm #include "operators.h"
7e8ea32000-08-13Henrik Grubbström (Grubba) #include "bignum.h"
700dac2002-02-05Martin Stjernholm #include "signal_handler.h"
4a5f542009-01-25Henrik Grubbström (Grubba) #include "backend.h"
d5c61f2002-12-07Henrik Grubbström (Grubba) #include "pike_rusage.h"
f919942010-10-17Martin Stjernholm #include "pike_cpulib.h"
d888282018-02-09Martin Nilsson #include "pike_compiler.h"
67d9142019-05-30Henrik Grubbström (Grubba) #include "sprintf.h"
07513e1996-10-04Fredrik Hübinette (Hubbe) 
cd11fa1999-05-13Henrik Grubbström (Grubba) #include <errno.h>
466caa2010-10-23Martin Stjernholm #include <math.h>
4dbca32015-10-18Martin Nilsson #include <time.h>
cd11fa1999-05-13Henrik Grubbström (Grubba) 
f663a02003-03-05Martin Stjernholm #ifdef HAVE_SYS_PRCTL_H #include <sys/prctl.h> #endif /* HAVE_SYS_PRCTL_H */
08c53c2003-11-25Jonas Wallden #ifdef HAVE_SYS_TIME_H #include <sys/time.h> #endif
8a26932014-07-21Per Hedbor /* 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;
7a52982015-09-09Henrik Grubbström (Grubba) static cpu_time_t thread_quanta = 0;
1d16162014-08-25Per Hedbor PMOD_EXPORT struct Pike_interpreter_struct * #if defined(__GNUC__) && __GNUC__ >= 3 __restrict #endif Pike_interpreter_pointer = &static_pike_interpreter;
8a26932014-07-21Per Hedbor 
3b54e52014-08-21Martin Nilsson PMOD_EXPORT struct Pike_interpreter_struct * pike_get_interpreter_pointer(void)
84c9d92014-07-22Per Hedbor { return Pike_interpreter_pointer; }
91518c2009-03-15Martin Stjernholm #else /* CONFIGURE_TEST */ #include "pike_threadlib.h" #endif
ef1e931998-03-26Henrik Grubbström (Grubba) 
a58dc82019-05-01Marcus Comstedt #ifdef _REENTRANT
91518c2009-03-15Martin Stjernholm #ifndef VERBOSE_THREADS_DEBUG
7d06ba2016-02-07Martin Nilsson #define THREADS_FPRINTF(LEVEL,...)
7d7b022003-04-02Martin Stjernholm #else
7d06ba2016-02-07Martin Nilsson #define THREADS_FPRINTF(LEVEL,...) do { \
91518c2009-03-15Martin Stjernholm  if ((VERBOSE_THREADS_DEBUG + 0) >= (LEVEL)) { \ /* E.g. THREADS_DISALLOW is used in numerous places where the */ \ /* value in errno must not be clobbered. */ \ int saved_errno = errno; \ fprintf (stderr, "[%"PRINTSIZET"x] ", (size_t) th_self()); \
7d06ba2016-02-07Martin Nilsson  fprintf (stderr, __VA_ARGS__); \
91518c2009-03-15Martin Stjernholm  errno = saved_errno; \ } \ } while(0) #endif /* VERBOSE_THREADS_DEBUG */
7d7b022003-04-02Martin Stjernholm 
e3ee062005-03-31Martin Nilsson #ifndef PIKE_THREAD_C_STACK_SIZE #define PIKE_THREAD_C_STACK_SIZE (256 * 1024) #endif PMOD_EXPORT size_t thread_stack_size=PIKE_THREAD_C_STACK_SIZE;
c432f72003-05-02Henrik Grubbström (Grubba) PMOD_EXPORT void thread_low_error (int errcode, const char *cmd, const char *fname, int lineno)
01185d2003-04-01Martin Stjernholm {
c7f2712007-06-09Martin Stjernholm #ifdef CONFIGURE_TEST fprintf (stderr, "%s:%d: %s\n" "Unexpected error from thread function: %d\n", fname, lineno, cmd, errcode); abort(); #else
91518c2009-03-15Martin Stjernholm  debug_fatal ("%s:%d: Fatal error: %s\n" "Unexpected error from thread function: %d\n", fname, lineno, cmd, errcode);
c7f2712007-06-09Martin Stjernholm #endif
01185d2003-04-01Martin Stjernholm }
1f88bf2001-09-24Henrik Grubbström (Grubba) /* SCO magic... */ int __thread_sys_behavior = 1;
83b1842003-02-08Martin Stjernholm #ifndef CONFIGURE_TEST
3c55762001-09-20Fredrik Hübinette (Hubbe) #if !defined(HAVE_PTHREAD_ATFORK) && !defined(th_atfork)
71ac9e1999-08-29Fredrik Hübinette (Hubbe) #include "callback.h"
58580c2001-11-12Martin Stjernholm #define PIKE_USE_OWN_ATFORK
71ac9e1999-08-29Fredrik Hübinette (Hubbe) 
3c55762001-09-20Fredrik Hübinette (Hubbe) 
71ac9e1999-08-29Fredrik Hübinette (Hubbe) static struct callback_list atfork_prepare_callback; static struct callback_list atfork_parent_callback; static struct callback_list atfork_child_callback; int th_atfork(void (*prepare)(void),void (*parent)(void),void (*child)(void)) { if(prepare) add_to_callback(&atfork_prepare_callback, (callback_func) prepare, 0, 0); if(parent) add_to_callback(&atfork_parent_callback, (callback_func) parent, 0, 0); if(child) add_to_callback(&atfork_child_callback, (callback_func) child, 0, 0); return 0; } void th_atfork_prepare(void) { call_callback(& atfork_prepare_callback, 0); } void th_atfork_parent(void) { call_callback(& atfork_parent_callback, 0); } void th_atfork_child(void) { call_callback(& atfork_child_callback, 0); } #endif
83b1842003-02-08Martin Stjernholm #endif /* !CONFIGURE_TEST */
dc7cc91998-01-14Fredrik Hübinette (Hubbe) #ifdef __NT__
91518c2009-03-15Martin Stjernholm PMOD_EXPORT int low_nt_create_thread(unsigned Pike_stack_size, unsigned (TH_STDCALL *fun)(void *), void *arg, unsigned *id)
cd67ac1999-05-11Fredrik Hübinette (Hubbe) {
7965d72001-01-24Fredrik Hübinette (Hubbe)  HANDLE h = (HANDLE)_beginthreadex(NULL, Pike_stack_size, fun, arg, 0, id);
cd67ac1999-05-11Fredrik Hübinette (Hubbe)  if(h) { CloseHandle(h); return 0; } else {
5182452012-06-28Martin Stjernholm  return errno;
cd67ac1999-05-11Fredrik Hübinette (Hubbe)  } }
dc7cc91998-01-14Fredrik Hübinette (Hubbe) #endif
e42eaf1998-01-02Fredrik Hübinette (Hubbe) #ifdef SIMULATE_COND_WITH_EVENT
74ea782016-01-29Martin Nilsson PMOD_EXPORT int co_wait(COND_T *c, PIKE_MUTEX_T *m)
e42eaf1998-01-02Fredrik Hübinette (Hubbe) { struct cond_t_queue me; event_init(&me.event);
dc7cc91998-01-14Fredrik Hübinette (Hubbe)  me.next=0;
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  mt_lock(& c->lock);
dc7cc91998-01-14Fredrik Hübinette (Hubbe)  if(c->tail) { c->tail->next=&me; c->tail=&me; }else{ c->head=c->tail=&me; }
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  mt_unlock(& c->lock); mt_unlock(m);
cb06582009-04-25Martin Stjernholm  /* NB: No race here since the event is manually reset. */
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  event_wait(&me.event); mt_lock(m); event_destroy(& me.event); /* Cancellation point?? */
71f3a21998-11-22Fredrik Hübinette (Hubbe) #ifdef PIKE_DEBUG
dc7cc91998-01-14Fredrik Hübinette (Hubbe)  if(me.next)
5aad932002-08-15Marcus Comstedt  Pike_fatal("Wait on event return prematurely!!\n");
dc7cc91998-01-14Fredrik Hübinette (Hubbe) #endif
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  return 0; }
74ea782016-01-29Martin Nilsson PMOD_EXPORT int co_wait_timeout(COND_T *c, PIKE_MUTEX_T *m, long s, long nanos)
4a5f542009-01-25Henrik Grubbström (Grubba) { struct cond_t_queue me; event_init(&me.event); me.next=0; mt_lock(& c->lock); if(c->tail) { c->tail->next=&me; c->tail=&me; }else{ c->head=c->tail=&me; } mt_unlock(& c->lock); mt_unlock(m); if (s || nanos) {
e7f7052009-01-26Henrik Grubbström (Grubba)  DWORD msec; /* Check for overflow (0xffffffff/1000). */ if (s >= 4294967) { msec = INFINITE; } else { msec = s*1000 + nanos/1000000; if (!msec) msec = 1; /* Underflow. */ } event_wait_msec(&me.event, msec);
4a5f542009-01-25Henrik Grubbström (Grubba)  } else { event_wait(&me.event); } mt_lock(m); event_destroy(& me.event); /* Cancellation point?? */ #ifdef PIKE_DEBUG if(me.next) Pike_fatal("Wait on event return prematurely!!\n"); #endif return 0; }
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT int co_signal(COND_T *c)
e42eaf1998-01-02Fredrik Hübinette (Hubbe) { struct cond_t_queue *t; mt_lock(& c->lock);
dc7cc91998-01-14Fredrik Hübinette (Hubbe)  if((t=c->head))
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  { c->head=t->next; t->next=0; if(!c->head) c->tail=0; } mt_unlock(& c->lock); if(t) event_signal(& t->event); return 0; }
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT int co_broadcast(COND_T *c)
e42eaf1998-01-02Fredrik Hübinette (Hubbe) { struct cond_t_queue *t,*n; mt_lock(& c->lock); n=c->head; c->head=c->tail=0; mt_unlock(& c->lock); while((t=n)) { n=t->next;
dc7cc91998-01-14Fredrik Hübinette (Hubbe)  t->next=0;
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  event_signal(& t->event); } return 0; }
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT int co_destroy(COND_T *c)
e42eaf1998-01-02Fredrik Hübinette (Hubbe) { struct cond_t_queue *t; mt_lock(& c->lock);
b1f4eb1998-01-13Fredrik Hübinette (Hubbe)  t=c->head; mt_unlock(& c->lock); if(t) return -1; mt_destroy(& c->lock);
e42eaf1998-01-02Fredrik Hübinette (Hubbe)  return 0; }
4a5f542009-01-25Henrik Grubbström (Grubba) #else /* !SIMULATE_COND_WITH_EVENT */
3d70c52009-01-25Henrik Grubbström (Grubba) #ifndef CONFIGURE_TEST
91518c2009-03-15Martin Stjernholm PMOD_EXPORT int co_wait_timeout(COND_T *c, PIKE_MUTEX_T *m, long s, long nanos)
4a5f542009-01-25Henrik Grubbström (Grubba) {
f010202011-11-16Tobias S. Josefowitz  struct timeval ct;
4a5f542009-01-25Henrik Grubbström (Grubba) #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. */
f010202011-11-16Tobias S. Josefowitz  ACCURATE_GETTIMEOFDAY(&ct); timeout.tv_sec = ct.tv_sec + s; timeout.tv_nsec = ct.tv_usec * 1000 + nanos;
4a5f542009-01-25Henrik Grubbström (Grubba)  return pthread_cond_timedwait(c, m, &timeout); #endif /* HAVE_PTHREAD_COND_RELTIMEDWAIT_NP */ #else /* !POSIX_THREADS */
d2a3f02009-01-25Henrik Grubbström (Grubba) #error co_wait_timeout does not support this thread model.
4a5f542009-01-25Henrik Grubbström (Grubba) #endif /* POSIX_THREADS */ }
3d70c52009-01-25Henrik Grubbström (Grubba) #endif /* !CONFIGURE_TEST */
4a5f542009-01-25Henrik Grubbström (Grubba)  #endif /* SIMULATE_COND_WITH_EVENT */
e42eaf1998-01-02Fredrik Hübinette (Hubbe) 
83b1842003-02-08Martin Stjernholm #ifdef POSIX_THREADS pthread_attr_t pattr; pthread_attr_t small_pattr; #endif static void really_low_th_init(void) { #ifdef SGI_SPROC_THREADS #error /* Need to specify a filename */ us_cookie = usinit(""); #endif /* SGI_SPROC_THREADS */ #ifdef POSIX_THREADS #ifdef HAVE_PTHREAD_INIT pthread_init(); #endif /* HAVE_PTHREAD_INIT */ #endif /* POSIX_THREADS */ #ifdef POSIX_THREADS pthread_attr_init(&pattr); #ifndef CONFIGURE_TEST #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE pthread_attr_setstacksize(&pattr, thread_stack_size); #endif #endif pthread_attr_setdetachstate(&pattr, PTHREAD_CREATE_DETACHED); pthread_attr_init(&small_pattr); #ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE pthread_attr_setstacksize(&small_pattr, 4096*sizeof(char *)); #endif pthread_attr_setdetachstate(&small_pattr, PTHREAD_CREATE_DETACHED); #endif }
0a14c22008-08-05Martin Stjernholm static void cleanup_thread_state (struct thread_state *th);
83b1842003-02-08Martin Stjernholm #ifndef CONFIGURE_TEST
e42eaf1998-01-02Fredrik Hübinette (Hubbe) 
8bfefe2014-09-04Martin Nilsson #if defined (RDTSC) || \
59e8272010-10-17Martin Stjernholm  (!defined(HAVE_GETHRTIME) && \
8bfefe2014-09-04Martin Nilsson  !(defined(HAVE_MACH_TASK_INFO_H) && defined(TASK_THREAD_TIMES_INFO)))
91518c2009-03-15Martin Stjernholm static clock_t thread_start_clock = 0; static THREAD_T last_clocked_thread = 0; #define USE_CLOCK_FOR_SLICES
da66442010-10-23Martin Stjernholm #ifdef RDTSC static int use_tsc_for_slices; #define TSC_START_INTERVAL 100000
e03e2c2011-04-02Martin Stjernholm static INT64 prev_tsc; /* TSC and */
a771ee2010-10-24Martin Stjernholm static clock_t prev_clock; /* clock() at the beg of the last tsc interval */
da66442010-10-23Martin Stjernholm #endif
91518c2009-03-15Martin Stjernholm #endif
17f08c2000-07-06Fredrik Hübinette (Hubbe) #define THIS_THREAD ((struct thread_state *)CURRENT_STORAGE)
5740881998-01-01Fredrik Hübinette (Hubbe) 
a29e021996-10-15Fredrik Hübinette (Hubbe) static struct callback *threads_evaluator_callback=0;
07513e1996-10-04Fredrik Hübinette (Hubbe) 
91518c2009-03-15Martin Stjernholm PMOD_EXPORT int num_threads = 1; PMOD_EXPORT int threads_disabled = 0;
fb7d862015-06-01Henrik Grubbström (Grubba) PMOD_EXPORT cpu_time_t threads_disabled_acc_time = 0; PMOD_EXPORT cpu_time_t threads_disabled_start = 0;
91518c2009-03-15Martin Stjernholm 
c91f892000-04-19Martin Stjernholm #ifdef PIKE_DEBUG
9c4f692020-07-05Henrik Grubbström (Grubba) /* NB: Accessed from error.c:debug_va_fatal(). */
7167852021-05-16Henrik Grubbström (Grubba) THREAD_T threads_disabled_thread = 0;
c91f892000-04-19Martin Stjernholm #endif
c5f4e22002-09-14Martin Stjernholm #ifdef INTERNAL_PROFILING PMOD_EXPORT unsigned long thread_yields = 0; #endif
91518c2009-03-15Martin Stjernholm static int th_running = 0;
74ea782016-01-29Martin Nilsson 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;
5c2f2f2018-09-16Henrik Grubbström (Grubba) static struct program *mutex_program = NULL;
91518c2009-03-15Martin Stjernholm static struct program *mutex_key = 0;
ca85822019-08-21Henrik Grubbström (Grubba) static struct program *rwmutex_program = NULL;
cff4172020-06-13Henrik Grubbström (Grubba) static struct program *rwmutex_key_program = NULL;
f327f22018-09-23Henrik Grubbström (Grubba) static struct program *condition_program = NULL;
fa8c692000-11-30Fredrik Hübinette (Hubbe) PMOD_EXPORT struct program *thread_id_prog = 0;
91518c2009-03-15Martin Stjernholm static struct program *thread_local_prog = 0;
89fc4c2000-08-10Henrik Grubbström (Grubba) PMOD_EXPORT ptrdiff_t thread_storage_offset;
91518c2009-03-15Martin Stjernholm static int live_threads = 0; static COND_T live_threads_change; static COND_T threads_disabled_change;
621e752015-10-19Per Hedbor struct thread_local_var
91518c2009-03-15Martin Stjernholm { INT32 id; }; static volatile IMUTEX_T *interleave_list = NULL; #define THREADSTATE2OBJ(X) ((X)->thread_obj)
fcca612016-02-11Martin Nilsson #ifdef PIKE_DEBUG
91518c2009-03-15Martin Stjernholm  /* This is a debug wrapper to enable checks that the interpreter lock
ca39b52010-10-31Martin Stjernholm  * is held by the current thread. */ static int debug_is_locked;
91518c2009-03-15Martin Stjernholm static THREAD_T debug_locking_thread;
ca39b52010-10-31Martin Stjernholm #define SET_LOCKING_THREAD (debug_is_locked = 1, \ debug_locking_thread = th_self()) #define UNSET_LOCKING_THREAD (debug_is_locked = 0)
01b9212016-01-12Per Hedbor static void check_interpreter_lock (DLOC_DECL)
91518c2009-03-15Martin Stjernholm { if (th_running) { THREAD_T self;
ca39b52010-10-31Martin Stjernholm  if (!debug_is_locked)
91518c2009-03-15Martin Stjernholm  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"); } }
d559142017-07-17Martin Nilsson static UINT64 thread_swaps = 0; static UINT64 check_threads_calls = 0; static UINT64 check_threads_yields = 0; static UINT64 check_threads_swaps = 0;
e8989c2014-08-22Arne Goedeke static void f__thread_swaps (INT32 UNUSED(args))
15ecdc2011-04-02Martin Stjernholm  {push_ulongest (thread_swaps);}
e8989c2014-08-22Arne Goedeke static void f__check_threads_calls (INT32 UNUSED(args))
15ecdc2011-04-02Martin Stjernholm  {push_ulongest (check_threads_calls);}
e8989c2014-08-22Arne Goedeke static void f__check_threads_yields (INT32 UNUSED(args))
15ecdc2011-04-02Martin Stjernholm  {push_ulongest (check_threads_yields);}
e8989c2014-08-22Arne Goedeke static void f__check_threads_swaps (INT32 UNUSED(args))
15ecdc2011-04-02Martin Stjernholm  {push_ulongest (check_threads_swaps);}
91518c2009-03-15Martin Stjernholm #else #define SET_LOCKING_THREAD 0
ca39b52010-10-31Martin Stjernholm #define UNSET_LOCKING_THREAD 0
91518c2009-03-15Martin Stjernholm 
fcca612016-02-11Martin Nilsson #endif /* PIKE_DEBUG */
91518c2009-03-15Martin Stjernholm 
01b9212016-01-12Per Hedbor PMOD_EXPORT void pike_low_lock_interpreter (DLOC_DECL)
91518c2009-03-15Martin Stjernholm {
63ba772010-10-12Martin Stjernholm  /* 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);
91518c2009-03-15Martin Stjernholm  mt_lock (&interpreter_lock);
63ba772010-10-12Martin Stjernholm  mt_unlock (&interpreter_lock_wanted);
91518c2009-03-15Martin Stjernholm  SET_LOCKING_THREAD;
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Got iplock @ %s:%d\n", DLOC_ARGS_OPT);
91518c2009-03-15Martin Stjernholm }
01b9212016-01-12Per Hedbor PMOD_EXPORT void pike_low_wait_interpreter (COND_T *cond COMMA_DLOC_DECL)
91518c2009-03-15Martin Stjernholm {
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Waiting on cond %p without iplock @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  cond, DLOC_ARGS_OPT);
ca39b52010-10-31Martin Stjernholm  UNSET_LOCKING_THREAD;
63ba772010-10-12Martin Stjernholm  /* 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. */
91518c2009-03-15Martin Stjernholm  co_wait (cond, &interpreter_lock);
63ba772010-10-12Martin Stjernholm 
91518c2009-03-15Martin Stjernholm  SET_LOCKING_THREAD;
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Got signal on cond %p with iplock @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  cond, DLOC_ARGS_OPT);
91518c2009-03-15Martin Stjernholm }
01b9212016-01-12Per Hedbor PMOD_EXPORT int pike_low_timedwait_interpreter (COND_T *cond,
91518c2009-03-15Martin Stjernholm  long sec, long nsec COMMA_DLOC_DECL) { int res;
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Waiting on cond %p without iplock @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  cond, DLOC_ARGS_OPT);
ca39b52010-10-31Martin Stjernholm  UNSET_LOCKING_THREAD;
63ba772010-10-12Martin Stjernholm  /* 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. */
91518c2009-03-15Martin Stjernholm  res = co_wait_timeout (cond, &interpreter_lock, sec, nsec);
63ba772010-10-12Martin Stjernholm 
91518c2009-03-15Martin Stjernholm  SET_LOCKING_THREAD;
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Got signal on cond %p with iplock @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  cond, DLOC_ARGS_OPT);
91518c2009-03-15Martin Stjernholm  return res; } static void threads_disabled_wait (DLOC_DECL) { assert (threads_disabled);
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
91518c2009-03-15Martin Stjernholm  do {
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Waiting on threads_disabled @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  DLOC_ARGS_OPT);
ca39b52010-10-31Martin Stjernholm  UNSET_LOCKING_THREAD;
91518c2009-03-15Martin Stjernholm  co_wait (&threads_disabled_change, &interpreter_lock); SET_LOCKING_THREAD; } while (threads_disabled);
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Continue after threads_disabled @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  DLOC_ARGS_OPT);
91518c2009-03-15Martin Stjernholm }
01b9212016-01-12Per Hedbor PMOD_EXPORT void pike_lock_interpreter (DLOC_DECL)
91518c2009-03-15Martin Stjernholm { pike_low_lock_interpreter (DLOC_ARGS_OPT); if (threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT); }
01b9212016-01-12Per Hedbor PMOD_EXPORT void pike_unlock_interpreter (DLOC_DECL)
91518c2009-03-15Martin Stjernholm {
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Releasing iplock @ %s:%d\n", DLOC_ARGS_OPT);
ca39b52010-10-31Martin Stjernholm  UNSET_LOCKING_THREAD;
91518c2009-03-15Martin Stjernholm  mt_unlock (&interpreter_lock); }
01b9212016-01-12Per Hedbor PMOD_EXPORT void pike_wait_interpreter (COND_T *cond COMMA_DLOC_DECL)
91518c2009-03-15Martin Stjernholm {
8d3d582013-02-18Henrik Grubbström (Grubba)  int owner = threads_disabled;
91518c2009-03-15Martin Stjernholm  pike_low_wait_interpreter (cond COMMA_DLOC_ARGS_OPT);
1c93882017-08-10Henrik Grubbström (Grubba)  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); }
91518c2009-03-15Martin Stjernholm }
01b9212016-01-12Per Hedbor PMOD_EXPORT int pike_timedwait_interpreter (COND_T *cond,
91518c2009-03-15Martin Stjernholm  long sec, long nsec COMMA_DLOC_DECL) {
cb04462013-02-18Henrik Grubbström (Grubba)  int owner = threads_disabled;
91518c2009-03-15Martin Stjernholm  int res = pike_low_timedwait_interpreter (cond, sec, nsec COMMA_DLOC_ARGS_OPT);
1c93882017-08-10Henrik Grubbström (Grubba)  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); }
91518c2009-03-15Martin Stjernholm  return res; } PMOD_EXPORT void pike_init_thread_state (struct thread_state *ts) {
d97eb72011-07-10Henrik Grubbström (Grubba)  /* NB: Assumes that there's a temporary Pike_interpreter_struct * in Pike_interpreter_pointer, which we copy, and replace * with ourselves. */
91518c2009-03-15Martin Stjernholm  Pike_interpreter.thread_state = ts; ts->state = Pike_interpreter;
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = &ts->state;
91518c2009-03-15Martin Stjernholm  ts->id = th_self(); ts->status = THREAD_RUNNING; ts->swapped = 0;
7a52982015-09-09Henrik Grubbström (Grubba)  ts->interval_start = get_real_time();
91518c2009-03-15Martin Stjernholm #ifdef PIKE_DEBUG ts->debug_flags = 0;
15ecdc2011-04-02Martin Stjernholm  thread_swaps++;
91518c2009-03-15Martin Stjernholm #endif
7ee4aa2002-09-14Martin Stjernholm #ifdef USE_CLOCK_FOR_SLICES
da66442010-10-23Martin Stjernholm  /* Initialize thread_start_clock to zero instead of clock() since we * don't know for how long the thread already has run. */
91518c2009-03-15Martin Stjernholm  thread_start_clock = 0; last_clocked_thread = ts->id;
da66442010-10-23Martin Stjernholm #ifdef RDTSC prev_tsc = 0; #endif
d0aa0d2011-04-02Martin Stjernholm #ifdef PROFILE_CHECK_THREADS
fa77662016-02-11Martin Nilsson  WERR("[%d:%f] pike_init_thread_state: tsc reset\n", getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS));
d0aa0d2011-04-02Martin Stjernholm #endif
91518c2009-03-15Martin Stjernholm #endif } PMOD_EXPORT void pike_swap_out_thread (struct thread_state *ts COMMA_DLOC_DECL) {
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (2, "Swap out %sthread %p @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  ts == Pike_interpreter.thread_state ? "current " : "", ts, DLOC_ARGS_OPT);
91518c2009-03-15Martin Stjernholm  #ifdef PROFILING if (!ts->swapped) { cpu_time_t now = get_cpu_time();
ec41312016-02-11Martin Nilsson  W_PROFILING_DEBUG("%p: Swap out at: %" PRINT_CPU_TIME " unlocked: %" PRINT_CPU_TIME "\n", ts, now, ts->state.unlocked_time);
91518c2009-03-15Martin Stjernholm  ts->state.unlocked_time -= now; }
7ee4aa2002-09-14Martin Stjernholm #endif
07513e1996-10-04Fredrik Hübinette (Hubbe) 
91518c2009-03-15Martin Stjernholm  ts->swapped=1;
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = NULL;
91518c2009-03-15Martin Stjernholm } PMOD_EXPORT void pike_swap_in_thread (struct thread_state *ts COMMA_DLOC_DECL)
d86cd71998-08-24Marcus Comstedt {
a229952016-02-07Martin Nilsson  THREADS_FPRINTF (2, "Swap in thread %p @ %s:%d\n", ts, DLOC_ARGS_OPT);
d86cd71998-08-24Marcus Comstedt 
91518c2009-03-15Martin Stjernholm #ifdef PIKE_DEBUG
d97eb72011-07-10Henrik Grubbström (Grubba)  if (Pike_interpreter_pointer)
91518c2009-03-15Martin Stjernholm  pike_fatal_dloc ("Thread %"PRINTSIZET"x swapped in " "over existing thread %"PRINTSIZET"x.\n", (size_t) ts->id, (size_t) (Pike_interpreter.thread_state ? Pike_interpreter.thread_state->id : 0)); #endif #ifdef PROFILING if (ts->swapped) { cpu_time_t now = get_cpu_time();
ec41312016-02-11Martin Nilsson  W_PROFILING_DEBUG("%p: Swap in at: %" PRINT_CPU_TIME " unlocked: %" PRINT_CPU_TIME "\n", ts, now, ts->state.unlocked_time); /* Pike_interpreter_pointer is always null here... */
91518c2009-03-15Martin Stjernholm  ts->state.unlocked_time += now; } #endif ts->swapped=0;
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = &ts->state;
15ecdc2011-04-02Martin Stjernholm #ifdef PIKE_DEBUG thread_swaps++; #endif
91518c2009-03-15Martin Stjernholm  #ifdef USE_CLOCK_FOR_SLICES if (last_clocked_thread != ts->id) { thread_start_clock = clock(); last_clocked_thread = ts->id;
da66442010-10-23Martin Stjernholm #ifdef RDTSC RDTSC (prev_tsc);
a771ee2010-10-24Martin Stjernholm  prev_clock = thread_start_clock;
da66442010-10-23Martin Stjernholm #endif
91518c2009-03-15Martin Stjernholm  } #endif } PMOD_EXPORT void pike_swap_in_current_thread (struct thread_state *ts COMMA_DLOC_DECL) { #ifdef PIKE_DEBUG THREAD_T self = th_self(); if (!th_equal (ts->id, self)) pike_fatal_dloc ("Swapped in thread state %p into wrong thread " "%"PRINTSIZET"x - should be %"PRINTSIZET"x.\n", ts, th_self(), ts->id); #endif pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT); } #ifdef PIKE_DEBUG PMOD_EXPORT void pike_assert_thread_swapped_in (DLOC_DECL) { struct thread_state *ts=thread_state_for_id(th_self()); if(ts->swapped) pike_fatal_dloc ("Thread is not swapped in.\n"); if (ts->debug_flags & THREAD_DEBUG_LOOSE) pike_fatal_dloc ("Current thread is not bound to the interpreter. " "Nested use of ALLOW_THREADS()?\n"); } PMOD_EXPORT void pike_debug_check_thread (DLOC_DECL) { struct thread_state *ts=thread_state_for_id(th_self()); if (ts->debug_flags & THREAD_DEBUG_LOOSE) pike_fatal_dloc ("Current thread is not bound to the interpreter. " "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 */
7a52982015-09-09Henrik Grubbström (Grubba) /*! @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 */
91518c2009-03-15Martin Stjernholm PMOD_EXPORT void pike_threads_allow (struct thread_state *ts COMMA_DLOC_DECL) {
b2190b2017-04-27Henrik Grubbström (Grubba)  /* May get here after th_cleanup() when reporting leaks. */
91518c2009-03-15Martin Stjernholm  if (!ts) return;
7a52982015-09-09Henrik Grubbström (Grubba)  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(); } }
91518c2009-03-15Martin Stjernholm #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); ts->debug_flags |= THREAD_DEBUG_LOOSE; #endif if (num_threads > 1 && !threads_disabled) { pike_swap_out_thread (ts COMMA_DLOC_ARGS_OPT); pike_unlock_interpreter (DLOC_ARGS_OPT); }
9d9fa22016-02-12Martin Nilsson #if defined (PIKE_DEBUG)
91518c2009-03-15Martin Stjernholm  else { THREAD_T self = th_self(); if (threads_disabled && !th_equal(threads_disabled_thread, self)) 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) {
b2190b2017-04-27Henrik Grubbström (Grubba)  /* May get here if early init code throws errors. */
91518c2009-03-15Martin Stjernholm  if (!ts) return; if (ts->swapped) { pike_lock_interpreter (DLOC_ARGS_OPT); pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT); }
7a52982015-09-09Henrik Grubbström (Grubba)  if (UNLIKELY(thread_quanta)) { ts->interval_start = get_real_time(); }
91518c2009-03-15Martin Stjernholm #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
7a52982015-09-09Henrik Grubbström (Grubba)  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(); } }
91518c2009-03-15Martin Stjernholm #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); ts->debug_flags |= THREAD_DEBUG_LOOSE; #endif if (num_threads > 1 && !threads_disabled) { pike_swap_out_thread (ts COMMA_DLOC_ARGS_OPT); live_threads++;
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Increased live threads to %d\n", live_threads);
91518c2009-03-15Martin Stjernholm  pike_unlock_interpreter (DLOC_ARGS_OPT); }
9d9fa22016-02-12Martin Nilsson #if defined (PIKE_DEBUG)
91518c2009-03-15Martin Stjernholm  else { THREAD_T self = th_self(); if (threads_disabled && !th_equal(threads_disabled_thread, self)) 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_ext (struct thread_state *ts COMMA_DLOC_DECL) { #ifdef DO_PIKE_CLEANUP if (!ts) return; #endif if (ts->swapped) { pike_low_lock_interpreter (DLOC_ARGS_OPT); live_threads--;
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF (1, "Decreased live threads to %d\n", live_threads);
91518c2009-03-15Martin Stjernholm  co_broadcast (&live_threads_change); if (threads_disabled) threads_disabled_wait (DLOC_ARGS_OPT); pike_swap_in_thread (ts COMMA_DLOC_ARGS_OPT); }
7a52982015-09-09Henrik Grubbström (Grubba)  if (UNLIKELY(thread_quanta)) { ts->interval_start = get_real_time(); }
91518c2009-03-15Martin Stjernholm #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; /* If threads are disabled, we already hold the lock. */ if (threads_disabled) return;
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "Locking IMutex %p...\n", im);
91518c2009-03-15Martin Stjernholm  pike_threads_allow (ts COMMA_DLOC_ARGS_OPT); mt_lock(&((im)->lock)); pike_threads_disallow (ts COMMA_DLOC_ARGS_OPT);
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "Locked IMutex %p\n", im);
91518c2009-03-15Martin Stjernholm } 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;
9a8ddb2014-11-16Henrik Grubbström (Grubba)  USE_DLOC_ARGS();
a229952016-02-07Martin Nilsson  THREADS_FPRINTF(0, "Unlocking IMutex %p @ %s:%d\n",
7d06ba2016-02-07Martin Nilsson  im, DLOC_ARGS_OPT);
91518c2009-03-15Martin Stjernholm  mt_unlock(&(im->lock)); }
a91ca01998-07-10Henrik Grubbström (Grubba) 
335a552001-11-02Martin Stjernholm /* 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. */
f328f11998-07-17Henrik Grubbström (Grubba) void low_init_threads_disable(void)
a91ca01998-07-10Henrik Grubbström (Grubba) { /* Serious black magic to avoid dead-locks */ if (!threads_disabled) {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "low_init_threads_disable(): Locking IM's...\n");
a91ca01998-07-10Henrik Grubbström (Grubba) 
c20c4e2018-09-12Henrik Grubbström (Grubba)  lock_pike_compiler();
b867f92003-02-16Martin Stjernholm  if (Pike_interpreter.thread_state) {
2f748a1999-02-20Henrik Grubbström (Grubba)  /* Threads have been enabled. */
a91ca01998-07-10Henrik Grubbström (Grubba)  IMUTEX_T *im; THREADS_ALLOW(); /* Keep this the entire session. */ mt_lock(&interleave_lock); im = (IMUTEX_T *)interleave_list; while(im) { mt_lock(&(im->lock)); im = im->next; } THREADS_DISALLOW(); } else {
2f748a1999-02-20Henrik Grubbström (Grubba)  /* Threads haven't been enabled yet. */
a91ca01998-07-10Henrik Grubbström (Grubba)  IMUTEX_T *im; /* Keep this the entire session. */ mt_lock(&interleave_lock); im = (IMUTEX_T *)interleave_list; while(im) { mt_lock(&(im->lock)); im = im->next; } }
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "low_init_threads_disable(): Disabling threads.\n");
a91ca01998-07-10Henrik Grubbström (Grubba)  threads_disabled = 1;
fb7d862015-06-01Henrik Grubbström (Grubba)  threads_disabled_start = get_real_time();
95c8152001-11-01Martin Stjernholm #ifdef PIKE_DEBUG threads_disabled_thread = th_self(); #endif
a91ca01998-07-10Henrik Grubbström (Grubba)  } else { threads_disabled++; }
f328f11998-07-17Henrik Grubbström (Grubba)  THREADS_FPRINTF(0,
7d06ba2016-02-07Martin Nilsson  "low_init_threads_disable(): threads_disabled:%d\n", threads_disabled);
f328f11998-07-17Henrik Grubbström (Grubba) }
16df701998-07-16Fredrik Hübinette (Hubbe) 
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl object(_disable_threads) _disable_threads() *!
335a552001-11-02Martin Stjernholm  *! This function first posts a notice to all threads that it is time
95ed622016-03-23Henrik Grubbström (Grubba)  *! to halt. It then waits until all threads actually @b{have@} halted,
335a552001-11-02Martin Stjernholm  *! and then then returns a lock object. All other threads will be *! blocked from running until that object has been freed/destroyed. *! *! It's mainly useful to do things that require a temporary uid/gid
95ed622016-03-23Henrik Grubbström (Grubba)  *! change, since on many OSes the effective user and group apply to
335a552001-11-02Martin Stjernholm  *! all threads.
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! @note
335a552001-11-02Martin Stjernholm  *! 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.)
4b9aed2015-06-01Henrik Grubbström (Grubba)  *! *! @seealso *! @[gethrdtime()]
e413da2001-02-01Henrik Grubbström (Grubba)  */
74dfe82012-12-30Jonas Walldén void init_threads_disable(struct object *UNUSED(o))
f328f11998-07-17Henrik Grubbström (Grubba) {
d937872009-03-13Martin Stjernholm  low_init_threads_disable();
f328f11998-07-17Henrik Grubbström (Grubba)  if(live_threads) { SWAP_OUT_CURRENT_THREAD(); while (live_threads) {
91518c2009-03-15Martin Stjernholm  THREADS_FPRINTF(1,
7d06ba2016-02-07Martin Nilsson  "_disable_threads(): Waiting for %d threads to finish\n", live_threads);
91518c2009-03-15Martin Stjernholm  low_co_wait_interpreter (&live_threads_change);
16df701998-07-16Fredrik Hübinette (Hubbe)  }
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "_disable_threads(): threads now disabled\n");
f328f11998-07-17Henrik Grubbström (Grubba)  SWAP_IN_CURRENT_THREAD();
a91ca01998-07-10Henrik Grubbström (Grubba)  } }
74dfe82012-12-30Jonas Walldén void exit_threads_disable(struct object *UNUSED(o))
de413f1998-03-26Per Hedbor {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "exit_threads_disable(): threads_disabled:%d\n", threads_disabled);
40e9491998-07-05Henrik Grubbström (Grubba)  if(threads_disabled) { if(!--threads_disabled) {
a91ca01998-07-10Henrik Grubbström (Grubba)  IMUTEX_T *im = (IMUTEX_T *)interleave_list;
fb7d862015-06-01Henrik Grubbström (Grubba)  threads_disabled_acc_time += get_real_time() - threads_disabled_start;
a91ca01998-07-10Henrik Grubbström (Grubba)  /* Order shouldn't matter for unlock, so no need to do it backwards. */ while(im) {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "exit_threads_disable(): Unlocking IM %p\n", im);
a91ca01998-07-10Henrik Grubbström (Grubba)  mt_unlock(&(im->lock)); im = im->next; }
bc3a9a2018-02-09Henrik Grubbström (Grubba)  unlock_pike_compiler();
a91ca01998-07-10Henrik Grubbström (Grubba)  mt_unlock(&interleave_lock);
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "exit_threads_disable(): Wake up!\n");
2202fe1998-04-23Fredrik Hübinette (Hubbe)  co_broadcast(&threads_disabled_change);
95c8152001-11-01Martin Stjernholm #ifdef PIKE_DEBUG threads_disabled_thread = 0; #endif
40e9491998-07-05Henrik Grubbström (Grubba)  }
71f3a21998-11-22Fredrik Hübinette (Hubbe) #ifdef PIKE_DEBUG
40e9491998-07-05Henrik Grubbström (Grubba)  } else {
5aad932002-08-15Marcus Comstedt  Pike_fatal("exit_threads_disable() called too many times!\n");
71f3a21998-11-22Fredrik Hübinette (Hubbe) #endif /* PIKE_DEBUG */
40e9491998-07-05Henrik Grubbström (Grubba)  }
de413f1998-03-26Per Hedbor }
063fe31998-03-10Per Hedbor 
a91ca01998-07-10Henrik Grubbström (Grubba) void init_interleave_mutex(IMUTEX_T *im)
063fe31998-03-10Per Hedbor {
a91ca01998-07-10Henrik Grubbström (Grubba)  mt_init(&(im->lock));
6e1db51998-07-09Henrik Grubbström (Grubba) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "init_interleave_mutex(): init_threads_disable()\n");
6e1db51998-07-09Henrik Grubbström (Grubba) 
a91ca01998-07-10Henrik Grubbström (Grubba)  init_threads_disable(NULL);
6e1db51998-07-09Henrik Grubbström (Grubba) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "init_interleave_mutex(): Locking IM %p\n", im);
6e1db51998-07-09Henrik Grubbström (Grubba) 
a91ca01998-07-10Henrik Grubbström (Grubba)  /* Lock it so that it can be unlocked by exit_threads_disable() */ mt_lock(&(im->lock)); im->next = (IMUTEX_T *)interleave_list; if (interleave_list) { interleave_list->prev = im;
6e1db51998-07-09Henrik Grubbström (Grubba)  }
a91ca01998-07-10Henrik Grubbström (Grubba)  interleave_list = im; im->prev = NULL;
6e1db51998-07-09Henrik Grubbström (Grubba) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "init_interleave_mutex(): exit_threads_disable()\n");
a91ca01998-07-10Henrik Grubbström (Grubba)  exit_threads_disable(NULL); } void exit_interleave_mutex(IMUTEX_T *im) { init_threads_disable(NULL); if (im->prev) { im->prev->next = im->next; } else { interleave_list = im->next; } if (im->next) { im->next->prev = im->prev;
ef1e931998-03-26Henrik Grubbström (Grubba)  }
a91ca01998-07-10Henrik Grubbström (Grubba)  /* Just to be nice... */ mt_unlock(&(im->lock)); exit_threads_disable(NULL);
063fe31998-03-10Per Hedbor }
eac2091998-02-27Marcus Comstedt  /* Thread hashtable */
f2c01e2003-01-08Martin Stjernholm struct thread_state *thread_table_chains[THREAD_TABLE_SIZE]; int num_pike_threads=0;
eac2091998-02-27Marcus Comstedt 
22ca071998-04-08Fredrik Hübinette (Hubbe) void thread_table_init(void)
eac2091998-02-27Marcus Comstedt { INT32 x; for(x=0; x<THREAD_TABLE_SIZE; x++) thread_table_chains[x] = NULL; } unsigned INT32 thread_table_hash(THREAD_T *tid) {
d213271998-02-28Fredrik Hübinette (Hubbe)  return th_hash(*tid) % THREAD_TABLE_SIZE;
eac2091998-02-27Marcus Comstedt }
0431312003-02-15Henrik Grubbström (Grubba) PMOD_EXPORT void thread_table_insert(struct thread_state *s)
eac2091998-02-27Marcus Comstedt { unsigned INT32 h = thread_table_hash(&s->id);
71f3a21998-11-22Fredrik Hübinette (Hubbe) #ifdef PIKE_DEBUG
939b181998-07-17Fredrik Hübinette (Hubbe)  if(h>=THREAD_TABLE_SIZE)
5aad932002-08-15Marcus Comstedt  Pike_fatal("thread_table_hash failed miserably!\n");
26cd941999-05-07Fredrik Hübinette (Hubbe)  if(thread_state_for_id(s->id))
3be5501999-06-08Fredrik Hübinette (Hubbe)  { if(thread_state_for_id(s->id) == s)
5aad932002-08-15Marcus Comstedt  Pike_fatal("Registring thread twice!\n");
3be5501999-06-08Fredrik Hübinette (Hubbe)  else
5aad932002-08-15Marcus Comstedt  Pike_fatal("Forgot to unregister thread!\n");
3be5501999-06-08Fredrik Hübinette (Hubbe)  }
939b181998-07-17Fredrik Hübinette (Hubbe) #endif
eac2091998-02-27Marcus Comstedt  mt_lock( & thread_table_lock );
56ac102000-03-29Fredrik Hübinette (Hubbe)  num_pike_threads++;
eac2091998-02-27Marcus Comstedt  if((s->hashlink = thread_table_chains[h]) != NULL) s->hashlink->backlink = &s->hashlink; thread_table_chains[h] = s; s->backlink = &thread_table_chains[h];
13670c2015-05-25Martin Nilsson  mt_unlock( & thread_table_lock );
eac2091998-02-27Marcus Comstedt }
0431312003-02-15Henrik Grubbström (Grubba) PMOD_EXPORT void thread_table_delete(struct thread_state *s)
eac2091998-02-27Marcus Comstedt { mt_lock( & thread_table_lock );
56ac102000-03-29Fredrik Hübinette (Hubbe)  num_pike_threads--;
eac2091998-02-27Marcus Comstedt  if(s->hashlink != NULL) s->hashlink->backlink = s->backlink; *(s->backlink) = s->hashlink; mt_unlock( & thread_table_lock ); }
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT struct thread_state *thread_state_for_id(THREAD_T tid)
eac2091998-02-27Marcus Comstedt { unsigned INT32 h = thread_table_hash(&tid);
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *s = NULL;
71f3a21998-11-22Fredrik Hübinette (Hubbe) #ifdef PIKE_DEBUG
939b181998-07-17Fredrik Hübinette (Hubbe)  if(h>=THREAD_TABLE_SIZE)
5aad932002-08-15Marcus Comstedt  Pike_fatal("thread_table_hash failed miserably!\n");
939b181998-07-17Fredrik Hübinette (Hubbe) #endif
eac2091998-02-27Marcus Comstedt  mt_lock( & thread_table_lock );
d213271998-02-28Fredrik Hübinette (Hubbe)  if(thread_table_chains[h] == NULL) {
eac2091998-02-27Marcus Comstedt  /* NULL result */
d213271998-02-28Fredrik Hübinette (Hubbe)  } else if(th_equal((s=thread_table_chains[h])->id, tid)) {
eac2091998-02-27Marcus Comstedt  /* Quick return */
d213271998-02-28Fredrik Hübinette (Hubbe)  } else {
eac2091998-02-27Marcus Comstedt  while((s = s->hashlink) != NULL)
d213271998-02-28Fredrik Hübinette (Hubbe)  if(th_equal(s->id, tid))
eac2091998-02-27Marcus Comstedt  break; if(s != NULL) {
ec2bab2000-06-24Fredrik Hübinette (Hubbe)  /* Move the Pike_interpreter to the head of the chain, in case
eac2091998-02-27Marcus Comstedt  we want to search for it again */ /* Unlink */ if(s->hashlink != NULL) s->hashlink->backlink = s->backlink; *(s->backlink) = s->hashlink; /* And relink at the head of the chain */ if((s->hashlink = thread_table_chains[h]) != NULL) s->hashlink->backlink = &s->hashlink; thread_table_chains[h] = s; s->backlink = &thread_table_chains[h]; } } mt_unlock( & thread_table_lock ); return s; /* NOTEZ BIEN: Return value only guaranteed to remain valid as long as you have the interpreter lock, unless tid == th_self() */ }
048d232001-02-27Martin Stjernholm struct thread_state *gdb_thread_state_for_id(THREAD_T tid) /* Should only be used from a debugger session. */ { unsigned INT32 h = thread_table_hash(&tid); struct thread_state *s; for (s = thread_table_chains[h]; s != NULL; s = s->hashlink) if(th_equal(s->id, tid)) break; return s; } INT32 gdb_next_thread_state(INT32 prev, struct thread_state **ts) /* Used by gdb_backtraces. */ { if (!*ts || !(*ts)->hashlink) { if (!*ts) prev = -1; while (++prev < THREAD_TABLE_SIZE) if ((*ts = thread_table_chains[prev])) return prev; *ts = NULL; return 0; } *ts = (*ts)->hashlink; return prev; }
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT struct object *thread_for_id(THREAD_T tid)
eac2091998-02-27Marcus Comstedt {
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *s = thread_state_for_id(tid);
26cd941999-05-07Fredrik Hübinette (Hubbe)  return (s == NULL? NULL : THREADSTATE2OBJ(s));
eac2091998-02-27Marcus Comstedt  /* See NB in thread_state_for_id. Lifespan of result can be prolonged by incrementing refcount though. */ }
ff26422018-08-07Tobias S. Josefowitz 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); }
2b42cc2007-10-06Marcus Comstedt 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... */
dc48072008-12-27Henrik Grubbström (Grubba) #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
ff26422018-08-07Tobias S. Josefowitz  CALL_WITH_ERROR_HANDLING(state, func, ctx);
dc48072008-12-27Henrik Grubbström (Grubba)  #ifdef PIKE_DEBUG /* Restore the looseness property of the thread. */ state->debug_flags |= is_loose; #endif
2b42cc2007-10-06Marcus Comstedt  } else { /* Nope, let's get it... */ mt_lock_interpreter(); SWAP_IN_THREAD(state);
dc48072008-12-27Henrik Grubbström (Grubba)  DO_IF_DEBUG(state->debug_flags &= ~THREAD_DEBUG_LOOSE;)
2b42cc2007-10-06Marcus Comstedt 
ff26422018-08-07Tobias S. Josefowitz  CALL_WITH_ERROR_HANDLING(state, func, ctx);
2b42cc2007-10-06Marcus Comstedt  /* Restore */
dc48072008-12-27Henrik Grubbström (Grubba)  DO_IF_DEBUG(state->debug_flags |= THREAD_DEBUG_LOOSE;)
2b42cc2007-10-06Marcus Comstedt  SWAP_OUT_THREAD(state); mt_unlock_interpreter(); } } else { /* Not a pike thread. Create a temporary thread_id... */ struct object *thread_obj;
d97eb72011-07-10Henrik Grubbström (Grubba)  struct Pike_interpreter_struct new_interpreter;
2b42cc2007-10-06Marcus Comstedt  mt_lock_interpreter();
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = &new_interpreter;
2b42cc2007-10-06Marcus Comstedt  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);
ff26422018-08-07Tobias S. Josefowitz  CALL_WITH_ERROR_HANDLING(Pike_interpreter.thread_state, func, ctx);
2b42cc2007-10-06Marcus Comstedt  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--;
cf13df2013-06-16Arne Goedeke #ifdef PIKE_DEBUG Pike_interpreter_pointer = NULL; #endif
8b91fc2018-08-08Henrik Grubbström (Grubba)  mt_unlock_interpreter();
2b42cc2007-10-06Marcus Comstedt  } } PMOD_EXPORT void enable_external_threads(void) {
13670c2015-05-25Martin Nilsson  num_threads++;
2b42cc2007-10-06Marcus Comstedt } PMOD_EXPORT void disable_external_threads(void) { num_threads--; }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @module Thread */
95363a2000-04-11Fredrik Hübinette (Hubbe) 
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl array(Thread.Thread) all_threads() *! *! This function returns an array with the thread ids of all threads. *! *! @seealso
c7b7dd2001-10-28Martin Nilsson  *! @[Thread()]
e413da2001-02-01Henrik Grubbström (Grubba)  */
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT void f_all_threads(INT32 args)
eac2091998-02-27Marcus Comstedt { /* Return an unordered array containing all threads that was running at the time this function was invoked */ struct svalue *oldsp;
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *s;
eac2091998-02-27Marcus Comstedt  pop_n_elems(args);
17f08c2000-07-06Fredrik Hübinette (Hubbe)  oldsp = Pike_sp;
f2c01e2003-01-08Martin Stjernholm  FOR_EACH_THREAD (s, {
26cd941999-05-07Fredrik Hübinette (Hubbe)  struct object *o = THREADSTATE2OBJ(s);
88f3202002-06-17Henrik Grubbström (Grubba)  if (o) { ref_push_object(o); }
f2c01e2003-01-08Martin Stjernholm  });
bd67392015-10-14Martin Nilsson  f_aggregate(Pike_sp - oldsp);
eac2091998-02-27Marcus Comstedt }
38e1e82001-11-08Fredrik Hübinette (Hubbe) #ifdef PIKE_DEBUG
00e6682006-07-05Martin Stjernholm PMOD_EXPORT void debug_list_all_threads(void)
38e1e82001-11-08Fredrik Hübinette (Hubbe) { INT32 x; struct thread_state *s; THREAD_T self = th_self();
fa77662016-02-11Martin Nilsson  WERR("--Listing all threads--\n"); WERR("Current thread: %"PRINTSIZET"x\n", (size_t) self); WERR("Current interpreter thread state: %p%s\n", Pike_interpreter.thread_state, Pike_interpreter.thread_state == (struct thread_state *) (ptrdiff_t) -1 ? " (swapped)" : ""); WERR("Current thread state according to thread_state_for_id(): %p\n", thread_state_for_id (self)); WERR("Current thread obj: %p\n", (Pike_interpreter.thread_state && Pike_interpreter.thread_state != (struct thread_state *) (ptrdiff_t) -1) ? Pike_interpreter.thread_state->thread_obj : NULL); WERR("Current thread hash: %d\n",thread_table_hash(&self)); WERR("Current stack pointer: %p\n",&self);
38e1e82001-11-08Fredrik Hübinette (Hubbe)  for(x=0; x<THREAD_TABLE_SIZE; x++) { for(s=thread_table_chains[x]; s; s=s->hashlink) { struct object *o = THREADSTATE2OBJ(s);
fa77662016-02-11Martin Nilsson  WERR("ThTab[%d]: state=%p, obj=%p, " "swapped=%d, sp=%p (%+"PRINTPTRDIFFT"d), fp=%p, stackbase=%p, " "id=%"PRINTSIZET"x\n", x, s, o, s->swapped, s->state.stack_pointer, s->state.stack_pointer - s->state.evaluator_stack, s->state.frame_pointer, s->state.stack_top, (size_t) s->id);
38e1e82001-11-08Fredrik Hübinette (Hubbe)  } }
fa77662016-02-11Martin Nilsson  WERR("-----------------------\n");
38e1e82001-11-08Fredrik Hübinette (Hubbe) } #endif
95363a2000-04-11Fredrik Hübinette (Hubbe) 
1f21332000-07-28Fredrik Hübinette (Hubbe) PMOD_EXPORT int count_pike_threads(void)
56ac102000-03-29Fredrik Hübinette (Hubbe) { return num_pike_threads; }
eac2091998-02-27Marcus Comstedt 
2ea31a2016-02-22Henrik Grubbström (Grubba) #ifdef HAVE_GETHRTIME /* Workaround for old Solaris, which believes that eg GCC * doesn't support long long, which in turn causes hrtime_t * to become a non-scalar type. */ union pike_hrtime { INT64 val; hrtime_t hrt; }; #endif
74dfe82012-12-30Jonas Walldén static void check_threads(struct callback *UNUSED(cb), void *UNUSED(arg), void *UNUSED(arg2))
a29e021996-10-15Fredrik Hübinette (Hubbe) {
0c22042008-11-18Martin Stjernholm #ifdef PROFILE_CHECK_THREADS
d0aa0d2011-04-02Martin Stjernholm  static unsigned long calls = 0, yields = 0; static unsigned long clock_checks = 0, no_clock_advs = 0;
a771ee2010-10-24Martin Stjernholm  static unsigned long tps = 0, tps_int_n = 0; /* TSC intervals per slice. */ static double tps_int_mean = 0.0, tps_int_m2 = 0.0;
0c22042008-11-18Martin Stjernholm  calls++; #endif
15ecdc2011-04-02Martin Stjernholm #ifdef PIKE_DEBUG check_threads_calls++; #endif
0c22042008-11-18Martin Stjernholm 
59e8272010-10-17Martin Stjernholm #if defined (USE_CLOCK_FOR_SLICES) && defined (PIKE_DEBUG) if (last_clocked_thread != th_self()) Pike_fatal ("Stale thread %08lx in last_clocked_thread (self is %08lx)\n", (unsigned long) last_clocked_thread, (unsigned long) th_self()); #endif
9d21162008-11-18Martin Stjernholm 
92ec352010-10-23Martin Stjernholm #if defined(RDTSC) && defined(USE_CLOCK_FOR_SLICES)
2cf5272010-10-17Artur Skawina  /* We can get here as often as 30+ thousand times per second; let's try to avoid doing as many clock(3)/times(2) syscalls
f756242010-10-24Martin Stjernholm  by using the TSC. */
2cf5272010-10-17Artur Skawina 
59e8272010-10-17Martin Stjernholm  if (use_tsc_for_slices) {
a771ee2010-10-24Martin Stjernholm  static INT64 target_int = TSC_START_INTERVAL; INT64 tsc_now, tsc_elapsed;
2cf5272010-10-17Artur Skawina 
da66442010-10-23Martin Stjernholm  /* prev_tsc is normally always valid here, but it can be zero * after a tsc jump reset and just after a thread has been * "pike-ified" with INIT_THREAD_STATE (in which case * thread_start_clock is zero too). */
2cf5272010-10-17Artur Skawina 
a771ee2010-10-24Martin Stjernholm  RDTSC(tsc_now); tsc_elapsed = tsc_now - prev_tsc;
da66442010-10-23Martin Stjernholm 
a771ee2010-10-24Martin Stjernholm  if (tsc_elapsed < target_int) {
da66442010-10-23Martin Stjernholm  if (tsc_elapsed < 0) { /* The counter jumped back in time, so reset and continue. In * the worst case this keeps happening all the time, and then * the only effect is that we always fall back to * clock(3). */
e9bda92010-10-22Martin Stjernholm #ifdef PROFILE_CHECK_THREADS
fa77662016-02-11Martin Nilsson  WERR("[%d:%f] TSC backward jump detected " "(now: %"PRINTINT64"d, prev: %"PRINTINT64"d, " "target_int: %"PRINTINT64"d) - resetting\n", getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS), tsc_now, prev_tsc, target_int);
e9bda92010-10-22Martin Stjernholm #endif
a771ee2010-10-24Martin Stjernholm  target_int = TSC_START_INTERVAL;
da66442010-10-23Martin Stjernholm  prev_tsc = 0;
e9bda92010-10-22Martin Stjernholm  }
59e8272010-10-17Martin Stjernholm  else return;
2cf5272010-10-17Artur Skawina  }
466caa2010-10-23Martin Stjernholm #ifdef PROFILE_CHECK_THREADS clock_checks++;
a771ee2010-10-24Martin Stjernholm  tps++; #endif { clock_t clock_now = clock(); /* Aim tsc intervals at 1/20th of a thread time slice interval, * i.e. at 1/400 sec. That means the time is checked with * clock(3) approx 20 times per slice. That still cuts the vast * majority of the clock() calls and we're still fairly safe * against tsc inconsistencies of different sorts, like cpu clock * rate changes (on older cpu's with variable tsc), * 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
e03e2c2011-04-02Martin Stjernholm  * 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. */
a771ee2010-10-24Martin Stjernholm  if (prev_tsc) {
c0402d2012-07-23Jonas Walldén  if (clock_now > prev_clock) {
a771ee2010-10-24Martin Stjernholm  /* 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
e03e2c2011-04-02Martin Stjernholm  * ~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. */
c0402d2012-07-23Jonas Walldén  clock_t tsc_interval_time = clock_now - prev_clock;
a771ee2010-10-24Martin Stjernholm  INT64 new_target_int = (tsc_elapsed * (CLOCKS_PER_SEC / 400)) / tsc_interval_time;
6343382011-04-02Martin Stjernholm  if (new_target_int < target_int << 2)
a771ee2010-10-24Martin Stjernholm  target_int = new_target_int; else {
e03e2c2011-04-02Martin Stjernholm  /* The most likely cause for this is high variance in the * interval lengths due to low clock(3) resolution. */
a771ee2010-10-24Martin Stjernholm #ifdef PROFILE_CHECK_THREADS
fa77662016-02-11Martin Nilsson  WERR("[%d:%f] Capping large TSC interval increase " "(from %"PRINTINT64"d to %"PRINTINT64"d)\n", getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS), target_int, new_target_int);
a771ee2010-10-24Martin Stjernholm #endif /* The + 1 is paranoia just in case it has become zero somehow. */
6343382011-04-02Martin Stjernholm  target_int = (target_int << 2) + 1;
a771ee2010-10-24Martin Stjernholm  } prev_tsc = tsc_now; prev_clock = clock_now;
1d16142016-02-11Martin Nilsson  }
a771ee2010-10-24Martin Stjernholm  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. */
c0402d2012-07-23Jonas Walldén  if (clock_now < prev_clock) {
a771ee2010-10-24Martin Stjernholm  /* 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++;
466caa2010-10-23Martin Stjernholm #endif
a771ee2010-10-24Martin Stjernholm  }
64c5662010-10-23Martin Stjernholm  }
a771ee2010-10-24Martin Stjernholm  else { #ifdef PROFILE_CHECK_THREADS
fa77662016-02-11Martin Nilsson  WERR("[%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);
a771ee2010-10-24Martin Stjernholm #endif prev_tsc = tsc_now;
64c5662010-10-23Martin Stjernholm  }
a771ee2010-10-24Martin Stjernholm 
c0402d2012-07-23Jonas Walldén  if (clock_now < thread_start_clock)
a771ee2010-10-24Martin Stjernholm  /* 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;
2cf5272010-10-17Artur Skawina  }
64c5662010-10-23Martin Stjernholm 
a771ee2010-10-24Martin Stjernholm #ifdef PROFILE_CHECK_THREADS { double delta = tps - tps_int_mean; tps_int_n++; tps_int_mean += delta / tps_int_n; tps_int_m2 += delta * (tps - tps_int_mean); tps = 0; } #endif
64c5662010-10-23Martin Stjernholm 
59e8272010-10-17Martin Stjernholm  goto do_yield;
2cf5272010-10-17Artur Skawina  }
92ec352010-10-23Martin Stjernholm #endif /* RDTSC && USE_CLOCK_FOR_SLICES */
59e8272010-10-17Martin Stjernholm  #ifdef HAVE_GETHRTIME
7ee4aa2002-09-14Martin Stjernholm  {
59d46a2016-02-22Henrik Grubbström (Grubba)  static union pike_hrtime last_;
2ea31a2016-02-22Henrik Grubbström (Grubba)  union pike_hrtime now; now.hrt = gethrtime(); if( now.val-last_.val < 50000000 ) /* 0.05s slice */
7ee4aa2002-09-14Martin Stjernholm  return;
2ea31a2016-02-22Henrik Grubbström (Grubba)  last_.val = now.val;
7ee4aa2002-09-14Martin Stjernholm  }
82069a2003-11-26Henrik Grubbström (Grubba) #elif defined(HAVE_MACH_TASK_INFO_H) && defined(TASK_THREAD_TIMES_INFO)
08c53c2003-11-25Jonas Wallden  { 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;
13670c2015-05-25Martin Nilsson 
bc2a032009-04-21Jonas Wallden  /* 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. */
5767532009-04-21Jonas Wallden  struct timeval tv;
c679ec2012-01-06Jonas Walldén  ACCURATE_GETTIMEOFDAY(&tv); {
5767532009-04-21Jonas Wallden  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; }
13670c2015-05-25Martin Nilsson 
9d21162008-11-18Martin Stjernholm  /* Get user time and test if 50 ms has passed since last check. */
08c53c2003-11-25Jonas Wallden  if (task_info(mach_task_self(), TASK_THREAD_TIMES_INFO, (task_info_t) &info, &info_size) == 0) {
9d21162008-11-18Martin Stjernholm  static INT64 last_check = 0; INT64 now = info.user_time.seconds * 1000000 + info.user_time.microseconds + info.system_time.seconds * 1000000 + info.system_time.microseconds; if (now - last_check < 50000) return; last_check = now;
08c53c2003-11-25Jonas Wallden  } }
7ee4aa2002-09-14Martin Stjernholm #elif defined (USE_CLOCK_FOR_SLICES)
b07fde2010-10-24Martin Stjernholm  { clock_t clock_now = clock();
c0402d2012-07-23Jonas Walldén  if (clock_now < thread_start_clock)
b07fde2010-10-24Martin Stjernholm  /* 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; }
0c22042008-11-18Martin Stjernholm #else static int div_; if(div_++ & 255) return;
319fb12002-09-14Martin Stjernholm #endif
0a861b1997-09-17Fredrik Hübinette (Hubbe) 
59e8272010-10-17Martin Stjernholm  do_yield:;
15ecdc2011-04-02Martin Stjernholm #ifdef PIKE_DEBUG check_threads_yields++; #endif
59e8272010-10-17Martin Stjernholm 
0c22042008-11-18Martin Stjernholm #ifdef PROFILE_CHECK_THREADS { static long last_time; struct timeval now;
466caa2010-10-23Martin Stjernholm 
d0aa0d2011-04-02Martin Stjernholm  yields++;
f010202011-11-16Tobias S. Josefowitz  ACCURATE_GETTIMEOFDAY (&now);
0c22042008-11-18Martin Stjernholm  if (now.tv_sec > last_time) {
fa77662016-02-11Martin Nilsson  WERR("[%d:%f] check_threads: %lu calls, " "%lu clocks, %lu no advs, %lu yields" ", tps %g:%.1e\n", getpid(), get_real_time() * (1.0 / CPU_TIME_TICKS), calls, clock_checks, no_clock_advs, yields, tps_int_mean, tps_int_n > 1 ? sqrt (tps_int_m2 / (tps_int_n - 1)) : 0.0);
0c22042008-11-18Martin Stjernholm  last_time = (unsigned long) now.tv_sec;
d0aa0d2011-04-02Martin Stjernholm  calls = yields = clock_checks = no_clock_advs = 0;
0c22042008-11-18Martin Stjernholm  } } #endif
15ecdc2011-04-02Martin Stjernholm  { #ifdef PIKE_DEBUG
d559142017-07-17Martin Nilsson  UINT64 old_thread_swaps = thread_swaps;
15ecdc2011-04-02Martin Stjernholm #endif pike_thread_yield(); #ifdef PIKE_DEBUG if (thread_swaps != old_thread_swaps) check_threads_swaps++; #endif }
5d63712010-09-28Martin Stjernholm }
a76b312010-09-28Henrik Grubbström (Grubba) PMOD_EXPORT void pike_thread_yield(void)
5d63712010-09-28Martin Stjernholm {
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
26cd941999-05-07Fredrik Hübinette (Hubbe) 
de413f1998-03-26Per Hedbor  THREADS_ALLOW(); /* Allow other threads to run */
307b932009-03-13Martin Stjernholm  /* FIXME: Ought to use condition vars or something to get another * thread to run. yield functions are notoriously unreliable and * poorly defined. It might not really yield we need it to. It might * make us yield to another process instead of just another thread. * It might even make us sleep for a short while. */
335a552001-11-02Martin Stjernholm  th_yield();
de413f1998-03-26Per Hedbor  THREADS_DISALLOW();
26cd941999-05-07Fredrik Hübinette (Hubbe) 
7ee4aa2002-09-14Martin Stjernholm #ifdef USE_CLOCK_FOR_SLICES
41a0962009-03-13Martin Stjernholm  /* If we didn't yield then give ourselves a new time slice. If we * did yield then thread_start_clock is the current clock anyway * after the thread swap in. */
7ee4aa2002-09-14Martin Stjernholm  thread_start_clock = clock();
da66442010-10-23Martin Stjernholm #ifdef RDTSC RDTSC (prev_tsc);
a771ee2010-10-24Martin Stjernholm  prev_clock = thread_start_clock;
da66442010-10-23Martin Stjernholm #endif
41a0962009-03-13Martin Stjernholm #ifdef PIKE_DEBUG if (last_clocked_thread != th_self()) Pike_fatal ("Stale thread %08lx in last_clocked_thread (self is %08lx)\n", (unsigned long) last_clocked_thread, (unsigned long) th_self()); #endif
7ee4aa2002-09-14Martin Stjernholm #endif
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
a29e021996-10-15Fredrik Hübinette (Hubbe) }
1d456f2003-02-20Henrik Grubbström (Grubba) struct thread_starter { struct thread_state *thread_state; /* State of the thread. */ struct array *args; /* Arguments passed to the thread. */ #ifdef HAVE_BROKEN_LINUX_THREAD_EUID int euid, egid; #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ }; /* Thread func starting new Pike-level threads. */
0431312003-02-15Henrik Grubbström (Grubba) TH_RETURN_TYPE new_thread_func(void *data)
07513e1996-10-04Fredrik Hübinette (Hubbe) {
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  struct thread_starter arg = *(struct thread_starter *)data;
1d456f2003-02-20Henrik Grubbström (Grubba)  struct object *thread_obj; struct thread_state *thread_state;
07513e1996-10-04Fredrik Hübinette (Hubbe)  JMP_BUF back;
b1b51f1996-10-11Fredrik Hübinette (Hubbe) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "new_thread_func(): Thread %p created...\n", arg.thread_state);
c17fed2000-05-20Henrik Grubbström (Grubba) 
222d922000-05-20Henrik Grubbström (Grubba) #ifdef HAVE_BROKEN_LINUX_THREAD_EUID
c17fed2000-05-20Henrik Grubbström (Grubba)  /* Work-around for Linux's pthreads not propagating the * effective uid & gid. */ if (!geteuid()) {
f663a02003-03-05Martin Stjernholm #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);
a1ee482003-10-06Martin Stjernholm #ifdef PIKE_DEBUG if (current == -1)
fa77662016-02-11Martin Nilsson  WERR("%s:%d: Unexpected error from prctl(2). errno=%d\n", __FILE__, __LINE__, errno);
a1ee482003-10-06Martin Stjernholm #endif
f663a02003-03-05Martin Stjernholm #endif
b096df2014-03-13Per Hedbor #ifdef HAVE_BROKEN_LINUX_THREAD_EUID if( setegid(arg.egid) != 0 || seteuid(arg.euid) != 0 ) {
fa77662016-02-11Martin Nilsson  WERR("%s:%d: Unexpected error from setegid(2). errno=%d\n", __FILE__, __LINE__, errno);
b096df2014-03-13Per Hedbor  } #endif
f663a02003-03-05Martin Stjernholm #if defined(HAVE_PRCTL) && defined(PR_SET_DUMPABLE)
a1ee482003-10-06Martin Stjernholm  if (current != -1 && prctl(PR_SET_DUMPABLE, current) == -1) {
35e1152016-02-07Martin Nilsson  DWERR("%s:%d: Unexpected error from prctl(2). errno=%d\n", __FILE__, __LINE__, errno);
a1ee482003-10-06Martin Stjernholm  }
f663a02003-03-05Martin Stjernholm #endif
c17fed2000-05-20Henrik Grubbström (Grubba)  }
222d922000-05-20Henrik Grubbström (Grubba) #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */
0d51b32008-09-09Martin Stjernholm  /* Lock the interpreter now, but don't wait on * threads_disabled_change since the spawning thread might be * holding it. */ low_mt_lock_interpreter();
b867f92003-02-16Martin Stjernholm 
fcca612016-02-11Martin Nilsson #ifdef PIKE_DEBUG
b867f92003-02-16Martin Stjernholm  if(d_flag) { THREAD_T self = th_self(); if( !th_equal(arg.thread_state->id, self) ) Pike_fatal("Current thread is wrong. %lx %lx\n", (long)arg.thread_state->id, (long)self); }
fcca612016-02-11Martin Nilsson #endif /* PIKE_DEBUG */
b867f92003-02-16Martin Stjernholm  arg.thread_state->swapped = 0;
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = &arg.thread_state->state;
b867f92003-02-16Martin Stjernholm 
0612442001-05-16Fredrik Hübinette (Hubbe) #ifdef PROFILING Pike_interpreter.stack_bottom=((char *)&data); #endif
17f08c2000-07-06Fredrik Hübinette (Hubbe)  Pike_interpreter.stack_top=((char *)&data)+ (thread_stack_size-16384) * STACK_DIRECTION; Pike_interpreter.recoveries = NULL;
0431312003-02-15Henrik Grubbström (Grubba) 
1d456f2003-02-20Henrik Grubbström (Grubba)  add_ref(thread_obj = arg.thread_state->thread_obj); INIT_THREAD_STATE(thread_state = arg.thread_state); /* Inform the spawning thread that we are now running. */ co_broadcast(&thread_state->status_change);
f0cd4c1999-03-21Henrik Grubbström (Grubba) 
0d51b32008-09-09Martin Stjernholm  /* After signalling the status change to the spawning thread we may * now wait if threads are disabled. */ if (threads_disabled) { SWAP_OUT_CURRENT_THREAD();
91518c2009-03-15Martin Stjernholm  threads_disabled_wait (DLOC);
0d51b32008-09-09Martin Stjernholm  SWAP_IN_CURRENT_THREAD(); }
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
26cd941999-05-07Fredrik Hübinette (Hubbe) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "new_thread_func(): Thread %p inited\n", arg.thread_state);
38e1e82001-11-08Fredrik Hübinette (Hubbe) 
07513e1996-10-04Fredrik Hübinette (Hubbe)  if(SETJMP(back)) {
d273ca2014-12-07Henrik Grubbström (Grubba)  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); }
6697042000-11-20Martin Stjernholm  call_handle_error();
d273ca2014-12-07Henrik Grubbström (Grubba)  } if(throw_severity == THROW_EXIT)
14ff492009-09-29Martin Stjernholm  { /* 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); }
d273ca2014-12-07Henrik Grubbström (Grubba)  thread_state->status = THREAD_ABORTED;
07513e1996-10-04Fredrik Hübinette (Hubbe)  } else {
725ba91996-10-12Fredrik Hübinette (Hubbe)  INT32 args=arg.args->size;
61e9a01998-01-25Fredrik Hübinette (Hubbe)  back.severity=THROW_EXIT;
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  push_array_items(arg.args); arg.args=0;
97ffe41997-01-26Per Hedbor  f_call_function(args);
5740881998-01-01Fredrik Hübinette (Hubbe) 
a93a162008-12-19Martin Stjernholm  /* 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);
5740881998-01-01Fredrik Hübinette (Hubbe)  pop_stack();
d273ca2014-12-07Henrik Grubbström (Grubba)  thread_state->status = THREAD_EXITED;
07513e1996-10-04Fredrik Hübinette (Hubbe)  }
1d456f2003-02-20Henrik Grubbström (Grubba)  UNSETJMP(back);
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
38e1e82001-11-08Fredrik Hübinette (Hubbe) 
621e752015-10-19Per Hedbor  if(thread_state->thread_locals != NULL) { free_mapping(thread_state->thread_locals); thread_state->thread_locals = NULL;
d86cd71998-08-24Marcus Comstedt  }
1d456f2003-02-20Henrik Grubbström (Grubba)  co_broadcast(&thread_state->status_change);
07513e1996-10-04Fredrik Hübinette (Hubbe) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "new_thread_func(): Thread %p done\n", arg.thread_state);
b1b51f1996-10-11Fredrik Hübinette (Hubbe) 
1d456f2003-02-20Henrik Grubbström (Grubba)  /* This thread is now officially dead. */
2ef3b92005-01-25Henrik Grubbström (Grubba)  while(Pike_fp) POP_PIKE_FRAME(); reset_evaluator(); low_cleanup_interpret(&thread_state->state);
1d456f2003-02-20Henrik Grubbström (Grubba) 
0a14c22008-08-05Martin Stjernholm  if (!thread_state->thread_obj) /* Do this only if exit_thread_obj already has run. */ cleanup_thread_state (thread_state);
1d456f2003-02-20Henrik Grubbström (Grubba)  thread_table_delete(thread_state); EXIT_THREAD_STATE(thread_state); Pike_interpreter.thread_state = thread_state = NULL;
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = NULL;
1d456f2003-02-20Henrik Grubbström (Grubba)  /* Free ourselves. * NB: This really ought to run in some other thread... */
f1ee642003-03-17Henrik Grubbström (Grubba)  free_object(thread_obj);
1d456f2003-02-20Henrik Grubbström (Grubba)  thread_obj = NULL;
07513e1996-10-04Fredrik Hübinette (Hubbe)  num_threads--;
fe76ce1997-09-08Fredrik Hübinette (Hubbe)  if(!num_threads && threads_evaluator_callback)
a29e021996-10-15Fredrik Hübinette (Hubbe)  { remove_callback(threads_evaluator_callback); threads_evaluator_callback=0; }
0f65e12002-09-14Martin Stjernholm  #ifdef INTERNAL_PROFILING fprintf (stderr, "Thread usage summary:\n"); debug_print_rusage (stderr); #endif
4bc7e42001-11-26Henrik Grubbström (Grubba)  /* FIXME: What about threads_disable? */
c91f892000-04-19Martin Stjernholm  mt_unlock_interpreter();
07513e1996-10-04Fredrik Hübinette (Hubbe)  th_exit(0);
9282fd2015-09-27Martin Nilsson  UNREACHABLE(return 0);
07513e1996-10-04Fredrik Hübinette (Hubbe) }
97ffe41997-01-26Per Hedbor #ifdef UNIX_THREADS int num_lwps = 1; #endif
b1b51f1996-10-11Fredrik Hübinette (Hubbe) 
e413da2001-02-01Henrik Grubbström (Grubba) /*! @class Thread */
761bb02015-08-20Henrik Grubbström (Grubba) /*! @decl void create(function(mixed...:mixed|void) f, mixed ... args)
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! 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
c7b7dd2001-10-28Martin Nilsson  *! @[this_thread()] for the new thread.
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! @note *! This function is only available on systems with POSIX or UNIX or WIN32 *! threads support. *! *! @seealso *! @[Mutex], @[Condition], @[this_thread()] */
07513e1996-10-04Fredrik Hübinette (Hubbe) void f_thread_create(INT32 args) {
1d456f2003-02-20Henrik Grubbström (Grubba)  struct thread_starter arg;
0431312003-02-15Henrik Grubbström (Grubba)  struct thread_state *thread_state = (struct thread_state *)Pike_fp->current_storage;
07513e1996-10-04Fredrik Hübinette (Hubbe)  int tmp;
0431312003-02-15Henrik Grubbström (Grubba) 
5cd3122015-12-08Henrik Grubbström (Grubba)  if (args < 1) {
06bd612016-01-26Martin Nilsson  SIMPLE_WRONG_NUM_ARGS_ERROR("create", 1);
5cd3122015-12-08Henrik Grubbström (Grubba)  }
a729352015-12-13Henrik Grubbström (Grubba)  if (!callablep(Pike_sp - args)) {
f982742016-01-26Martin Nilsson  SIMPLE_ARG_TYPE_ERROR("create", 1, "function");
5cd3122015-12-08Henrik Grubbström (Grubba)  }
0431312003-02-15Henrik Grubbström (Grubba)  if (thread_state->status != THREAD_NOT_STARTED) {
6818b02003-02-20Henrik Grubbström (Grubba)  Pike_error("Threads can not be restarted (status:%d).\n", thread_state->status);
0431312003-02-15Henrik Grubbström (Grubba)  }
1d456f2003-02-20Henrik Grubbström (Grubba)  arg.args = aggregate_array(args); arg.thread_state = thread_state;
b2a0fb1997-02-06Henrik Grubbström (Grubba) 
2ef3b92005-01-25Henrik Grubbström (Grubba)  if (low_init_interpreter(&thread_state->state)) { free_array(arg.args); Pike_error("Out of memory allocating stack.\n"); }
222d922000-05-20Henrik Grubbström (Grubba) #ifdef HAVE_BROKEN_LINUX_THREAD_EUID
1d456f2003-02-20Henrik Grubbström (Grubba)  arg.euid = geteuid(); arg.egid = getegid();
222d922000-05-20Henrik Grubbström (Grubba) #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */
c17fed2000-05-20Henrik Grubbström (Grubba) 
4743f81999-06-02Fredrik Hübinette (Hubbe)  do {
1d456f2003-02-20Henrik Grubbström (Grubba)  tmp = th_create(&thread_state->id,
d40e381999-10-14Henrik Grubbström (Grubba)  new_thread_func,
1d456f2003-02-20Henrik Grubbström (Grubba)  &arg);
700dac2002-02-05Martin Stjernholm  if (tmp == EINTR) check_threads_etc();
4743f81999-06-02Fredrik Hübinette (Hubbe)  } while( tmp == EINTR );
b2a0fb1997-02-06Henrik Grubbström (Grubba) 
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  if(!tmp) { num_threads++;
1d456f2003-02-20Henrik Grubbström (Grubba)  thread_table_insert(thread_state);
a29e021996-10-15Fredrik Hübinette (Hubbe) 
fe76ce1997-09-08Fredrik Hübinette (Hubbe)  if(!threads_evaluator_callback)
a29e021996-10-15Fredrik Hübinette (Hubbe)  { threads_evaluator_callback=add_to_callback(&evaluator_callbacks, check_threads, 0,0);
424d9c1999-05-02Fredrik Hübinette (Hubbe)  dmalloc_accept_leak(threads_evaluator_callback);
a29e021996-10-15Fredrik Hübinette (Hubbe)  }
91518c2009-03-15Martin Stjernholm 
0431312003-02-15Henrik Grubbström (Grubba)  /* Wait for the thread to start properly. * so that we can avoid races.
1d456f2003-02-20Henrik Grubbström (Grubba)  * * 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...
0431312003-02-15Henrik Grubbström (Grubba)  */
b867f92003-02-16Martin Stjernholm  SWAP_OUT_CURRENT_THREAD();
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "f_thread_create %p waiting...\n", thread_state);
91518c2009-03-15Martin Stjernholm  while (thread_state->status == THREAD_NOT_STARTED)
335f1b2017-08-09Henrik Grubbström (Grubba)  co_wait_interpreter (&thread_state->status_change);
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "f_thread_create %p continue\n", thread_state);
b867f92003-02-16Martin Stjernholm  SWAP_IN_CURRENT_THREAD();
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  } else {
2ef3b92005-01-25Henrik Grubbström (Grubba)  low_cleanup_interpret(&thread_state->state);
1d456f2003-02-20Henrik Grubbström (Grubba)  free_array(arg.args);
0431312003-02-15Henrik Grubbström (Grubba)  Pike_error("Failed to create thread (errno = %d).\n", tmp);
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  }
1d456f2003-02-20Henrik Grubbström (Grubba) 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "f_thread_create %p done\n", thread_state);
6d1a5e1996-10-07Fredrik Hübinette (Hubbe) }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @endclass */
1e4a641997-09-03Martin Stjernholm #ifdef UNIX_THREADS
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl void thread_set_concurrency(int concurrency)
c7b7dd2001-10-28Martin Nilsson  *! *! @fixme *! Document this function
e413da2001-02-01Henrik Grubbström (Grubba)  */
97ffe41997-01-26Per Hedbor void f_thread_set_concurrency(INT32 args) { int c=1;
d4ecd72003-01-05Martin Nilsson  if(args) c=Pike_sp[-args].u.integer; else
06bd612016-01-26Martin Nilsson  SIMPLE_WRONG_NUM_ARGS_ERROR("thread_set_concurrency", 1);
97ffe41997-01-26Per Hedbor  pop_n_elems(args);
09dceb1997-09-01Per Hedbor  num_lwps=c;
97ffe41997-01-26Per Hedbor  th_setconcurrency(c); }
1e4a641997-09-03Martin Stjernholm #endif
97ffe41997-01-26Per Hedbor 
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl Thread.Thread this_thread() *! *! This function returns the object that identifies this thread. *! *! @seealso
c7b7dd2001-10-28Martin Nilsson  *! @[Thread()]
e413da2001-02-01Henrik Grubbström (Grubba)  */
fa8c692000-11-30Fredrik Hübinette (Hubbe) PMOD_EXPORT void f_this_thread(INT32 args)
6d1a5e1996-10-07Fredrik Hübinette (Hubbe) { pop_n_elems(args);
cb54df2015-09-02Henrik Grubbström (Grubba)  if (Pike_interpreter.thread_state && Pike_interpreter.thread_state->thread_obj) {
1d456f2003-02-20Henrik Grubbström (Grubba)  ref_push_object(Pike_interpreter.thread_state->thread_obj); } else { /* Threads not enabled yet/anylonger */ push_undefined(); }
07513e1996-10-04Fredrik Hübinette (Hubbe) }
7a52982015-09-09Henrik Grubbström (Grubba) /*! @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) {
0a3c552016-05-09Martin Nilsson  INT64 ns = 0;
7a52982015-09-09Henrik Grubbström (Grubba)  #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(); } }
60d9872000-03-23Fredrik Hübinette (Hubbe) #define THIS_MUTEX ((struct mutex_storage *)(CURRENT_STORAGE))
5988921996-10-05Fredrik Hübinette (Hubbe)  /* 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. */
d461912020-06-26Henrik Grubbström (Grubba) struct key_storage;
5988921996-10-05Fredrik Hübinette (Hubbe) struct mutex_storage { COND_T condition;
d461912020-06-26Henrik Grubbström (Grubba)  struct key_storage *key;
2702952004-04-21Martin Stjernholm  int num_waiting;
5988921996-10-05Fredrik Hübinette (Hubbe) };
ea9a822020-06-24Henrik Grubbström (Grubba) enum key_kind { KEY_UNINITIALIZED = 0, KEY_INITIALIZED,
4781e52020-06-28Henrik Grubbström (Grubba)  KEY_NONE = KEY_INITIALIZED, KEY_SHARED,
a94a082020-07-02Henrik Grubbström (Grubba)  KEY_DOWNGRADED,
35fa442020-06-27Henrik Grubbström (Grubba)  KEY_PENDING, KEY_EXCLUSIVE,
ea9a822020-06-24Henrik Grubbström (Grubba) };
5988921996-10-05Fredrik Hübinette (Hubbe) struct key_storage {
d461912020-06-26Henrik Grubbström (Grubba)  struct object *self; /* NOT reference-counted! */
5988921996-10-05Fredrik Hübinette (Hubbe)  struct mutex_storage *mut;
33887a2002-10-28Martin Stjernholm  struct object *mutex_obj;
1d456f2003-02-20Henrik Grubbström (Grubba)  struct thread_state *owner; struct object *owner_obj;
35fa442020-06-27Henrik Grubbström (Grubba)  struct key_storage *prev; struct key_storage *next;
ea9a822020-06-24Henrik Grubbström (Grubba)  enum key_kind kind;
5988921996-10-05Fredrik Hübinette (Hubbe) }; #define OB2KEY(X) ((struct key_storage *)((X)->storage))
07513e1996-10-04Fredrik Hübinette (Hubbe) 
e413da2001-02-01Henrik Grubbström (Grubba) /*! @class Mutex *!
c7b7dd2001-10-28Martin Nilsson  *! @[Mutex] is a class that implements mutual exclusion locks.
e413da2001-02-01Henrik Grubbström (Grubba)  *! 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)
6018db2021-07-21Henrik Grubbström (Grubba)  *! @decl MutexKey lock(int type, int(0..)|float seconds) *! @decl MutexKey lock(int type, int(0..) seconds @ *! int(0..999999999) nanos)
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! 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
4f68012002-10-28Martin Stjernholm  *! the key is destructed or has no more references the mutex will
5fba122004-04-26Martin Stjernholm  *! automatically be unlocked.
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! The @[type] argument specifies what @[lock()] should do if the *! mutex is already locked by this thread: *! @int
a9cdcf2001-02-06Henrik Grubbström (Grubba)  *! @value 0
e413da2001-02-01Henrik Grubbström (Grubba)  *! 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 *!
6018db2021-07-21Henrik Grubbström (Grubba)  *! @param seconds *! Seconds to wait before the timeout is reached. *! *! @param nanos *! Nano (1/1000000000) seconds to wait before the timeout is reached. *! This value is added to the number of seconds specified by @[seconds]. *! *! A timeout of zero seconds disables the timeout. *! *! @note *! The support for timeouts was added in Pike 8.1.14, which was *! before the first public release of Pike 8.1. *! *! @note *! Note that the timeout is approximate (best effort), and may *! be exceeded if eg the interpreter is busy after the timeout. *!
d059be2004-04-23Martin Stjernholm  *! @note
5fba122004-04-26Martin Stjernholm  *! If the mutex is destructed while it's locked or while threads are *! waiting on it, it will continue to exist internally until the last *! thread has stopped waiting and the last @[MutexKey] has *! disappeared, but further calls to the functions in this class will *! fail as is usual for destructed objects.
d059be2004-04-23Martin Stjernholm  *!
d85b4b2004-05-01Martin Stjernholm  *! @note *! Pike 7.4 and earlier destructed any outstanding lock when the *! mutex was destructed, but threads waiting in @[lock] still got *! functioning locks as discussed above. This is inconsistent no *! matter how you look at it, so it was changed in 7.6. The old *! behavior is retained in compatibility mode for applications that *! explicitly destruct mutexes to unlock them. *!
e413da2001-02-01Henrik Grubbström (Grubba)  *! @seealso *! @[trylock()] */
07513e1996-10-04Fredrik Hübinette (Hubbe) void f_mutex_lock(INT32 args) {
5988921996-10-05Fredrik Hübinette (Hubbe)  struct mutex_storage *m;
d461912020-06-26Henrik Grubbström (Grubba)  struct key_storage *key;
07513e1996-10-04Fredrik Hübinette (Hubbe)  struct object *o;
ef72d42020-07-01Henrik Grubbström (Grubba)  INT_TYPE type = 0;
6018db2021-07-21Henrik Grubbström (Grubba)  INT_TYPE seconds = 0, nanos = 0;
5988921996-10-05Fredrik Hübinette (Hubbe) 
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
38e1e82001-11-08Fredrik Hübinette (Hubbe) 
07513e1996-10-04Fredrik Hübinette (Hubbe)  m=THIS_MUTEX;
6018db2021-07-21Henrik Grubbström (Grubba)  if (args <= 2) { FLOAT_TYPE fsecs = 0.0; get_all_args(NULL, args, ".%i%F", &type, &fsecs); if (fsecs > 0.0) { seconds = (INT_TYPE)fsecs; nanos = (INT_TYPE)((fsecs - seconds) * 1000000000); } } else { INT_TYPE adj = 0; get_all_args(NULL, args, "%i%i%i", &type, &seconds, &nanos); /* Normalize. */ adj = nanos / 1000000000; if (nanos < 0) adj--; nanos -= adj * 1000000000; seconds += adj; }
a49ee61999-04-02Fredrik Hübinette (Hubbe)  switch(type) { default:
212c392018-02-25Martin Nilsson  bad_arg_error("lock", args, 2, "int(0..2)", Pike_sp+1-args,
69c0aa2014-08-25Martin Nilsson  "Unknown mutex locking style: %"PRINTPIKEINT"d\n",type);
13670c2015-05-25Martin Nilsson 
a49ee61999-04-02Fredrik Hübinette (Hubbe)  case 0: case 2:
4781e52020-06-28Henrik Grubbström (Grubba)  for (key = m->key; key; key = key->next) { if(key->owner == Pike_interpreter.thread_state) { THREADS_FPRINTF(0, "Recursive LOCK k:%p, m:%p(%p), t:%p\n", key, m, key->mut, Pike_interpreter.thread_state);
a49ee61999-04-02Fredrik Hübinette (Hubbe) 
4781e52020-06-28Henrik Grubbström (Grubba)  if(type==0) Pike_error("Recursive mutex locks!\n");
a49ee61999-04-02Fredrik Hübinette (Hubbe) 
4781e52020-06-28Henrik Grubbström (Grubba)  pop_n_elems(args); push_int(0); return; }
a49ee61999-04-02Fredrik Hübinette (Hubbe)  } case 1: break; }
46d7bf1997-09-03Henrik Grubbström (Grubba)  /* Needs to be cloned here, since create() * might use threads. */
f777b72003-02-15Henrik Grubbström (Grubba)  o=fast_clone_object(mutex_key);
4bdad01997-09-01Per Hedbor 
35fa442020-06-27Henrik Grubbström (Grubba)  key = OB2KEY(o); key->mut = m; add_ref (key->mutex_obj = Pike_fp->current_object); key->prev = NULL; if ((key->next = m->key)) { m->key->prev = key; } m->key = key; key->kind = KEY_PENDING;
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
26cd941999-05-07Fredrik Hübinette (Hubbe) 
35fa442020-06-27Henrik Grubbström (Grubba)  if(key->next)
fe76ce1997-09-08Fredrik Hübinette (Hubbe)  {
adb19b1999-06-30Fredrik Hübinette (Hubbe)  if(threads_disabled) { free_object(o);
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Cannot wait for mutexes when threads are disabled!\n");
adb19b1999-06-30Fredrik Hübinette (Hubbe)  }
3399e62020-07-03Henrik Grubbström (Grubba)  m->num_waiting++;
6018db2021-07-21Henrik Grubbström (Grubba)  if (seconds || nanos) { /* NB: Negative seconds ==> try lock. */ if (seconds >= 0) { struct timeval timeout; ACCURATE_GETTIMEOFDAY(&timeout); timeout.tv_sec += seconds; timeout.tv_usec += nanos/1000; do { THREADS_FPRINTF(1, "WAITING TO LOCK m:%p\n", m); SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter_timeout(& m->condition, seconds, nanos); SWAP_IN_CURRENT_THREAD(); check_threads_etc(); if (key->next) { /* Check for timeout, and update seconds & nanos. */ struct timeval now; ACCURATE_GETTIMEOFDAY(&now); seconds = timeout.tv_sec - now.tv_sec; nanos = (timeout.tv_usec - now.tv_usec) * 1000; if (nanos < 0) { seconds--; nanos += 1000000000; } if ((seconds < 0) || (!seconds && !nanos)) break; } } while (key->next); } } else { do { THREADS_FPRINTF(1, "WAITING TO LOCK m:%p\n", m); SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter(& m->condition); SWAP_IN_CURRENT_THREAD(); check_threads_etc(); } while (key->next); }
2702952004-04-21Martin Stjernholm  m->num_waiting--;
fe76ce1997-09-08Fredrik Hübinette (Hubbe)  }
2702952004-04-21Martin Stjernholm 
6018db2021-07-21Henrik Grubbström (Grubba)  if (key->next) { /* Timeout. */ /* NB: We know that mutex_key doesn't have an lfun:_destruct() * that inhibits our destruct(). */ destruct(o); free_object(o); DEBUG_CHECK_THREAD(); THREADS_FPRINTF(1, "LOCK k:%p, m:%p(%p), t:%p\n", NULL, m, NULL, Pike_interpreter.thread_state); pop_n_elems(args); push_int(0); return; }
2702952004-04-21Martin Stjernholm #ifdef PICKY_MUTEX if (!Pike_fp->current_object->prog) { free_object (o);
8af5622016-08-09Henrik Grubbström (Grubba)  if (!m->num_waiting) {
2702952004-04-21Martin Stjernholm  co_destroy (&m->condition);
8af5622016-08-09Henrik Grubbström (Grubba)  }
2702952004-04-21Martin Stjernholm  Pike_error ("Mutex was destructed while waiting for lock.\n"); } #endif
35fa442020-06-27Henrik Grubbström (Grubba)  key->kind = KEY_EXCLUSIVE;
fe76ce1997-09-08Fredrik Hübinette (Hubbe) 
b867f92003-02-16Martin Stjernholm  DEBUG_CHECK_THREAD();
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "LOCK k:%p, m:%p(%p), t:%p\n",
35fa442020-06-27Henrik Grubbström (Grubba)  key, m, key->mut,
7d06ba2016-02-07Martin Nilsson  Pike_interpreter.thread_state);
65d3c31997-09-08Fredrik Hübinette (Hubbe)  pop_n_elems(args);
07513e1996-10-04Fredrik Hübinette (Hubbe)  push_object(o); }
4781e52020-06-28Henrik Grubbström (Grubba) /*! @decl MutexKey shared_lock() *! @decl MutexKey shared_lock(int type)
6018db2021-07-21Henrik Grubbström (Grubba)  *! @decl MutexKey shared_lock(int type, int(0..)|float seconds) *! @decl MutexKey shared_lock(int type, int(0..) seconds @ *! int(0..999999999) nanos)
4781e52020-06-28Henrik Grubbström (Grubba)  *! *! 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 mutex will *! automatically be unlocked. *! *! The @[type] argument specifies what @[lock()] should do if the *! mutex is already locked exclusively by this thread: *! @int *! @value 0 *! 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 *!
6018db2021-07-21Henrik Grubbström (Grubba)  *! @param seconds *! Seconds to wait before the timeout is reached. *! *! @param nanos *! Nano (1/1000000000) seconds to wait before the timeout is reached. *! This value is added to the number of seconds specified by @[seconds]. *! *! A timeout of zero seconds disables the timeout. *! *! @note *! Note that the timeout is approximate (best effort), and may *! be exceeded if eg the interpreter is busy after the timeout. *!
4781e52020-06-28Henrik Grubbström (Grubba)  *! @note
8f1ff92020-06-29Henrik Grubbström (Grubba)  *! Note that if the current thread already holds a shared key, *! a new will be created without waiting, and regardless of *! the value of @[type]. *! *! @note
4781e52020-06-28Henrik Grubbström (Grubba)  *! If the mutex is destructed while it's locked or while threads are *! waiting on it, it will continue to exist internally until the last *! thread has stopped waiting and the last @[MutexKey] has *! disappeared, but further calls to the functions in this class will *! fail as is usual for destructed objects. *!
6018db2021-07-21Henrik Grubbström (Grubba)  *! @note *! Support for shared keys was added in Pike 8.1. *!
4781e52020-06-28Henrik Grubbström (Grubba)  *! @seealso *! @[lock()], @[trylock()], @[try_shared_lock()] */ void f_mutex_shared_lock(INT32 args) { struct mutex_storage *m;
8f1ff92020-06-29Henrik Grubbström (Grubba)  struct key_storage *key, *prev = NULL; struct object *o, *prev_o = NULL; INT_TYPE type = 0;
6018db2021-07-21Henrik Grubbström (Grubba)  INT_TYPE seconds = 0, nanos = 0;
4781e52020-06-28Henrik Grubbström (Grubba)  DEBUG_CHECK_THREAD(); m=THIS_MUTEX;
6018db2021-07-21Henrik Grubbström (Grubba)  if (args <= 2) { FLOAT_TYPE fsecs = 0.0; get_all_args(NULL, args, ".%i%F", &type, &fsecs); if (fsecs > 0.0) { seconds = (INT_TYPE)fsecs; nanos = (INT_TYPE)((fsecs - seconds) * 1000000000); } } else { INT_TYPE adj = 0; get_all_args(NULL, args, "%i%i%i", &type, &seconds, &nanos); /* Normalize. */ adj = nanos / 1000000000; if (nanos < 0) adj--; nanos -= adj * 1000000000; seconds += adj; }
4781e52020-06-28Henrik Grubbström (Grubba) 
8f1ff92020-06-29Henrik Grubbström (Grubba)  if ((type < 0) || (type > 2)) { bad_arg_error("shared_lock", args, 2, "int(0..2)", Pike_sp+1-args, "Unknown mutex locking style: %"PRINTPIKEINT"d\n", type); }
4781e52020-06-28Henrik Grubbström (Grubba) 
8f1ff92020-06-29Henrik Grubbström (Grubba)  for (key = m->key; key; key = key->next) { if (key->owner == Pike_interpreter.thread_state) { THREADS_FPRINTF(0, "Recursive LOCK k:%p, m:%p(%p), t:%p\n", key, m, key->mut, Pike_interpreter.thread_state);
4781e52020-06-28Henrik Grubbström (Grubba) 
8f1ff92020-06-29Henrik Grubbström (Grubba)  if (key->kind >= KEY_PENDING) { /* This thread already has an exclusive lock. */ switch(type) { case 0: Pike_error("Recursive mutex locks!\n"); break; case 1: continue; case 2: pop_n_elems(args); push_int(0); return;
4781e52020-06-28Henrik Grubbström (Grubba)  } }
8f1ff92020-06-29Henrik Grubbström (Grubba)  prev = key; prev_o = prev->self; add_ref(prev_o);
4781e52020-06-28Henrik Grubbström (Grubba)  break;
8f1ff92020-06-29Henrik Grubbström (Grubba)  }
4781e52020-06-28Henrik Grubbström (Grubba)  } /* Needs to be cloned here, since create() * might use threads. */ o=fast_clone_object(mutex_key); DEBUG_CHECK_THREAD();
8f1ff92020-06-29Henrik Grubbström (Grubba)  if (prev_o && !prev_o->prog) { /* Paranoia: prev got destructed during the clone above. * * NB: We rely on this thread not having added a new key * during the clone above. * * NB: We also rely on prev not getting unlinked for any other * reason than getting destructed. */ prev = NULL; free_object(prev_o); prev_o = NULL; for (key = m->key; key; key = key->next) { if (key->kind >= KEY_PENDING) continue; if (key->owner == Pike_interpreter.thread_state) { prev = key; prev_o = prev->self; add_ref(prev_o); break; }
4781e52020-06-28Henrik Grubbström (Grubba)  }
8f1ff92020-06-29Henrik Grubbström (Grubba)  }
4781e52020-06-28Henrik Grubbström (Grubba) 
8f1ff92020-06-29Henrik Grubbström (Grubba)  if (!prev) {
6018db2021-07-21Henrik Grubbström (Grubba)  if(threads_disabled) { free_object(o); Pike_error("Cannot wait for mutexes when threads are disabled!\n"); }
8f1ff92020-06-29Henrik Grubbström (Grubba) 
6018db2021-07-21Henrik Grubbström (Grubba)  m->num_waiting++; if (seconds || nanos) { /* NB: Negative seconds ==> try lock. */ if (seconds >= 0) { struct timeval timeout; ACCURATE_GETTIMEOFDAY(&timeout); timeout.tv_sec += seconds; timeout.tv_usec += nanos/1000; while (m->key && (m->key->kind >= KEY_PENDING)) { THREADS_FPRINTF(1, "WAITING TO SHARE LOCK m:%p\n", m); SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter_timeout(& m->condition, seconds, nanos); SWAP_IN_CURRENT_THREAD(); check_threads_etc(); if (m->key && (m->key->kind >= KEY_PENDING)) { /* Check for timeout, and update seconds & nanos. */ struct timeval now; ACCURATE_GETTIMEOFDAY(&now); seconds = timeout.tv_sec - now.tv_sec; nanos = (timeout.tv_usec - now.tv_usec) * 1000; if (nanos < 0) { seconds--; nanos += 1000000000; } if ((seconds < 0) || (!seconds && !nanos)) break; } } } } else { while (m->key && (m->key->kind >= KEY_PENDING)) { THREADS_FPRINTF(1, "WAITING TO SHARE LOCK m:%p\n", m); SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter(& m->condition); SWAP_IN_CURRENT_THREAD(); check_threads_etc(); }
8f1ff92020-06-29Henrik Grubbström (Grubba)  }
6018db2021-07-21Henrik Grubbström (Grubba)  m->num_waiting--;
a94a082020-07-02Henrik Grubbström (Grubba)  if (m->key && (m->key->kind == KEY_DOWNGRADED)) { /* Link new read keys after the downgraded key (if any). */ prev = m->key; prev_o = prev->self; add_ref(prev_o); }
4781e52020-06-28Henrik Grubbström (Grubba)  }
6018db2021-07-21Henrik Grubbström (Grubba)  if (m->key && (m->key->kind >= KEY_PENDING)) { /* Timeout. */ /* NB: We know that mutex_key doesn't have an lfun:_destruct() * that inhibits our destruct(). */ destruct(o); free_object(o); DEBUG_CHECK_THREAD(); THREADS_FPRINTF(1, "SHARED_LOCK k:%p, m:%p(%p), t:%p\n", NULL, m, NULL, Pike_interpreter.thread_state); pop_n_elems(args); push_int(0); return; }
4781e52020-06-28Henrik Grubbström (Grubba) #ifdef PICKY_MUTEX if (!Pike_fp->current_object->prog) { /* The mutex has been destructed during our wait. */ destruct(o); free_object (o); if (!m->num_waiting) { co_destroy (&m->condition); }
a94a082020-07-02Henrik Grubbström (Grubba)  if (prev_o) free_object(prev_o);
4781e52020-06-28Henrik Grubbström (Grubba)  Pike_error ("Mutex was destructed while waiting for lock.\n"); } #endif
8f1ff92020-06-29Henrik Grubbström (Grubba)  /* Three cases here:
4781e52020-06-28Henrik Grubbström (Grubba)  *
8f1ff92020-06-29Henrik Grubbström (Grubba)  * prev != NULL * prev == NULL && m->key == NULL * prev == NULL && m->key && m->key->kind == KEY_READ
4781e52020-06-28Henrik Grubbström (Grubba)  */ key = OB2KEY(o); key->mut = m; add_ref (key->mutex_obj = Pike_fp->current_object);
8f1ff92020-06-29Henrik Grubbström (Grubba)  if (prev) { /* Add key after prev in the list. */ key->prev = prev; if ((key->next = prev->next)) { prev->next->prev = key; } prev->next = key; free_object(prev_o); } else { /* Add key first in the list. */ key->prev = NULL; if ((key->next = m->key)) { m->key->prev = key; } m->key = key;
4781e52020-06-28Henrik Grubbström (Grubba)  }
a94a082020-07-02Henrik Grubbström (Grubba) 
4781e52020-06-28Henrik Grubbström (Grubba)  key->kind = KEY_SHARED; DEBUG_CHECK_THREAD(); THREADS_FPRINTF(1, "SHARED_LOCK k:%p, m:%p(%p), t:%p\n", key, m, key->mut, Pike_interpreter.thread_state); pop_n_elems(args); push_object(o); }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @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()] */
07513e1996-10-04Fredrik Hübinette (Hubbe) void f_mutex_trylock(INT32 args) {
5988921996-10-05Fredrik Hübinette (Hubbe)  struct mutex_storage *m;
d461912020-06-26Henrik Grubbström (Grubba)  struct key_storage *key;
5988921996-10-05Fredrik Hübinette (Hubbe)  struct object *o;
ef72d42020-07-01Henrik Grubbström (Grubba)  INT_TYPE type = 0;
5988921996-10-05Fredrik Hübinette (Hubbe)  int i=0;
07513e1996-10-04Fredrik Hübinette (Hubbe) 
46d7bf1997-09-03Henrik Grubbström (Grubba)  /* No reason to release the interpreter lock here * since we aren't calling any functions that take time. */
a49ee61999-04-02Fredrik Hübinette (Hubbe)  m=THIS_MUTEX;
ef72d42020-07-01Henrik Grubbström (Grubba)  get_all_args(NULL, args, ".%i", &type);
a49ee61999-04-02Fredrik Hübinette (Hubbe)  switch(type)
65df5c1997-09-01Per Hedbor  {
a49ee61999-04-02Fredrik Hübinette (Hubbe)  default:
212c392018-02-25Martin Nilsson  bad_arg_error("trylock", args, 2, "int(0..2)", Pike_sp+1-args,
69c0aa2014-08-25Martin Nilsson  "Unknown mutex locking style: %"PRINTPIKEINT"d\n",type);
a49ee61999-04-02Fredrik Hübinette (Hubbe)  case 0:
35fa442020-06-27Henrik Grubbström (Grubba)  for (key = m->key; key; key = key->next) { if(key->owner == Pike_interpreter.thread_state) { Pike_error("Recursive mutex locks!\n"); }
a49ee61999-04-02Fredrik Hübinette (Hubbe)  } case 2: case 1: break;
65df5c1997-09-01Per Hedbor  }
50fd181997-09-17Fredrik Hübinette (Hubbe) 
a49ee61999-04-02Fredrik Hübinette (Hubbe)  o=clone_object(mutex_key,0);
d461912020-06-26Henrik Grubbström (Grubba)  key = OB2KEY(o);
a49ee61999-04-02Fredrik Hübinette (Hubbe) 
5988921996-10-05Fredrik Hübinette (Hubbe)  if(!m->key) {
d461912020-06-26Henrik Grubbström (Grubba)  key->mut = m; add_ref (key->mutex_obj = Pike_fp->current_object); m->key = key;
35fa442020-06-27Henrik Grubbström (Grubba)  key->kind = KEY_EXCLUSIVE;
5988921996-10-05Fredrik Hübinette (Hubbe)  i=1; }
13670c2015-05-25Martin Nilsson 
65d3c31997-09-08Fredrik Hübinette (Hubbe)  pop_n_elems(args);
07513e1996-10-04Fredrik Hübinette (Hubbe)  if(i) { push_object(o); } else {
de78e62020-03-02Henrik Grubbström (Grubba)  /* NB: We know that mutex_key doesn't have an lfun:_destruct() * that inhibits our destruct(). */
5988921996-10-05Fredrik Hübinette (Hubbe)  destruct(o); free_object(o);
07513e1996-10-04Fredrik Hübinette (Hubbe)  push_int(0); } }
4781e52020-06-28Henrik Grubbström (Grubba) /*! @decl MutexKey try_shared_lock() *! @decl MutexKey try_shared_lock(int type) *! *! This function performs the same operation as @[shared_lock()], *! but if the mutex is already locked exclusively, it will return *! zero instead of sleeping until it's unlocked. *!
6018db2021-07-21Henrik Grubbström (Grubba)  *! @note *! Support for shared keys was added in Pike 8.1. *!
4781e52020-06-28Henrik Grubbström (Grubba)  *! @seealso *! @[shared_lock()], @[lock()], @[trylock()] */ void f_mutex_try_shared_lock(INT32 args) { struct mutex_storage *m;
8f1ff92020-06-29Henrik Grubbström (Grubba)  struct key_storage *key, *prev = NULL;
4781e52020-06-28Henrik Grubbström (Grubba)  struct object *o;
8f1ff92020-06-29Henrik Grubbström (Grubba)  INT_TYPE type = 0;
4781e52020-06-28Henrik Grubbström (Grubba)  int i=0; /* No reason to release the interpreter lock here * since we aren't calling any functions that take time. */ m=THIS_MUTEX;
8f1ff92020-06-29Henrik Grubbström (Grubba)  get_all_args(NULL, args, ".%i", &type);
4781e52020-06-28Henrik Grubbström (Grubba) 
8f1ff92020-06-29Henrik Grubbström (Grubba)  if ((type < 0) || (type > 2)) { bad_arg_error("try_shared_lock", args, 2, "int(0..2)", Pike_sp+1-args, "Unknown mutex locking style: %"PRINTPIKEINT"d\n",type); }
4781e52020-06-28Henrik Grubbström (Grubba) 
8f1ff92020-06-29Henrik Grubbström (Grubba)  o = clone_object(mutex_key, 0); for (key = m->key; key; key = key->next) { if(key->owner == Pike_interpreter.thread_state) { if (key->kind >= KEY_PENDING) { if (type == 0) { free_object(o);
4781e52020-06-28Henrik Grubbström (Grubba)  Pike_error("Recursive mutex locks!\n"); }
8f1ff92020-06-29Henrik Grubbström (Grubba)  continue;
4781e52020-06-28Henrik Grubbström (Grubba)  }
8f1ff92020-06-29Henrik Grubbström (Grubba)  prev = key;
4781e52020-06-28Henrik Grubbström (Grubba)  break;
8f1ff92020-06-29Henrik Grubbström (Grubba)  }
4781e52020-06-28Henrik Grubbström (Grubba)  }
a94a082020-07-02Henrik Grubbström (Grubba)  if (m->key && (m->key->kind == KEY_DOWNGRADED) && !prev) { /* Keep the downgraded key first. */ prev = m->key; }
4781e52020-06-28Henrik Grubbström (Grubba)  key = OB2KEY(o);
8f1ff92020-06-29Henrik Grubbström (Grubba)  if (prev) {
f2f6e42020-06-30Henrik Grubbström (Grubba)  /* Link after prev. */
8f1ff92020-06-29Henrik Grubbström (Grubba)  key->mut = m; add_ref (key->mutex_obj = Pike_fp->current_object); key->prev = prev;
f2f6e42020-06-30Henrik Grubbström (Grubba)  if ((key->next = prev->next)) {
8f1ff92020-06-29Henrik Grubbström (Grubba)  prev->next->prev = key; } prev->next = key; key->kind = KEY_SHARED; i=1; } else if (!m->key || (m->key->kind < KEY_PENDING)) {
4781e52020-06-28Henrik Grubbström (Grubba)  key->mut = m; add_ref (key->mutex_obj = Pike_fp->current_object); if ((key->next = m->key)) { key->next->prev = key; } m->key = key; key->prev = NULL; key->kind = KEY_SHARED; i=1; } pop_n_elems(args); if(i) { push_object(o); } else { /* NB: We know that mutex_key doesn't have an lfun:_destruct() * that inhibits our destruct(). */ destruct(o); free_object(o); push_int(0); } }
b9bba02001-08-08Leif Stensson /*! @decl Thread.Thread current_locking_thread() *! *! This mutex method returns the object that identifies the thread that *! has locked the mutex. 0 is returned if the mutex isn't locked. *! *! @seealso
c7b7dd2001-10-28Martin Nilsson  *! @[Thread()]
b9bba02001-08-08Leif Stensson  */ PMOD_EXPORT void f_mutex_locking_thread(INT32 args) { struct mutex_storage *m = THIS_MUTEX;
35fa442020-06-27Henrik Grubbström (Grubba)  struct key_storage *k;
b9bba02001-08-08Leif Stensson  pop_n_elems(args);
35fa442020-06-27Henrik Grubbström (Grubba)  for (k = m->key; k; k = k->next) { if (k->kind == KEY_PENDING) continue; if (m->key->owner) { ref_push_object(m->key->owner->thread_obj); return; } } push_int(0);
b9bba02001-08-08Leif Stensson }
33887a2002-10-28Martin Stjernholm /*! @decl Thread.MutexKey current_locking_key()
b9bba02001-08-08Leif Stensson  *! *! This mutex method returns the key object currently governing *! the lock on this mutex. 0 is returned if the mutex isn't locked. *! *! @seealso
c7b7dd2001-10-28Martin Nilsson  *! @[Thread()]
b9bba02001-08-08Leif Stensson  */ PMOD_EXPORT void f_mutex_locking_key(INT32 args) { struct mutex_storage *m = THIS_MUTEX;
35fa442020-06-27Henrik Grubbström (Grubba)  struct key_storage *k;
b9bba02001-08-08Leif Stensson  pop_n_elems(args);
35fa442020-06-27Henrik Grubbström (Grubba)  for (k = m->key; k; k = k->next) { if (k->kind == KEY_PENDING) continue;
d461912020-06-26Henrik Grubbström (Grubba)  ref_push_object(m->key->self);
35fa442020-06-27Henrik Grubbström (Grubba)  return; } push_int(0);
b9bba02001-08-08Leif Stensson }
8271012016-12-20Jonas Walldén /*! @decl protected string _sprintf(int c) *! *! Describes the mutex including the thread that currently holds the lock *! (if any). */ void f_mutex__sprintf (INT32 args) { struct mutex_storage *m = THIS_MUTEX;
35fa442020-06-27Henrik Grubbström (Grubba)  struct key_storage *k;
8271012016-12-20Jonas Walldén  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; }
35fa442020-06-27Henrik Grubbström (Grubba)  k = m->key; if (k) { enum key_kind kind = KEY_UNINITIALIZED; struct svalue *save_sp = Pike_sp; push_static_text("Thread.Mutex(/*"); for (; k; k = k->next) { if (k->kind != kind) { kind = k->kind; switch(kind) { case KEY_PENDING: push_static_text(" Waiting by"); break; case KEY_EXCLUSIVE: push_static_text(" Locked by"); break; default: break; } } else { push_static_text(","); } push_static_text(" 0x"); push_int64(PTR_TO_INT(THREAD_T_TO_PTR(m->key->owner->id))); f_int2hex(1); } push_static_text(" */)"); f_add(Pike_sp - save_sp);
8271012016-12-20Jonas Walldén  } else {
9acf3b2019-09-06Henrik Grubbström (Grubba)  push_static_text("Thread.Mutex(/* Unlocked */)");
8271012016-12-20Jonas Walldén  } }
f327f22018-09-23Henrik Grubbström (Grubba) /*! @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)); }
74dfe82012-12-30Jonas Walldén void init_mutex_obj(struct object *UNUSED(o))
5988921996-10-05Fredrik Hübinette (Hubbe) { co_init(& THIS_MUTEX->condition);
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
5988921996-10-05Fredrik Hübinette (Hubbe)  THIS_MUTEX->key=0;
2702952004-04-21Martin Stjernholm  THIS_MUTEX->num_waiting = 0;
b467522017-06-25Martin Nilsson #endif
5988921996-10-05Fredrik Hübinette (Hubbe) }
07513e1996-10-04Fredrik Hübinette (Hubbe) 
74dfe82012-12-30Jonas Walldén void exit_mutex_obj(struct object *UNUSED(o))
5988921996-10-05Fredrik Hübinette (Hubbe) {
2702952004-04-21Martin Stjernholm  struct mutex_storage *m = THIS_MUTEX;
d461912020-06-26Henrik Grubbström (Grubba)  struct key_storage *key = m->key;
2702952004-04-21Martin Stjernholm 
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "DESTROYING MUTEX m:%p\n", THIS_MUTEX);
2702952004-04-21Martin Stjernholm 
5fba122004-04-26Martin Stjernholm #ifndef PICKY_MUTEX if (key) { /* The last key will destroy m->condition in its exit hook. */
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "Destructed mutex is in use - delaying cleanup\n");
5fba122004-04-26Martin Stjernholm  } else { #ifdef PIKE_DEBUG if (m->num_waiting) Pike_error ("key/num_waiting confusion.\n"); #endif co_destroy(& m->condition); } #else
f72d412004-04-23Henrik Grubbström (Grubba)  if(key) {
35fa442020-06-27Henrik Grubbström (Grubba)  struct key_storage *next = key->next;
2702952004-04-21Martin Stjernholm  m->key=0;
35fa442020-06-27Henrik Grubbström (Grubba)  for (; key; key = next) { next = key->next; key->prev = NULL; key->next = NULL; /* NB: We know that mutex_key doesn't have an lfun:_destruct() * that inhibits our destruct(). */ destruct(key->self); /* Will destroy m->condition if m->num_waiting is zero. */ }
2702952004-04-21Martin Stjernholm  if(m->num_waiting) {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "Destructed mutex is being waited on.\n");
8454562017-04-26Henrik Grubbström (Grubba)  THREADS_ALLOW();
2702952004-04-21Martin Stjernholm  /* 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);
8454562017-04-26Henrik Grubbström (Grubba)  /* 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();
2702952004-04-21Martin Stjernholm  } }
8af5622016-08-09Henrik Grubbström (Grubba)  else {
5fba122004-04-26Martin Stjernholm  co_destroy(& m->condition);
8af5622016-08-09Henrik Grubbström (Grubba)  }
5fba122004-04-26Martin Stjernholm #endif
5988921996-10-05Fredrik Hübinette (Hubbe) }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @endclass */
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @class MutexKey *! *! Objects of this class are returned by @[Mutex()->lock()]
0c56d52021-07-23Henrik Grubbström (Grubba)  *! and @[Mutex()->trylock()] et al. They are also passed as arguments
192ceb2003-11-22Henrik Grubbström (Grubba)  *! to @[Condition()->wait()]. *! *! 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] */
60d9872000-03-23Fredrik Hübinette (Hubbe) #define THIS_KEY ((struct key_storage *)(CURRENT_STORAGE))
74dfe82012-12-30Jonas Walldén void init_mutex_key_obj(struct object *UNUSED(o))
5988921996-10-05Fredrik Hübinette (Hubbe) {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "KEY k:%p, t:%p\n", THIS_KEY, Pike_interpreter.thread_state);
b467522017-06-25Martin Nilsson #ifdef PIKE_NULL_IS_SPECIAL
5988921996-10-05Fredrik Hübinette (Hubbe)  THIS_KEY->mut=0;
33887a2002-10-28Martin Stjernholm  THIS_KEY->mutex_obj = NULL;
35fa442020-06-27Henrik Grubbström (Grubba)  THIS_KEY->prev = NULL; THIS_KEY->next = NULL;
b467522017-06-25Martin Nilsson #endif
d461912020-06-26Henrik Grubbström (Grubba)  THIS_KEY->self = Pike_fp->current_object;
1d456f2003-02-20Henrik Grubbström (Grubba)  THIS_KEY->owner = Pike_interpreter.thread_state;
84091f2015-09-07Arne Goedeke  THIS_KEY->owner_obj = Pike_interpreter.thread_state->thread_obj; if (THIS_KEY->owner_obj) add_ref(THIS_KEY->owner_obj);
ea9a822020-06-24Henrik Grubbström (Grubba)  THIS_KEY->kind = KEY_INITIALIZED;
5988921996-10-05Fredrik Hübinette (Hubbe) }
07513e1996-10-04Fredrik Hübinette (Hubbe) 
74dfe82012-12-30Jonas Walldén void exit_mutex_key_obj(struct object *DEBUGUSED(o))
07513e1996-10-04Fredrik Hübinette (Hubbe) {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(1, "UNLOCK k:%p m:(%p) t:%p o:%p\n", THIS_KEY, THIS_KEY->mut, Pike_interpreter.thread_state, THIS_KEY->owner);
5988921996-10-05Fredrik Hübinette (Hubbe)  if(THIS_KEY->mut)
07513e1996-10-04Fredrik Hübinette (Hubbe)  {
03b1fe1997-09-06Per Hedbor  struct mutex_storage *mut = THIS_KEY->mut;
d461912020-06-26Henrik Grubbström (Grubba)  struct key_storage *key = THIS_KEY;
f72d412004-04-23Henrik Grubbström (Grubba)  struct object *mutex_obj;
c113871997-09-15Henrik Grubbström (Grubba) 
35fa442020-06-27Henrik Grubbström (Grubba)  /* Unlink */ if (key->prev) {
71f3a21998-11-22Fredrik Hübinette (Hubbe) #ifdef PIKE_DEBUG
35fa442020-06-27Henrik Grubbström (Grubba)  if(key->prev->next != key) Pike_fatal("Inconsistent mutex key link %p != %p!\n", key->prev->next, key); #endif if ((key->prev->next = key->next)) { key->next->prev = key->prev; } key->prev = NULL; } else { #ifdef PIKE_DEBUG /* Note: mut->key can be NULL if our corresponding mutex * has been destructed. * * FIXME: What about the case with temporary unlinking in * Condition()->wait()? */ if(mut->key && (mut->key != key)) Pike_fatal("Mutex unlock from wrong key %p != %p!\n", key->mut->key, key);
5988921996-10-05Fredrik Hübinette (Hubbe) #endif
35fa442020-06-27Henrik Grubbström (Grubba)  if ((mut->key = key->next)) { key->next->prev = NULL; } } key->next = NULL;
46d7bf1997-09-03Henrik Grubbström (Grubba)  if (THIS_KEY->owner) {
1d456f2003-02-20Henrik Grubbström (Grubba)  THIS_KEY->owner = NULL; } if (THIS_KEY->owner_obj) { free_object(THIS_KEY->owner_obj); THIS_KEY->owner_obj=0;
46d7bf1997-09-03Henrik Grubbström (Grubba)  }
5988921996-10-05Fredrik Hübinette (Hubbe)  THIS_KEY->mut=0;
ea9a822020-06-24Henrik Grubbström (Grubba)  THIS_KEY->kind = KEY_UNINITIALIZED;
f72d412004-04-23Henrik Grubbström (Grubba)  mutex_obj = THIS_KEY->mutex_obj; THIS_KEY->mutex_obj = NULL;
8454562017-04-26Henrik Grubbström (Grubba)  if (mut->num_waiting) { THREADS_ALLOW();
35fa442020-06-27Henrik Grubbström (Grubba)  co_broadcast(&mut->condition);
8454562017-04-26Henrik Grubbström (Grubba)  /* 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) {
2702952004-04-21Martin Stjernholm  co_destroy (&mut->condition);
8af5622016-08-09Henrik Grubbström (Grubba)  }
8454562017-04-26Henrik Grubbström (Grubba) 
73cd0e2005-11-15Henrik Grubbström (Grubba)  if (mutex_obj) free_object(mutex_obj);
07513e1996-10-04Fredrik Hübinette (Hubbe)  } }
0c56d52021-07-23Henrik Grubbström (Grubba) /*! @decl protected string _sprintf(int c) */
27f1f92019-05-20Henrik Grubbström (Grubba) static void f_mutex_key__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; } if (THIS_KEY->mutex_obj) {
9acf3b2019-09-06Henrik Grubbström (Grubba)  push_text("Thread.MutexKey(/* %O */)");
27f1f92019-05-20Henrik Grubbström (Grubba)  ref_push_object(THIS_KEY->mutex_obj); f_sprintf(2); } else {
9acf3b2019-09-06Henrik Grubbström (Grubba)  push_text("Thread.MutexKey(/* Unlocked */)");
27f1f92019-05-20Henrik Grubbström (Grubba)  } }
0c56d52021-07-23Henrik Grubbström (Grubba) /*! @decl void downgrade() *! *! Downgrade an exclusive lock into a shared lock. *! *! A downgraded lock may be upgraded back to an exclusive lock *! by calling @[upgrade()] or @[try_upgrade()]. *! *! @seealso *! @[upgrade()], @[try_upgrade()] */
a94a082020-07-02Henrik Grubbström (Grubba) static void f_mutex_key_downgrade(INT32 args) { struct key_storage *key = THIS_KEY; if (key->kind < KEY_PENDING) return; if (key->kind == KEY_PENDING) { Pike_error("It is not possible to downgrade a key that is waiting.\n"); } key->kind = KEY_DOWNGRADED; co_broadcast(&key->mut->condition); }
0c56d52021-07-23Henrik Grubbström (Grubba) /*! @decl void upgrade() *! *! Upgrade a downgraded lock into an exclusive lock. *! *! Waits until all concurrent shared locks are released, *! and upgrades this lock into being an exclusive lock. *! Any attempts to make new shared locks will block as *! soon as this function is called. *! *! @seealso *! @[downgrade()], @[try_upgrade()] */
a94a082020-07-02Henrik Grubbström (Grubba) static void f_mutex_key_upgrade(INT32 args) { struct key_storage *key = THIS_KEY; struct mutex_storage *m = key->mut; if (key->kind >= KEY_PENDING) return; if (key->kind != KEY_DOWNGRADED) { Pike_error("Upgrade is not allowed for non-degraded mutex keys.\n"); } if(key->next) { if(threads_disabled) { /* Restore the key state. */ Pike_error("Cannot wait for mutexes when threads are disabled!\n"); } key->kind = KEY_PENDING; m->num_waiting++; do { THREADS_FPRINTF(1, "WAITING TO LOCK m:%p\n", m); SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter(& m->condition); SWAP_IN_CURRENT_THREAD(); check_threads_etc(); } while (key->next); m->num_waiting--; } key->kind = KEY_EXCLUSIVE; }
0c56d52021-07-23Henrik Grubbström (Grubba) /*! @decl int(0..1) try_upgrade() *! *! Try to upgrade a downgraded lock into an exclusive lock. *! *! @returns *! @int *! @value 0 *! Returns @expr{0@} (zero) if there are other concurrent *! shared locks active. *! @value 1 *! Returns @expr{1@} if this is the only active lock, and *! upgrading it to being exclusive succeeded. *! @endint *! *! @seealso *! @[downgrade()], @[upgrade()] */
a94a082020-07-02Henrik Grubbström (Grubba) static void f_mutex_key_try_upgrade(INT32 args) { struct key_storage *key = THIS_KEY; if (key->kind >= KEY_PENDING) { push_int(1); return; } if (key->kind != KEY_DOWNGRADED) { Pike_error("Upgrade is not allowed for non-degraded mutex keys.\n"); } if (key->next) { push_int(0); return; } key->kind = KEY_EXCLUSIVE; push_int(1); }
ca85822019-08-21Henrik Grubbström (Grubba) /*! @endclass */
cff4172020-06-13Henrik Grubbström (Grubba) enum rwmux_kind { RWMUX_NONE = 0,
3d1d6e2020-06-23Henrik Grubbström (Grubba)  RWMUX_TRY_READ = RWMUX_NONE,
cff4172020-06-13Henrik Grubbström (Grubba)  RWMUX_READ,
e944042020-06-14Henrik Grubbström (Grubba)  RWMUX_DOWNGRADED,
3d1d6e2020-06-23Henrik Grubbström (Grubba)  RWMUX_TRY_WRITE = RWMUX_DOWNGRADED,
1adec62020-12-09Henrik Grubbström (Grubba)  RWMUX_UPGRADING,
cff4172020-06-13Henrik Grubbström (Grubba)  RWMUX_WRITE, };
ca85822019-08-21Henrik Grubbström (Grubba) struct rwmutex_key_storage {
1adec62020-12-09Henrik Grubbström (Grubba)  struct object *self; /* NOT ref-counted! */
ca85822019-08-21Henrik Grubbström (Grubba)  struct rwmutex_storage *rwmutex; struct object *rwmutex_obj; struct thread_state *owner; struct object *owner_obj; struct rwmutex_key_storage *prev; struct rwmutex_key_storage *next;
cff4172020-06-13Henrik Grubbström (Grubba)  enum rwmux_kind kind;
ca85822019-08-21Henrik Grubbström (Grubba) }; /*! @class RWMutex *! *! Read-Write Mutex. *! *! A mutex that allows for a single writer (exclusive locking) *! or multiple readers (shared locking). *! *! @seealso *! @[Mutex] */ struct rwmutex_storage { COND_T condition;
e944042020-06-14Henrik Grubbström (Grubba)  int readers; /* Number of read locks. */ int writers; /* Write lock state. * -1 Downgraded write lock exists. * 0 No write lock exists. * 1 Write lock is pending. * 2 Write lock exists. */
3555842020-06-21Henrik Grubbström (Grubba)  struct rwmutex_key_storage *key;
ca85822019-08-21Henrik Grubbström (Grubba) }; #define THIS_RWMUTEX ((struct rwmutex_storage *)Pike_fp->current_storage) static void init_rwmutex_obj(struct object *o) { co_init(&THIS_RWMUTEX->condition); } static void exit_rwmutex_obj(struct object *o) { co_destroy(&THIS_RWMUTEX->condition); }
cff4172020-06-13Henrik Grubbström (Grubba) /*! @decl RWKey read_lock()
ca85822019-08-21Henrik Grubbström (Grubba)  *! *! Obtain a lock for reading (shared lock). *!
f86a3f2019-10-16Henrik Grubbström (Grubba)  *! @throws *! Throws an error if the current thread already has
0e4ebd2020-06-12Henrik Grubbström (Grubba)  *! a lock (read or write).
f86a3f2019-10-16Henrik Grubbström (Grubba)  *!
ca85822019-08-21Henrik Grubbström (Grubba)  *! @seealso
3d1d6e2020-06-23Henrik Grubbström (Grubba)  *! @[try_read_lock()], @[write_lock()]
ca85822019-08-21Henrik Grubbström (Grubba)  */ static void f_rwmutex_read_lock(INT32 args) { ref_push_object_inherit(Pike_fp->current_object, Pike_fp->context - Pike_fp->current_object->prog->inherits);
cff4172020-06-13Henrik Grubbström (Grubba)  push_int(RWMUX_READ); push_object(clone_object(rwmutex_key_program, 2));
ca85822019-08-21Henrik Grubbström (Grubba) }
3d1d6e2020-06-23Henrik Grubbström (Grubba) /*! @decl RWKey try_read_lock() *! *! Attempt to obtain a lock for reading (shared lock). *! *! @throws *! Throws an error if the current thread already has *! a lock (read or write). *! *! @seealso *! @[read_lock()], @[write_lock()] */ static void f_rwmutex_try_read_lock(INT32 args) { ref_push_object_inherit(Pike_fp->current_object, Pike_fp->context - Pike_fp->current_object->prog->inherits); push_int(RWMUX_TRY_READ); push_object(clone_object(rwmutex_key_program, 2)); }
cff4172020-06-13Henrik Grubbström (Grubba) /*! @decl RWKey write_lock()
ca85822019-08-21Henrik Grubbström (Grubba)  *! *! Obtain a lock for writing (exclusive lock). *!
f86a3f2019-10-16Henrik Grubbström (Grubba)  *! @throws *! Throws an error if the current thread already has
0e4ebd2020-06-12Henrik Grubbström (Grubba)  *! a lock (read or write).
f86a3f2019-10-16Henrik Grubbström (Grubba)  *!
ca85822019-08-21Henrik Grubbström (Grubba)  *! @seealso
3d1d6e2020-06-23Henrik Grubbström (Grubba)  *! @[read_lock()], @[try_read_lock()], @[try_write_lock()]
ca85822019-08-21Henrik Grubbström (Grubba)  */ static void f_rwmutex_write_lock(INT32 args) { ref_push_object_inherit(Pike_fp->current_object, Pike_fp->context - Pike_fp->current_object->prog->inherits);
cff4172020-06-13Henrik Grubbström (Grubba)  push_int(RWMUX_WRITE); push_object(clone_object(rwmutex_key_program, 2));
ca85822019-08-21Henrik Grubbström (Grubba) }
3d1d6e2020-06-23Henrik Grubbström (Grubba) /*! @decl RWKey try_write_lock() *! *! Attempt to obtain a lock for writing (exclusive lock). *! *! @throws *! Throws an error if the current thread already has *! a lock (read or write). *! *! @seealso *! @[read_lock()], @[try_read_lock()], @[write_lock()] */ static void f_rwmutex_try_write_lock(INT32 args) { ref_push_object_inherit(Pike_fp->current_object, Pike_fp->context - Pike_fp->current_object->prog->inherits); push_int(RWMUX_TRY_WRITE); push_object(clone_object(rwmutex_key_program, 2)); }
1adec62020-12-09Henrik Grubbström (Grubba) /*! @decl array(Thread.Thread) current_locking_threads() *! *! Return an array with the treads that are holding *! keys to the mutex. *! *! @seealso *! @[current_locking_threads()] */ static void f_rwmutex_current_locking_threads(INT32 args) { struct rwmutex_storage *m = THIS_RWMUTEX; struct rwmutex_key_storage *key; int count = 0; for (key = m->key; key; key = key->next) { if (key->owner_obj && key->owner_obj->prog) { ref_push_object(key->owner_obj); count++; } } f_aggregate(count); } /*! @decl array(Thread.Thread) current_locking_keys() *! *! Return an array with the currently active keys *! to the mutex. *! *! @seealso *! @[current_locking_threads()] */ static void f_rwmutex_current_locking_keys(INT32 args) { struct rwmutex_storage *m = THIS_RWMUTEX; struct rwmutex_key_storage *key; int count = 0; for (key = m->key; key; key = key->next) { if (key->self && key->self->prog) { ref_push_object(key->self); count++; } } f_aggregate(count); }
ca85822019-08-21Henrik Grubbström (Grubba) /*! @decl string _sprintf(int c) *! *! Describes the mutex including the thread(s) that currently hold *! locks (if any). */ static void f_rwmutex__sprintf(INT32 args) { struct rwmutex_storage *m = THIS_RWMUTEX; int c = 0; struct rwmutex_key_storage *key; struct svalue *save_sp; 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; } save_sp = Pike_sp; push_static_text("Thread.RWMutex(/* r"); push_int(m->readers); push_static_text("/w"); push_int(m->writers);
3555842020-06-21Henrik Grubbström (Grubba)  if (m->key) {
ca85822019-08-21Henrik Grubbström (Grubba)  push_static_text(" Locked by");
3555842020-06-21Henrik Grubbström (Grubba)  for (key = m->key; key; key = key->next) {
ca85822019-08-21Henrik Grubbström (Grubba)  if (key->owner) { if (!c) { push_static_text(","); } push_static_text(" 0x"); push_int64(PTR_TO_INT(THREAD_T_TO_PTR(key->owner->id))); f_int2hex(1); c = 0; } } } push_static_text(" */)"); f_add(Pike_sp - save_sp); } /*! @endclass */
1adec62020-12-09Henrik Grubbström (Grubba) /*! @class RWKey */
ca85822019-08-21Henrik Grubbström (Grubba) #define THIS_RWKEY ((struct rwmutex_key_storage *)(CURRENT_STORAGE)) static void init_rwmutex_key_obj(struct object *UNUSED(o)) {
1adec62020-12-09Henrik Grubbström (Grubba)  /* NB: This function is also called from create(), so it * needs to support being called multiple times on * the same storage. */ THIS_RWKEY->self = Pike_fp->current_object;
ca85822019-08-21Henrik Grubbström (Grubba)  if (THIS_RWKEY->owner_obj) { free_object(THIS_RWKEY->owner_obj); } THIS_RWKEY->owner = Pike_interpreter.thread_state; THIS_RWKEY->owner_obj = Pike_interpreter.thread_state->thread_obj; if (THIS_RWKEY->owner_obj) add_ref(THIS_RWKEY->owner_obj);
cff4172020-06-13Henrik Grubbström (Grubba)  THIS_RWKEY->kind = RWMUX_NONE;
ca85822019-08-21Henrik Grubbström (Grubba) }
cff4172020-06-13Henrik Grubbström (Grubba) static void exit_rwmutex_key_obj(struct object *UNUSED(o))
ca85822019-08-21Henrik Grubbström (Grubba) { if (THIS_RWKEY->rwmutex) { struct rwmutex_storage *rwmutex = THIS_RWKEY->rwmutex; struct object *rwmutex_obj = THIS_RWKEY->rwmutex_obj; THIS_RWKEY->rwmutex = NULL; THIS_RWKEY->rwmutex_obj = NULL; if (THIS_RWKEY->owner_obj) { free_object(THIS_RWKEY->owner_obj); } THIS_RWKEY->owner = NULL; THIS_RWKEY->owner_obj = NULL; /* Unlink */ if (THIS_RWKEY->prev) { THIS_RWKEY->prev->next = THIS_RWKEY->next; } else {
3555842020-06-21Henrik Grubbström (Grubba)  rwmutex->key = THIS_RWKEY->next;
ca85822019-08-21Henrik Grubbström (Grubba)  } if (THIS_RWKEY->next) { THIS_RWKEY->next->prev = THIS_RWKEY->prev; } THIS_RWKEY->next = NULL; THIS_RWKEY->prev = NULL;
cff4172020-06-13Henrik Grubbström (Grubba)  THIS_RWKEY->rwmutex = NULL;
ca85822019-08-21Henrik Grubbström (Grubba) 
cff4172020-06-13Henrik Grubbström (Grubba)  switch(THIS_RWKEY->kind) { case RWMUX_NONE: break; case RWMUX_READ: rwmutex->readers--; break;
e944042020-06-14Henrik Grubbström (Grubba)  case RWMUX_DOWNGRADED: rwmutex->writers = 0; rwmutex->readers--; break;
cff4172020-06-13Henrik Grubbström (Grubba)  case RWMUX_WRITE:
e944042020-06-14Henrik Grubbström (Grubba)  rwmutex->writers = 0;
cff4172020-06-13Henrik Grubbström (Grubba)  break;
ca85822019-08-21Henrik Grubbström (Grubba)  }
cff4172020-06-13Henrik Grubbström (Grubba)  THIS_RWKEY->kind = RWMUX_NONE; co_broadcast(&rwmutex->condition);
ca85822019-08-21Henrik Grubbström (Grubba)  if (rwmutex_obj) { free_object(rwmutex_obj); }
cff4172020-06-13Henrik Grubbström (Grubba)  /* Attempt to force a thread switch in case there is * anyone waiting on us. */ THREADS_ALLOW(); #ifdef HAVE_NO_YIELD sleep(0); #else th_yield(); #endif THREADS_DISALLOW();
ca85822019-08-21Henrik Grubbström (Grubba)  } }
cff4172020-06-13Henrik Grubbström (Grubba) static void f_rwmutex_key_create(INT32 args)
ca85822019-08-21Henrik Grubbström (Grubba) { struct object *rwmutex_obj = NULL; struct rwmutex_storage *rwmutex = NULL;
0817a52019-10-14Henrik Grubbström (Grubba)  struct rwmutex_key_storage *m;
cff4172020-06-13Henrik Grubbström (Grubba)  enum rwmux_kind kind = RWMUX_READ;
f86a3f2019-10-16Henrik Grubbström (Grubba)  int self_count = 0;
ca85822019-08-21Henrik Grubbström (Grubba) 
cff4172020-06-13Henrik Grubbström (Grubba)  get_all_args("create", args, "%o.%d", &rwmutex_obj, &kind);
ca85822019-08-21Henrik Grubbström (Grubba)  if (!(rwmutex = get_storage(rwmutex_obj, rwmutex_program))) { SIMPLE_ARG_TYPE_ERROR("create", 1, "RWMutex"); }
f86a3f2019-10-16Henrik Grubbström (Grubba)  while (THIS_RWKEY->rwmutex) { /* Not really a supported operation, but... */
cff4172020-06-13Henrik Grubbström (Grubba)  exit_rwmutex_key_obj(NULL);
f86a3f2019-10-16Henrik Grubbström (Grubba)  }
3555842020-06-21Henrik Grubbström (Grubba)  for (m = rwmutex->key; m; m = m->next) {
0817a52019-10-14Henrik Grubbström (Grubba)  if ((m->owner == Pike_interpreter.thread_state) && (m->owner)) {
f86a3f2019-10-16Henrik Grubbström (Grubba)  self_count++;
ca85822019-08-21Henrik Grubbström (Grubba)  } }
0e4ebd2020-06-12Henrik Grubbström (Grubba)  if (self_count) {
f86a3f2019-10-16Henrik Grubbström (Grubba)  /* NB: We have a lock already. * * This is a bad idea, as it may lead to dead-locks * if another thread has started an attempt to get * a write lock between our two read locks, or if * we already hold the write lock. */ Pike_error("Recursive mutex locks!\n");
ca85822019-08-21Henrik Grubbström (Grubba)  } init_rwmutex_key_obj(NULL); THIS_RWKEY->rwmutex = rwmutex; THIS_RWKEY->rwmutex_obj = rwmutex_obj; add_ref(rwmutex_obj); /* Link */ THIS_RWKEY->prev = NULL;
3555842020-06-21Henrik Grubbström (Grubba)  if ((THIS_RWKEY->next = rwmutex->key)) { rwmutex->key->prev = THIS_RWKEY;
ca85822019-08-21Henrik Grubbström (Grubba)  }
3555842020-06-21Henrik Grubbström (Grubba)  rwmutex->key = THIS_RWKEY;
ca85822019-08-21Henrik Grubbström (Grubba) 
cff4172020-06-13Henrik Grubbström (Grubba)  THIS_RWKEY->kind = RWMUX_NONE; /* No lock taken (yet). */ switch(kind) {
3d1d6e2020-06-23Henrik Grubbström (Grubba)  case RWMUX_TRY_READ: if (rwmutex->writers > 0) { /* There's a write lock. -- Attempt in try mode failed. */ destruct_object(Pike_fp->current_object, DESTRUCT_EXPLICIT); break; } rwmutex->readers++; THIS_RWKEY->kind = RWMUX_READ; break;
cff4172020-06-13Henrik Grubbström (Grubba)  case RWMUX_READ: /* States: * * readers writers self_count Action
e944042020-06-14Henrik Grubbström (Grubba)  * - <=0 - Add reader. * (No active writers.) * 0 1 0 Wait for writers to go to 0.
cff4172020-06-13Henrik Grubbström (Grubba)  * (Some other thread is attempting to * take a write lock.)
e944042020-06-14Henrik Grubbström (Grubba)  * >0 1 >0 Add reader.
cff4172020-06-13Henrik Grubbström (Grubba)  * (We already have a lock, which the other * thread is waiting for, so avoid dead lock.)
e944042020-06-14Henrik Grubbström (Grubba)  * - >1 0 Wait for writers to go to 0.
cff4172020-06-13Henrik Grubbström (Grubba)  * (Some other thread has a write lock.)
e944042020-06-14Henrik Grubbström (Grubba)  * - >1 >0 Add reader.
cff4172020-06-13Henrik Grubbström (Grubba)  * (As we have a lock and there is at least * one write lock, we must be the owners * of the write lock.) */
f86a3f2019-10-16Henrik Grubbström (Grubba)  SWAP_OUT_CURRENT_THREAD();
e944042020-06-14Henrik Grubbström (Grubba)  while(rwmutex->writers > 0) {
f86a3f2019-10-16Henrik Grubbström (Grubba)  co_wait_interpreter(&rwmutex->condition); } SWAP_IN_CURRENT_THREAD();
ca85822019-08-21Henrik Grubbström (Grubba) 
cff4172020-06-13Henrik Grubbström (Grubba)  rwmutex->readers++; THIS_RWKEY->kind = RWMUX_READ; break;
ca85822019-08-21Henrik Grubbström (Grubba) 
3d1d6e2020-06-23Henrik Grubbström (Grubba)  case RWMUX_TRY_WRITE: if (rwmutex->writers || rwmutex->readers) { /* There's a (degraded) write lock or a read lock. * -- Attempt in try mode failed. */ destruct_object(Pike_fp->current_object, DESTRUCT_EXPLICIT); break; } rwmutex->writers = 2; THIS_RWKEY->kind = RWMUX_WRITE; break;
cff4172020-06-13Henrik Grubbström (Grubba)  case RWMUX_WRITE: /* States: * * readers writers self_count Action
e944042020-06-14Henrik Grubbström (Grubba)  * - 0 - Set writers to 1, wait for readers * to go to 0, Set writers to 2.
cff4172020-06-13Henrik Grubbström (Grubba)  * (No write lock active.) * - -1 0 Wait for writers to go to 0, then as above.
e944042020-06-14Henrik Grubbström (Grubba)  * (Some other thread has a degraded write * lock.) * - 1 0 Wait for writers to go to 0, then as above.
cff4172020-06-13Henrik Grubbström (Grubba)  * (Some other thread is attempting to * get a write lock.)
e944042020-06-14Henrik Grubbström (Grubba)  * - >1 0 Wait for writers to go to 0, then as above.
cff4172020-06-13Henrik Grubbström (Grubba)  * (Some other thread has a write lock.)
e944042020-06-14Henrik Grubbström (Grubba)  * - >1 >0 Add writer.
cff4172020-06-13Henrik Grubbström (Grubba)  * (As we have a lock and there is at least * one write lock, we must be the owners * of the write lock.) * * Problem:
e944042020-06-14Henrik Grubbström (Grubba)  * >0 1 >0 (Some other thread is attempting to get
cff4172020-06-13Henrik Grubbström (Grubba)  * a write lock (waiting on us). If * self_count == readers, then we could * take the write lock, complete and then * restore the -1, but if the other thread * also has a self_count >0 we run into * a dead lock.)
f86a3f2019-10-16Henrik Grubbström (Grubba)  */
ca85822019-08-21Henrik Grubbström (Grubba) 
cff4172020-06-13Henrik Grubbström (Grubba)  SWAP_OUT_CURRENT_THREAD();
f86a3f2019-10-16Henrik Grubbström (Grubba)  while(rwmutex->writers) { co_wait_interpreter(&rwmutex->condition); }
ca85822019-08-21Henrik Grubbström (Grubba) 
e944042020-06-14Henrik Grubbström (Grubba)  rwmutex->writers = 1;
ca85822019-08-21Henrik Grubbström (Grubba) 
cff4172020-06-13Henrik Grubbström (Grubba)  while(rwmutex->readers) {
f86a3f2019-10-16Henrik Grubbström (Grubba)  co_wait_interpreter(&rwmutex->condition); }
ca85822019-08-21Henrik Grubbström (Grubba) 
e944042020-06-14Henrik Grubbström (Grubba)  rwmutex->writers = 2;
cff4172020-06-13Henrik Grubbström (Grubba)  SWAP_IN_CURRENT_THREAD(); THIS_RWKEY->kind = RWMUX_WRITE; break;
f86a3f2019-10-16Henrik Grubbström (Grubba)  }
ca85822019-08-21Henrik Grubbström (Grubba) }
e944042020-06-14Henrik Grubbström (Grubba) /*! @decl void downgrade() *! *! Downgrade this key into a (temporary) read-lock. *! *! This allows for concurrent read operations. This is *! a no-op for keys that aren't write-locks. *! *! If the key was a write-lock, it may later be upgraded *! back into a write-lock. *! *! @seealso *! @[upgrade()] */ static void f_rwmutex_key_downgrade(INT32 args) { if (THIS_RWKEY->kind <= RWMUX_DOWNGRADED) { /* Already a read lock or downgraded. */ return; } THIS_RWKEY->kind = RWMUX_DOWNGRADED; THIS_RWKEY->rwmutex->readers = 1; THIS_RWKEY->rwmutex->writers = -1;
81770b2020-06-16Henrik Grubbström (Grubba)  co_broadcast(&THIS_RWKEY->rwmutex->condition);
e944042020-06-14Henrik Grubbström (Grubba) } /*! @decl void upgrade() *! *! Upgrade this key back into a write-lock. *! *! This operation is only allowed on keys that have started *! out as write-locks. *! *! This is a no-op on keys that already are write-locks. *! *! For a downgraded write-lock, this operation will block *! until all concurrent read-locks are released. *! *! @throws *! Throws an error for keys that didn't start out as write-locks. *! *! @seealso
3d1d6e2020-06-23Henrik Grubbström (Grubba)  *! @[try_upgrade()], @[downgrade()]
e944042020-06-14Henrik Grubbström (Grubba)  */ static void f_rwmutex_key_upgrade(INT32 args) { struct rwmutex_storage *rwmutex = NULL; if (THIS_RWKEY->kind < RWMUX_DOWNGRADED) { Pike_error("Unsupported operation -- not a (degraded) write lock.\n"); } if (THIS_RWKEY->kind == RWMUX_WRITE) { /* Already upgraded. */ return; } THIS_RWKEY->kind = RWMUX_NONE; rwmutex = THIS_RWKEY->rwmutex; rwmutex->readers--; rwmutex->writers = 1; SWAP_OUT_CURRENT_THREAD(); while(rwmutex->readers) { co_wait_interpreter(&rwmutex->condition); } SWAP_IN_CURRENT_THREAD(); rwmutex->writers = 2; THIS_RWKEY->kind = RWMUX_WRITE; }
3d1d6e2020-06-23Henrik Grubbström (Grubba) /*! @decl int(0..1) try_upgrade() *! *! Attempt to upgrade this key back into a write-lock. *! *! This operation is only allowed on keys that have started *! out as write-locks. *! *! This is a no-op on keys that already are write-locks, in which *! case @expr{1@} will be returned. *! *! For a downgraded write-lock, this operation will upgrade the *! key into a write-lock if there are no active read locks. *! *! @throws *! Throws an error for keys that didn't start out as write-locks. *! *! @returns *! Returns @expr{1@} if the key is now a write-lock, and *! @expr{0@} (zero) if it is (still) a read-lock. *! *! @seealso *! @[upgrade()], @[downgrade()] */ static void f_rwmutex_key_try_upgrade(INT32 args) { struct rwmutex_storage *rwmutex = NULL; if (THIS_RWKEY->kind < RWMUX_DOWNGRADED) { Pike_error("Unsupported operation -- not a (degraded) write lock.\n"); } if (THIS_RWKEY->kind == RWMUX_DOWNGRADED) { rwmutex = THIS_RWKEY->rwmutex; if (rwmutex->readers != 1) { /* There are other readers active. */ push_int(0); return; } rwmutex->readers = 0; rwmutex->writers = 2; THIS_RWKEY->kind = RWMUX_WRITE; } push_int(1); }
cff4172020-06-13Henrik Grubbström (Grubba) static void f_rwmutex_key__sprintf(INT32 args)
ca85822019-08-21Henrik Grubbström (Grubba) { int c = 0; get_all_args("_sprintf", args, "%d", &c); if (c != 'O') { push_undefined(); return; } if (THIS_RWKEY->rwmutex_obj) {
cff4172020-06-13Henrik Grubbström (Grubba)  push_text("Thread.RWKey(/* %O, %s */)");
ca85822019-08-21Henrik Grubbström (Grubba)  ref_push_object(THIS_RWKEY->rwmutex_obj);
cff4172020-06-13Henrik Grubbström (Grubba)  switch(THIS_RWKEY->kind) { case RWMUX_NONE: push_text("Unlocked"); break; case RWMUX_READ: push_text("Read-lock"); break;
e944042020-06-14Henrik Grubbström (Grubba)  case RWMUX_DOWNGRADED: push_text("Downgraded-write-lock"); break;
cff4172020-06-13Henrik Grubbström (Grubba)  case RWMUX_WRITE: push_text("Write-lock"); break; } f_sprintf(3);
ca85822019-08-21Henrik Grubbström (Grubba)  } else {
cff4172020-06-13Henrik Grubbström (Grubba)  push_text("Thread.RWKey(/* Unlocked */)");
ca85822019-08-21Henrik Grubbström (Grubba)  } }
192ceb2003-11-22Henrik Grubbström (Grubba) /*! @endclass */
e413da2001-02-01Henrik Grubbström (Grubba) /*! @class Condition *! *! Implementation of condition variables. *! *! Condition variables are used by threaded programs *! to wait for events happening in other threads. *!
1520f12018-12-07Stephen R. van den Berg  *! In order to prevent races (which is the whole point of condition *! variables), the modification of a shared resource and the signal notifying *! modification of the resource must be performed inside the same mutex *! lock, and the examining of the resource and waiting for a signal that *! the resource has changed based on that examination must also happen *! inside the same mutex lock. *! *! Typical wait operation: *! @ol
ac0b6f2020-12-15H William Welliver  *! @item *! Take mutex lock *! @item *! Read/write shared resource *! @item *! Wait for the signal with the mutex lock in released state *! @item *! Reacquire mutex lock *! @item *! If needed, jump back to step 2 again *! @item *! Release mutex lock
1520f12018-12-07Stephen R. van den Berg  *! @endol *! *! Typical signal operation: *! @ol
ac0b6f2020-12-15H William Welliver  *! @item *! Take mutex lock *! @item *! Read/write shared resource *! @item *! Send signal *! @item *! Release mutex lock
1520f12018-12-07Stephen R. van den Berg  *! @endol *! *! @example *! You have some resource that multiple treads want to use. To *! protect this resource for simultaneous access, you create a shared mutex. *! Before you read or write the resource, you take the mutex so that you *! get a consistent and private view of / control over it. When you decide *! that the resource is not in the state you want it, and that you need *! to wait for some other thread to modify the state for you before you *! can continue, you wait on the conditional variable, which will *! temporarily relinquish the mutex during the wait. This way a *! different thread can take the mutex, update the state of the resource, *! and then signal the condition (which does not in itself release the *! mutex, but the signalled thread will be next in line once the mutex is *! released). *!
e413da2001-02-01Henrik Grubbström (Grubba)  *! @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
c7b7dd2001-10-28Martin Nilsson  *! @[Mutex]
e413da2001-02-01Henrik Grubbström (Grubba)  */
8349b42006-01-29Henrik Grubbström (Grubba) struct pike_cond { COND_T cond; int wait_count;
5c2f2f2018-09-16Henrik Grubbström (Grubba)  struct object *mutex_obj;
8349b42006-01-29Henrik Grubbström (Grubba) };
b0c72a2006-01-30Henrik Grubbström (Grubba) #define THIS_COND ((struct pike_cond *)(CURRENT_STORAGE))
8349b42006-01-29Henrik Grubbström (Grubba) 
5c2f2f2018-09-16Henrik Grubbström (Grubba) /*! @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); } } } }
4a59c02002-09-30Henrik Grubbström (Grubba) /*! @decl void wait(Thread.MutexKey mutex_key)
4a5f542009-01-25Henrik Grubbström (Grubba)  *! @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)
e413da2001-02-01Henrik Grubbström (Grubba)  *!
2ef3b92005-01-25Henrik Grubbström (Grubba)  *! Wait for condition.
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! This function makes the current thread sleep until the condition
4a5f542009-01-25Henrik Grubbström (Grubba)  *! variable is signalled or the timeout is reached. *! *! @param mutex_key *! A @[Thread.MutexKey] object for a @[Thread.Mutex]. It will be *! unlocked atomically before waiting for the signal and then *! relocked atomically when the signal is received or the timeout *! is reached. *! *! @param seconds *! Seconds to wait before the timeout is reached. *! *! @param nanos *! Nano (1/1000000000) seconds to wait before the timeout is reached. *! This value is added to the number of seconds specified by @[seconds]. *! *! A timeout of zero seconds disables the timeout.
2702952004-04-21Martin Stjernholm  *! *! The thread that sends the signal should have the mutex locked *! while sending it. Otherwise it's impossible to avoid races where *! signals are sent while the listener(s) haven't arrived to the *! @[wait] calls yet.
e413da2001-02-01Henrik Grubbström (Grubba)  *!
4a59c02002-09-30Henrik Grubbström (Grubba)  *! @note
4a5f542009-01-25Henrik Grubbström (Grubba)  *! 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
8349b42006-01-29Henrik Grubbström (Grubba)  *! Note also that any threads waiting on the condition will be
4a5f542009-01-25Henrik Grubbström (Grubba)  *! woken up when it gets destructed.
8349b42006-01-29Henrik Grubbström (Grubba)  *!
e413da2001-02-01Henrik Grubbström (Grubba)  *! @seealso
c7b7dd2001-10-28Martin Nilsson  *! @[Mutex->lock()]
e413da2001-02-01Henrik Grubbström (Grubba)  */
07513e1996-10-04Fredrik Hübinette (Hubbe) void f_cond_wait(INT32 args) {
d461912020-06-26Henrik Grubbström (Grubba)  struct object *key_obj, *mutex_obj;
4a59c02002-09-30Henrik Grubbström (Grubba)  struct mutex_storage *mut;
d461912020-06-26Henrik Grubbström (Grubba)  struct key_storage *key;
8349b42006-01-29Henrik Grubbström (Grubba)  struct pike_cond *c;
4a5f542009-01-25Henrik Grubbström (Grubba)  INT_TYPE seconds = 0, nanos = 0;
07513e1996-10-04Fredrik Hübinette (Hubbe) 
1c3c3d2000-03-24Henrik Grubbström (Grubba)  if(threads_disabled)
b2d3e42000-12-01Fredrik Hübinette (Hubbe)  Pike_error("Cannot wait for conditions when threads are disabled!\n");
4a59c02002-09-30Henrik Grubbström (Grubba) 
4a5f542009-01-25Henrik Grubbström (Grubba)  if (args <= 2) { FLOAT_TYPE fsecs = 0.0;
d461912020-06-26Henrik Grubbström (Grubba)  get_all_args(NULL, args, "%o.%F", &key_obj, &fsecs);
4a5f542009-01-25Henrik Grubbström (Grubba)  seconds = (INT_TYPE) fsecs; nanos = (INT_TYPE)((fsecs - seconds)*1000000000); } else {
078c6d2010-10-10Martin Stjernholm  /* FIXME: Support bignum nanos. */
d461912020-06-26Henrik Grubbström (Grubba)  get_all_args(NULL, args, "%o%i%i", &key_obj, &seconds, &nanos);
4a5f542009-01-25Henrik Grubbström (Grubba)  }
13670c2015-05-25Martin Nilsson 
5c2f2f2018-09-16Henrik Grubbström (Grubba)  c = THIS_COND;
d461912020-06-26Henrik Grubbström (Grubba)  if ((key_obj->prog != mutex_key) || (!((key = OB2KEY(key_obj))->kind)) || (!(mut = key->mut)) || (c->mutex_obj && key->mutex_obj && (key->mutex_obj != c->mutex_obj))) {
69c0aa2014-08-25Martin Nilsson  Pike_error("Bad argument 1 to wait()\n");
4a59c02002-09-30Henrik Grubbström (Grubba)  }
4781e52020-06-28Henrik Grubbström (Grubba)  if (key->kind != KEY_EXCLUSIVE) { Pike_error("Bad argument 1 to wait()\n"); }
1c3c3d2000-03-24Henrik Grubbström (Grubba)  if(args > 1) { pop_n_elems(args - 1); args = 1; }
07513e1996-10-04Fredrik Hübinette (Hubbe) 
4a59c02002-09-30Henrik Grubbström (Grubba)  /* Unlock mutex */
d461912020-06-26Henrik Grubbström (Grubba)  mutex_obj = key->mutex_obj;
35fa442020-06-27Henrik Grubbström (Grubba)  if (key->prev) { if ((key->prev->next = key->next)) { key->next->prev = key->prev; } key->prev = NULL; } else { if ((mut->key = key->next)) { key->next->prev = NULL; } } key->next = NULL;
d461912020-06-26Henrik Grubbström (Grubba)  key->mut = NULL; key->mutex_obj = NULL;
35fa442020-06-27Henrik Grubbström (Grubba)  key->kind = KEY_INITIALIZED; co_broadcast(& mut->condition);
13670c2015-05-25Martin Nilsson 
7685592019-09-06Henrik Grubbström (Grubba)  if (!c->mutex_obj) { /* Lock the condition to the first mutex it is used together with. */ c->mutex_obj = mutex_obj; add_ref(mutex_obj); }
4a59c02002-09-30Henrik Grubbström (Grubba)  /* Wait and allow mutex operations */
1c3c3d2000-03-24Henrik Grubbström (Grubba)  SWAP_OUT_CURRENT_THREAD();
8349b42006-01-29Henrik Grubbström (Grubba)  c->wait_count++;
4a5f542009-01-25Henrik Grubbström (Grubba)  if (seconds || nanos) { co_wait_interpreter_timeout(&(c->cond), seconds, nanos); } else { co_wait_interpreter(&(c->cond)); }
8349b42006-01-29Henrik Grubbström (Grubba)  c->wait_count--;
1c3c3d2000-03-24Henrik Grubbström (Grubba)  SWAP_IN_CURRENT_THREAD();
13670c2015-05-25Martin Nilsson 
8af5622016-08-09Henrik Grubbström (Grubba)  if (!mutex_obj->prog) { Pike_error("Mutex was destructed while waiting for cond.\n"); }
4a59c02002-09-30Henrik Grubbström (Grubba)  /* Lock mutex */
d059be2004-04-23Martin Stjernholm  mut->num_waiting++;
35fa442020-06-27Henrik Grubbström (Grubba)  { key->kind = KEY_PENDING; if ((key->next = mut->key)) { key->next->prev = key; } mut->key = key; key->mut = mut; key->mutex_obj = mutex_obj; while(key->next) { SWAP_OUT_CURRENT_THREAD(); co_wait_interpreter(& mut->condition); SWAP_IN_CURRENT_THREAD(); check_threads_etc(); } key->kind = KEY_EXCLUSIVE;
4a59c02002-09-30Henrik Grubbström (Grubba)  }
f72d412004-04-23Henrik Grubbström (Grubba)  mut->num_waiting--;
d059be2004-04-23Martin Stjernholm 
4a59c02002-09-30Henrik Grubbström (Grubba)  pop_stack(); return;
07513e1996-10-04Fredrik Hübinette (Hubbe) }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl void signal() *! *! @[signal()] wakes up one of the threads currently waiting for the *! condition. *!
a9cdcf2001-02-06Henrik Grubbström (Grubba)  *! @note
e413da2001-02-01Henrik Grubbström (Grubba)  *! Sometimes more than one thread is woken up. *! *! @seealso *! @[broadcast()] */
96f63c2015-11-25Henrik Grubbström (Grubba) void f_cond_signal(INT32 UNUSED(args))
8349b42006-01-29Henrik Grubbström (Grubba) { co_signal(&(THIS_COND->cond)); }
e413da2001-02-01Henrik Grubbström (Grubba)  /*! @decl void broadcast() *! *! @[broadcast()] wakes up all threads currently waiting for this condition. *! *! @seealso *! @[signal()] */
96f63c2015-11-25Henrik Grubbström (Grubba) void f_cond_broadcast(INT32 UNUSED(args))
8349b42006-01-29Henrik Grubbström (Grubba) { co_broadcast(&(THIS_COND->cond)); }
e413da2001-02-01Henrik Grubbström (Grubba) 
508ef02019-09-06Henrik Grubbström (Grubba)  static void f_cond__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; } if (THIS_COND->mutex_obj) { push_static_text("Thread.Condition(/* %O */)"); ref_push_object(THIS_COND->mutex_obj); f_sprintf(2); } else { push_text("Thread.Condition(/* Unassociated */)"); } }
74dfe82012-12-30Jonas Walldén void init_cond_obj(struct object *UNUSED(o))
8349b42006-01-29Henrik Grubbström (Grubba) { co_init(&(THIS_COND->cond)); THIS_COND->wait_count = 0; }
36e67a2006-01-28Martin Stjernholm 
74dfe82012-12-30Jonas Walldén void exit_cond_obj(struct object *UNUSED(o))
36e67a2006-01-28Martin Stjernholm {
8349b42006-01-29Henrik Grubbström (Grubba)  /* 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 */ while (THIS_COND->wait_count) {
a20b1b2006-04-25David Hedbor  co_broadcast(&(THIS_COND->cond));
8349b42006-01-29Henrik Grubbström (Grubba)  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));
5c2f2f2018-09-16Henrik Grubbström (Grubba)  if (THIS_COND->mutex_obj) { free_object(THIS_COND->mutex_obj); THIS_COND->mutex_obj = NULL; }
36e67a2006-01-28Martin Stjernholm }
07513e1996-10-04Fredrik Hübinette (Hubbe) 
e413da2001-02-01Henrik Grubbström (Grubba) /*! @endclass */ /*! @class Thread */
8b34842019-02-26Henrik Grubbström (Grubba) /*! @decl array(mixed) backtrace(int|void flags)
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! Returns the current call stack for the thread. *!
8b34842019-02-26Henrik Grubbström (Grubba)  *! @param flags *! *! A bit mask of flags affecting generation of the backtrace. *! *! Currently a single flag is defined: *! @int *! @value 1 *! Return @[LiveBacktraceFrame]s. This flag causes the frame *! objects to track changes (as long as they are in use), and *! makes eg local variables for functions available for *! inspection or change. *! *! Note that since these values are "live", they may change or *! dissapear at any time unless the corresponding thread has *! been halted or similar. *! @endint *!
e413da2001-02-01Henrik Grubbström (Grubba)  *! @returns *! The result has the same format as for @[predef::backtrace()]. *! *! @seealso *! @[predef::backtrace()] */
a7fef41997-09-03Per Hedbor void f_thread_backtrace(INT32 args) {
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *foo = THIS_THREAD;
8b34842019-02-26Henrik Grubbström (Grubba)  int flags = 0;
a8e9892001-09-05Fredrik Hübinette (Hubbe) 
1d456f2003-02-20Henrik Grubbström (Grubba)  if(foo == Pike_interpreter.thread_state)
fad4ca2001-09-05Fredrik Hübinette (Hubbe)  {
8b34842019-02-26Henrik Grubbström (Grubba)  f_backtrace(args); return;
fad4ca2001-09-05Fredrik Hübinette (Hubbe)  }
8b34842019-02-26Henrik Grubbström (Grubba) 
c15e462019-05-29Stephen R. van den Berg  get_all_args("backtrace", args, ".%d", &flags);
8b34842019-02-26Henrik Grubbström (Grubba)  pop_n_elems(args); if(foo->state.stack_pointer)
a7fef41997-09-03Per Hedbor  {
8b34842019-02-26Henrik Grubbström (Grubba)  low_backtrace(& foo->state, flags);
fad4ca2001-09-05Fredrik Hübinette (Hubbe)  } else {
8b34842019-02-26Henrik Grubbström (Grubba)  ref_push_array(&empty_array);
a7fef41997-09-03Per Hedbor  } }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl int status()
6f13822006-04-07Martin Stjernholm  *! *! Returns the status of the thread. *! *! @returns *! @int
8678ad2006-08-09Martin Nilsson  *! @value Thread.THREAD_NOT_STARTED *! @value Thread.THREAD_RUNNING *! @value Thread.THREAD_EXITED
d273ca2014-12-07Henrik Grubbström (Grubba)  *! @value Thread.THREAD_ABORTED
6f13822006-04-07Martin Stjernholm  *! @endint
e413da2001-02-01Henrik Grubbström (Grubba)  */
5740881998-01-01Fredrik Hübinette (Hubbe) void f_thread_id_status(INT32 args) { pop_n_elems(args); push_int(THIS_THREAD->status); }
19330c2020-06-20Stephen R. van den Berg /*! @decl int gethrvtime (void|int nsec) *! *! @returns *! The CPU time consumed by this thread. *! *! @seealso *! @[predef::gethrvtime()] */ void f_thread_id_gethrvtime(INT32 args) { #ifdef CPU_TIME_MIGHT_BE_THREAD_LOCAL thread_gethrvtime(THIS_THREAD, args); #else pop_n_elems(args); push_int(0); #endif }
30c0612008-06-29Martin Stjernholm /*! @decl protected string _sprintf(int c)
e413da2001-02-01Henrik Grubbström (Grubba)  *!
5e50ef2001-09-25Henrik Grubbström (Grubba)  *! Returns a string identifying the thread.
e413da2001-02-01Henrik Grubbström (Grubba)  */
9a0d422000-02-06Martin Stjernholm void f_thread_id__sprintf (INT32 args) {
e6dbc22002-11-29Marcus Comstedt  int c = 0;
017b572011-10-28Henrik Grubbström (Grubba)  if(args>0 && TYPEOF(Pike_sp[-args]) == PIKE_T_INT)
e6dbc22002-11-29Marcus Comstedt  c = Pike_sp[-args].u.integer;
9a0d422000-02-06Martin Stjernholm  pop_n_elems (args);
e6dbc22002-11-29Marcus Comstedt  if(c != 'O') { push_undefined(); return; }
8271012016-12-20Jonas Walldén  push_static_text ("Thread.Thread(0x");
d2361e2003-06-30Martin Stjernholm  push_int64(PTR_TO_INT(THREAD_T_TO_PTR(THIS_THREAD->id)));
8271012016-12-20Jonas Walldén  f_int2hex(1);
5e9fc02015-08-18Per Hedbor  push_static_text (")");
9a0d422000-02-06Martin Stjernholm  f_add (3); }
30c0612008-06-29Martin Stjernholm /*! @decl protected int id_number()
5e50ef2001-09-25Henrik Grubbström (Grubba)  *! *! Returns an id number identifying the thread. */ void f_thread_id_id_number(INT32 args) { pop_n_elems(args);
d2361e2003-06-30Martin Stjernholm  push_int64(PTR_TO_INT(THREAD_T_TO_PTR(THIS_THREAD->id)));
5e50ef2001-09-25Henrik Grubbström (Grubba) }
442abd2004-06-29Alexander Demenshin /*! @decl mixed wait()
e413da2001-02-01Henrik Grubbström (Grubba)  *! *! Waits for the thread to complete, and then returns *! the value returned from the thread function.
d273ca2014-12-07Henrik Grubbström (Grubba)  *! *! @throws *! Rethrows the error thrown by the thread if it exited by *! throwing an error.
e413da2001-02-01Henrik Grubbström (Grubba)  */
74dfe82012-12-30Jonas Walldén static void f_thread_id_result(INT32 UNUSED(args))
5740881998-01-01Fredrik Hübinette (Hubbe) {
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *th=THIS_THREAD;
d273ca2014-12-07Henrik Grubbström (Grubba)  int th_status;
5740881998-01-01Fredrik Hübinette (Hubbe) 
4bc7e42001-11-26Henrik Grubbström (Grubba)  if (threads_disabled) { Pike_error("Cannot wait for threads when threads are disabled!\n"); }
0a14c22008-08-05Martin Stjernholm  th->waiting++;
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "Thread->wait(): Waiting for thread_state %p " "(state:%d).\n", th, th->status);
d273ca2014-12-07Henrik Grubbström (Grubba)  while(th->status < THREAD_EXITED) {
700dac2002-02-05Martin Stjernholm  SWAP_OUT_CURRENT_THREAD();
c91f892000-04-19Martin Stjernholm  co_wait_interpreter(&th->status_change);
700dac2002-02-05Martin Stjernholm  SWAP_IN_CURRENT_THREAD(); check_threads_etc();
f72d412004-04-23Henrik Grubbström (Grubba)  THREADS_FPRINTF(0,
7d06ba2016-02-07Martin Nilsson  "Thread->wait(): Waiting for thread_state %p " "(state:%d).\n", th, th->status);
700dac2002-02-05Martin Stjernholm  }
5740881998-01-01Fredrik Hübinette (Hubbe) 
d273ca2014-12-07Henrik Grubbström (Grubba)  th_status = th->status;
0431312003-02-15Henrik Grubbström (Grubba)  assign_svalue_no_free(Pike_sp, &th->result);
17f08c2000-07-06Fredrik Hübinette (Hubbe)  Pike_sp++;
50ea682003-03-14Henrik Grubbström (Grubba)  dmalloc_touch_svalue(Pike_sp-1);
0a14c22008-08-05Martin Stjernholm  th->waiting--; if (!th->thread_obj) /* Do this only if exit_thread_obj already has run. */ cleanup_thread_state (th);
d273ca2014-12-07Henrik Grubbström (Grubba)  if (th_status == THREAD_ABORTED) f_throw(1);
5740881998-01-01Fredrik Hübinette (Hubbe) }
9595b92004-08-12Henrik Grubbström (Grubba) static int num_pending_interrupts = 0; static struct callback *thread_interrupt_callback = NULL; static void check_thread_interrupt(struct callback *foo,
74dfe82012-12-30Jonas Walldén  void *UNUSED(bar), void *UNUSED(gazonk))
9595b92004-08-12Henrik Grubbström (Grubba) {
18e21f2014-05-25Henrik Grubbström (Grubba)  if (Pike_interpreter.thread_state->flags & THREAD_FLAG_INHIBIT) { return; }
286b312004-12-30Henrik Grubbström (Grubba)  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;
9595b92004-08-12Henrik Grubbström (Grubba)  if (!--num_pending_interrupts) { remove_callback(foo); thread_interrupt_callback = NULL; }
286b312004-12-30Henrik Grubbström (Grubba)  if (throw_severity == THROW_ERROR) { Pike_error("Interrupted.\n"); } else { push_int(-1); assign_svalue(&throw_value, Pike_sp-1); pike_throw(); }
9595b92004-08-12Henrik Grubbström (Grubba)  } } /*! @decl void interrupt() *! @decl void interrupt(string msg) *! *! Interrupt the thread with the message @[msg]. *!
8d81c32018-10-12Henrik Grubbström (Grubba)  *! This function causes the thread to throw an error *! where the message defaults to @expr{"Interrupted.\n"@}. *!
9595b92004-08-12Henrik Grubbström (Grubba)  *! @fixme *! The argument @[msg] is currently ignored. *! *! @note *! Interrupts are asynchronous, and are currently not queued.
8d81c32018-10-12Henrik Grubbström (Grubba)  *! *! @note *! Due to the asynchronous nature of interrupts, it may take *! some time before the thread reacts to the interrupt.
9595b92004-08-12Henrik Grubbström (Grubba)  */ static void f_thread_id_interrupt(INT32 args) { /* FIXME: The msg argument is not supported yet. */ pop_n_elems(args);
286b312004-12-30Henrik Grubbström (Grubba)  if (!(THIS_THREAD->flags & THREAD_FLAG_SIGNAL_MASK)) {
9595b92004-08-12Henrik Grubbström (Grubba)  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. */ }
18e21f2014-05-25Henrik Grubbström (Grubba)  THIS_THREAD->flags |= THREAD_FLAG_INTR;
9595b92004-08-12Henrik Grubbström (Grubba) }
09a5732008-08-05Martin Stjernholm 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); } /* FIXME: Actually interrupt the thread. */ } th->flags |= THREAD_FLAG_TERM; }
286b312004-12-30Henrik Grubbström (Grubba) /*! @decl void kill() *! *! Interrupt the thread, and terminate it. *! *! @note *! Interrupts are asynchronous, and are currently not queued.
8d81c32018-10-12Henrik Grubbström (Grubba)  *! *! @note *! Due to the asynchronous nature of interrupts, it may take *! some time before the thread reacts to the interrupt.
286b312004-12-30Henrik Grubbström (Grubba)  */ static void f_thread_id_kill(INT32 args) { pop_n_elems(args);
09a5732008-08-05Martin Stjernholm  low_thread_kill (THIS_THREAD);
286b312004-12-30Henrik Grubbström (Grubba) }
74dfe82012-12-30Jonas Walldén void init_thread_obj(struct object *UNUSED(o))
a7fef41997-09-03Per Hedbor {
21b12a2014-09-03Martin Nilsson  memset(&THIS_THREAD->state, 0, sizeof(struct Pike_interpreter_struct));
1d456f2003-02-20Henrik Grubbström (Grubba)  THIS_THREAD->thread_obj = Pike_fp->current_object;
16479e2002-11-18Martin Stjernholm  THIS_THREAD->swapped = 0;
5740881998-01-01Fredrik Hübinette (Hubbe)  THIS_THREAD->status=THREAD_NOT_STARTED;
9595b92004-08-12Henrik Grubbström (Grubba)  THIS_THREAD->flags = 0;
0a14c22008-08-05Martin Stjernholm  THIS_THREAD->waiting = 0;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(THIS_THREAD->result, T_INT, NUMBER_UNDEFINED, integer, 0);
5740881998-01-01Fredrik Hübinette (Hubbe)  co_init(& THIS_THREAD->status_change);
621e752015-10-19Per Hedbor  THIS_THREAD->thread_locals=NULL;
247f732007-06-10Martin Stjernholm #ifdef CPU_TIME_MIGHT_BE_THREAD_LOCAL
f4a9952003-02-08Martin Stjernholm  THIS_THREAD->auto_gc_time = 0; #endif
5740881998-01-01Fredrik Hübinette (Hubbe) }
0a14c22008-08-05Martin Stjernholm static void cleanup_thread_state (struct thread_state *th)
5740881998-01-01Fredrik Hübinette (Hubbe) {
0a14c22008-08-05Martin Stjernholm #ifdef PIKE_DEBUG if (th->thread_obj) Pike_fatal ("Thread state still active.\n"); #endif /* Don't clean up if the thread is running or if someone is waiting * on status_change. They should call cleanup_thread_state later. */ if (th->status == THREAD_RUNNING || th->waiting) return;
286b312004-12-30Henrik Grubbström (Grubba)  if (THIS_THREAD->flags & THREAD_FLAG_SIGNAL_MASK) { Pike_interpreter.thread_state->flags &= ~THREAD_FLAG_SIGNAL_MASK;
9595b92004-08-12Henrik Grubbström (Grubba)  if (!--num_pending_interrupts) { remove_callback(thread_interrupt_callback); thread_interrupt_callback = NULL;
13670c2015-05-25Martin Nilsson  }
9595b92004-08-12Henrik Grubbström (Grubba)  }
0a14c22008-08-05Martin Stjernholm  co_destroy(& THIS_THREAD->status_change); th_destroy(& THIS_THREAD->id); }
74dfe82012-12-30Jonas Walldén void exit_thread_obj(struct object *UNUSED(o))
0a14c22008-08-05Martin Stjernholm { THIS_THREAD->thread_obj = NULL; cleanup_thread_state (THIS_THREAD);
621e752015-10-19Per Hedbor  if(THIS_THREAD->thread_locals != NULL) { free_mapping(THIS_THREAD->thread_locals); THIS_THREAD->thread_locals = NULL;
d86cd71998-08-24Marcus Comstedt  }
a7fef41997-09-03Per Hedbor }
a9cdcf2001-02-06Henrik Grubbström (Grubba) /*! @endclass
e413da2001-02-01Henrik Grubbström (Grubba)  */
74dfe82012-12-30Jonas Walldén static void thread_was_recursed(struct object *UNUSED(o))
f6d0171997-10-15Fredrik Hübinette (Hubbe) {
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *tmp=THIS_THREAD;
621e752015-10-19Per Hedbor  if(tmp->thread_locals != NULL) gc_recurse_mapping(tmp->thread_locals);
d86cd71998-08-24Marcus Comstedt }
74dfe82012-12-30Jonas Walldén static void thread_was_checked(struct object *UNUSED(o))
d86cd71998-08-24Marcus Comstedt {
17f08c2000-07-06Fredrik Hübinette (Hubbe)  struct thread_state *tmp=THIS_THREAD;
621e752015-10-19Per Hedbor  if(tmp->thread_locals != NULL) debug_gc_check (tmp->thread_locals,
d9d6f02001-06-30Martin Stjernholm  " as mapping for thread local values in thread");
1773212000-04-12Fredrik Hübinette (Hubbe)  #ifdef PIKE_DEBUG if(tmp->swapped)
cc2e9f2003-09-24Martin Stjernholm  gc_mark_stack_external (tmp->state.frame_pointer, tmp->state.stack_pointer, tmp->state.evaluator_stack);
1773212000-04-12Fredrik Hübinette (Hubbe) #endif
d86cd71998-08-24Marcus Comstedt }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @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
c7b7dd2001-10-28Martin Nilsson  *! stored in an instance of @[Local] can only be retrieved by that
e413da2001-02-01Henrik Grubbström (Grubba)  *! same thread. *! *! @note *! This class is simulated when Pike is compiled without thread support, *! so it's always available. */
d4e6372001-02-06Henrik Grubbström (Grubba) /* FIXME: Why not use an init callback()? */
96f63c2015-11-25Henrik Grubbström (Grubba) void f_thread_local_create( INT32 UNUSED(args) )
d86cd71998-08-24Marcus Comstedt { static INT32 thread_local_id = 0;
621e752015-10-19Per Hedbor  ((struct thread_local_var *)CURRENT_STORAGE)->id =
0319392001-02-06Per Hedbor  thread_local_id++; }
d86cd71998-08-24Marcus Comstedt 
0319392001-02-06Per Hedbor PMOD_EXPORT void f_thread_local(INT32 args) {
d86cd71998-08-24Marcus Comstedt  struct object *loc = clone_object(thread_local_prog,0); pop_n_elems(args); push_object(loc); }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @decl mixed get() *! *! Get the thread local value. *!
c7b7dd2001-10-28Martin Nilsson  *! This returns the value prevoiusly stored in the @[Local] object by
e413da2001-02-01Henrik Grubbström (Grubba)  *! the @[set()] method by this thread. *! *! @seealso *! @[set()] */
d86cd71998-08-24Marcus Comstedt void f_thread_local_get(INT32 args) { struct svalue key; struct mapping *m;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(key, T_INT, NUMBER_NUMBER, integer,
621e752015-10-19Per Hedbor  ((struct thread_local_var *)CURRENT_STORAGE)->id);
d86cd71998-08-24Marcus Comstedt  pop_n_elems(args);
0431312003-02-15Henrik Grubbström (Grubba)  if(Pike_interpreter.thread_state != NULL &&
621e752015-10-19Per Hedbor  (m = Pike_interpreter.thread_state->thread_locals) != NULL)
17f08c2000-07-06Fredrik Hübinette (Hubbe)  mapping_index_no_free(Pike_sp++, m, &key);
d86cd71998-08-24Marcus Comstedt  else {
074dd12011-10-22Henrik Grubbström (Grubba)  push_undefined();
d86cd71998-08-24Marcus Comstedt  } }
e413da2001-02-01Henrik Grubbström (Grubba) /*! @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()] */
d86cd71998-08-24Marcus Comstedt void f_thread_local_set(INT32 args) { struct svalue key; struct mapping *m;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(key, T_INT, NUMBER_NUMBER, integer,
621e752015-10-19Per Hedbor  ((struct thread_local_var *)CURRENT_STORAGE)->id);
d86cd71998-08-24Marcus Comstedt  if(args>1) pop_n_elems(args-1); else if(args<1)
06bd612016-01-26Martin Nilsson  SIMPLE_WRONG_NUM_ARGS_ERROR("Thread.Local.set", 1);
d86cd71998-08-24Marcus Comstedt 
1d456f2003-02-20Henrik Grubbström (Grubba)  if(Pike_interpreter.thread_state == NULL)
cd6f7e2000-12-01Martin Stjernholm  Pike_error("Trying to set Thread.Local without thread!\n");
d86cd71998-08-24Marcus Comstedt 
621e752015-10-19Per Hedbor  if((m = Pike_interpreter.thread_state->thread_locals) == NULL) m = Pike_interpreter.thread_state->thread_locals =
d86cd71998-08-24Marcus Comstedt  allocate_mapping(4);
17f08c2000-07-06Fredrik Hübinette (Hubbe)  mapping_insert(m, &key, &Pike_sp[-1]);
d86cd71998-08-24Marcus Comstedt }
f6d0171997-10-15Fredrik Hübinette (Hubbe) 
d9d6f02001-06-30Martin Stjernholm #ifdef PIKE_DEBUG
9a8ddb2014-11-16Henrik Grubbström (Grubba) void gc_check_thread_local (struct object *UNUSED(o))
d9d6f02001-06-30Martin Stjernholm { /* Only used by with locate_references. */ if (Pike_in_gc == GC_PASS_LOCATE) { struct svalue key, *val; struct thread_state *s;
017b572011-10-28Henrik Grubbström (Grubba)  SET_SVAL(key, T_INT, NUMBER_NUMBER, integer,
621e752015-10-19Per Hedbor  ((struct thread_local_var *)CURRENT_STORAGE)->id);
d9d6f02001-06-30Martin Stjernholm 
f2c01e2003-01-08Martin Stjernholm  FOR_EACH_THREAD (s, {
621e752015-10-19Per Hedbor  if (s->thread_locals && (val = low_mapping_lookup(s->thread_locals, &key)))
e1a35e2003-09-08Martin Stjernholm  debug_gc_check_svalues (val, 1,
d9d6f02001-06-30Martin Stjernholm  " as thread local value in Thread.Local object" " (indirect ref)");
f2c01e2003-01-08Martin Stjernholm  });
d9d6f02001-06-30Martin Stjernholm  } } #endif
e413da2001-02-01Henrik Grubbström (Grubba) /*! @endclass */
a9cdcf2001-02-06Henrik Grubbström (Grubba) /*! @endmodule */
64b7b62000-11-02Henrik Grubbström (Grubba) /* Thread farm code by Per
13670c2015-05-25Martin Nilsson  *
64b7b62000-11-02Henrik Grubbström (Grubba)  */ static struct farmer { struct farmer *neighbour; void *field; void (*harvest)(void *); THREAD_T me; COND_T harvest_moon; #ifdef HAVE_BROKEN_LINUX_THREAD_EUID int euid, egid; #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ } *farmers;
74ea782016-01-29Martin Nilsson static PIKE_MUTEX_T rosie;
64b7b62000-11-02Henrik Grubbström (Grubba)  static int _num_farmers, _num_idle_farmers; static TH_RETURN_TYPE farm(void *_a) { struct farmer *me = (struct farmer *)_a; #ifdef HAVE_BROKEN_LINUX_THREAD_EUID /* Work-around for Linux's pthreads not propagating the * effective uid & gid. */ if (!geteuid()) {
f663a02003-03-05Martin Stjernholm #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
b096df2014-03-13Per Hedbor #ifdef HAVE_BROKEN_LINUX_THREAD_EUID if( setegid(arg.egid) != 0 || seteuid(arg.euid) != 0 ) {
fa77662016-02-11Martin Nilsson  WERR("%s:%d: Unexpected error from setegid(2). errno=%d\n", __FILE__, __LINE__, errno);
b096df2014-03-13Per Hedbor  } #endif
f663a02003-03-05Martin Stjernholm #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
64b7b62000-11-02Henrik Grubbström (Grubba)  } #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ do { me->harvest( me->field ); me->harvest = 0; mt_lock( &rosie ); if( ++_num_idle_farmers > 16 ) { --_num_idle_farmers; --_num_farmers; mt_unlock( &rosie ); free( me ); return 0; } me->neighbour = farmers; farmers = me;
fa77662016-02-11Martin Nilsson 
64b7b62000-11-02Henrik Grubbström (Grubba)  while(!me->harvest) co_wait( &me->harvest_moon, &rosie ); --_num_idle_farmers; mt_unlock( &rosie ); } while(1);
9282fd2015-09-27Martin Nilsson  UNREACHABLE(return 0);
64b7b62000-11-02Henrik Grubbström (Grubba) } int th_num_idle_farmers(void) { return _num_idle_farmers; } int th_num_farmers(void) { return _num_farmers; } static struct farmer *new_farmer(void (*fun)(void *), void *args) { struct farmer *me = malloc(sizeof(struct farmer)); if (!me) { /* Out of memory */
5aad932002-08-15Marcus Comstedt  Pike_fatal("new_farmer(): Out of memory!\n");
64b7b62000-11-02Henrik Grubbström (Grubba)  } dmalloc_accept_leak(me); _num_farmers++; me->neighbour = 0; me->field = args; me->harvest = fun; co_init( &me->harvest_moon ); #ifdef HAVE_BROKEN_LINUX_THREAD_EUID me->euid = geteuid(); me->egid = getegid(); #endif /* HAVE_BROKEN_LINUX_THREAD_EUID */ th_create_small(&me->me, farm, me); return me; } PMOD_EXPORT void th_farm(void (*fun)(void *), void *here) {
6e00e22003-11-13Martin Stjernholm #ifdef PIKE_DEBUG
5aad932002-08-15Marcus Comstedt  if(!fun) Pike_fatal("The farmers don't known how to handle empty fields\n");
6e00e22003-11-13Martin Stjernholm #endif
64b7b62000-11-02Henrik Grubbström (Grubba)  mt_lock( &rosie ); if(farmers) { struct farmer *f = farmers; farmers = f->neighbour; f->field = here; f->harvest = fun; mt_unlock( &rosie ); co_signal( &f->harvest_moon ); return; } mt_unlock( &rosie ); new_farmer( fun, here ); } /* * Glue code. */
a91ca01998-07-10Henrik Grubbström (Grubba) void low_th_init(void)
07513e1996-10-04Fredrik Hübinette (Hubbe) {
7d06ba2016-02-07Martin Nilsson  THREADS_FPRINTF(0, "Initializing threads.\n");
fed8de1997-10-05Henrik Grubbström (Grubba) 
83b1842003-02-08Martin Stjernholm  really_low_th_init();
fed8de1997-10-05Henrik Grubbström (Grubba) 
12ae2f1997-09-05Henrik Grubbström (Grubba)  mt_init( & interpreter_lock);
63ba772010-10-12Martin Stjernholm  mt_init( & interpreter_lock_wanted);
bc21dc2001-11-01Martin Stjernholm  low_mt_lock_interpreter();
eac2091998-02-27Marcus Comstedt  mt_init( & thread_table_lock);
a91ca01998-07-10Henrik Grubbström (Grubba)  mt_init( & interleave_lock);
64b7b62000-11-02Henrik Grubbström (Grubba)  mt_init( & rosie);
ef1e931998-03-26Henrik Grubbström (Grubba)  co_init( & live_threads_change); co_init( & threads_disabled_change);
eac2091998-02-27Marcus Comstedt  thread_table_init();
864d3c1998-01-29Fredrik Hübinette (Hubbe) 
92ec352010-10-23Martin Stjernholm #if defined(RDTSC) && defined(USE_CLOCK_FOR_SLICES)
f919942010-10-17Martin Stjernholm  { INT32 cpuid[4]; x86_get_cpuid (1, cpuid);
fb09e92015-09-28Martin Nilsson  use_tsc_for_slices = cpuid[2] & 0x10; /* TSC exists */
927d3c2010-10-24Martin Stjernholm #if 0 /* Skip tsc invariant check - the current tsc interval method * should be robust enough to cope with variable tsc rates. */
f919942010-10-17Martin Stjernholm  if (use_tsc_for_slices) { x86_get_cpuid (0x80000007, cpuid);
fb09e92015-09-28Martin Nilsson  use_tsc_for_slices = cpuid[2] & 0x100; /* TSC is invariant */
f919942010-10-17Martin Stjernholm  }
927d3c2010-10-24Martin Stjernholm #endif
f919942010-10-17Martin Stjernholm  } #endif
b32ef12000-04-19Martin Stjernholm  th_running = 1;
a91ca01998-07-10Henrik Grubbström (Grubba) }
1d456f2003-02-20Henrik Grubbström (Grubba) static struct object *backend_thread_obj = NULL;
d97eb72011-07-10Henrik Grubbström (Grubba) static struct Pike_interpreter_struct *original_interpreter = NULL;
a91ca01998-07-10Henrik Grubbström (Grubba) void th_init(void) {
89fc4c2000-08-10Henrik Grubbström (Grubba)  ptrdiff_t mutex_key_offset;
ca85822019-08-21Henrik Grubbström (Grubba)  ptrdiff_t rwmutex_key_offset;
a91ca01998-07-10Henrik Grubbström (Grubba) 
e4419e1997-02-06Fredrik Hübinette (Hubbe) #ifdef UNIX_THREADS
13670c2015-05-25Martin Nilsson 
45ee5d1999-02-10Fredrik Hübinette (Hubbe)  ADD_EFUN("thread_set_concurrency",f_thread_set_concurrency,tFunc(tInt,tVoid), OPT_SIDE_EFFECT);
e4419e1997-02-06Fredrik Hübinette (Hubbe) #endif
07513e1996-10-04Fredrik Hübinette (Hubbe) 
15ecdc2011-04-02Martin Stjernholm #ifdef PIKE_DEBUG ADD_EFUN("_thread_swaps", f__thread_swaps, tFunc(tVoid,tInt), OPT_SIDE_EFFECT); ADD_EFUN("_check_threads_calls", f__check_threads_calls, tFunc(tVoid,tInt), OPT_SIDE_EFFECT); ADD_EFUN("_check_threads_yields", f__check_threads_yields, tFunc(tVoid,tInt), OPT_SIDE_EFFECT); ADD_EFUN("_check_threads_swaps", f__check_threads_swaps, tFunc(tVoid,tInt), OPT_SIDE_EFFECT); #endif
3f87be1999-12-14Martin Stjernholm  START_NEW_PROGRAM_ID(THREAD_MUTEX_KEY);
90e9781999-01-31Fredrik Hübinette (Hubbe)  mutex_key_offset = ADD_STORAGE(struct key_storage);
46d7bf1997-09-03Henrik Grubbström (Grubba)  /* This is needed to allow the gc to find the possible circular reference.
e413da2001-02-01Henrik Grubbström (Grubba)  * It also allows a thread to take over ownership of a key.
46d7bf1997-09-03Henrik Grubbström (Grubba)  */
1d456f2003-02-20Henrik Grubbström (Grubba)  PIKE_MAP_VARIABLE("_owner", mutex_key_offset + OFFSETOF(key_storage, owner_obj),
33887a2002-10-28Martin Stjernholm  tObjIs_THREAD_ID, T_OBJECT, 0); PIKE_MAP_VARIABLE("_mutex", mutex_key_offset + OFFSETOF(key_storage, mutex_obj),
95489a2008-06-29Martin Nilsson  tObjIs_THREAD_MUTEX, T_OBJECT, ID_PROTECTED|ID_PRIVATE);
27f1f92019-05-20Henrik Grubbström (Grubba)  ADD_FUNCTION("_sprintf",f_mutex_key__sprintf,tFunc(tInt,tStr),ID_PROTECTED);
a94a082020-07-02Henrik Grubbström (Grubba)  ADD_FUNCTION("downgrade", f_mutex_key_downgrade, tFunc(tNone,tVoid), 0); ADD_FUNCTION("upgrade", f_mutex_key_upgrade, tFunc(tNone,tVoid), 0); ADD_FUNCTION("try_upgrade", f_mutex_key_try_upgrade, tFunc(tNone,tInt01), 0);
5988921996-10-05Fredrik Hübinette (Hubbe)  set_init_callback(init_mutex_key_obj);
07513e1996-10-04Fredrik Hübinette (Hubbe)  set_exit_callback(exit_mutex_key_obj);
bad5162000-06-23Fredrik Hübinette (Hubbe)  mutex_key=Pike_compiler->new_program;
3f87be1999-12-14Martin Stjernholm  add_ref(mutex_key); end_class("mutex_key", 0);
b1f4eb1998-01-13Fredrik Hübinette (Hubbe)  mutex_key->flags|=PROGRAM_DESTRUCT_IMMEDIATE;
07513e1996-10-04Fredrik Hübinette (Hubbe) 
3f87be1999-12-14Martin Stjernholm  START_NEW_PROGRAM_ID(THREAD_MUTEX);
52e4c61999-12-13Martin Stjernholm  ADD_STORAGE(struct mutex_storage);
3f87be1999-12-14Martin Stjernholm  ADD_FUNCTION("lock",f_mutex_lock,
6018db2021-07-21Henrik Grubbström (Grubba)  tOr(tFunc(tOr(tInt02,tVoid) tOr3(tVoid, tIntPos, tFloat), tObjIs_THREAD_MUTEX_KEY), tFunc(tInt02 tIntPos tIntPos, tObjIs_THREAD_MUTEX_KEY)), 0);
4781e52020-06-28Henrik Grubbström (Grubba)  ADD_FUNCTION("shared_lock",f_mutex_shared_lock,
6018db2021-07-21Henrik Grubbström (Grubba)  tOr(tFunc(tOr(tInt02,tVoid) tOr3(tVoid, tIntPos, tFloat), tObjIs_THREAD_MUTEX_KEY), tFunc(tInt02 tIntPos tIntPos, tObjIs_THREAD_MUTEX_KEY)), 0);
3f87be1999-12-14Martin Stjernholm  ADD_FUNCTION("trylock",f_mutex_trylock, tFunc(tOr(tInt02,tVoid),tObjIs_THREAD_MUTEX_KEY),0);
4781e52020-06-28Henrik Grubbström (Grubba)  ADD_FUNCTION("try_shared_lock",f_mutex_try_shared_lock, tFunc(tOr(tInt02,tVoid),tObjIs_THREAD_MUTEX_KEY),0);
b9bba02001-08-08Leif Stensson  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);
4c4b752017-06-24Henrik Grubbström (Grubba)  ADD_FUNCTION("_sprintf",f_mutex__sprintf,tFunc(tInt,tStr),0);
5bb8d02018-09-27Henrik Grubbström (Grubba)  ADD_FUNCTION("condition", f_mutex_cond, tFunc(tNone, tObjIs_THREAD_CONDITION), 0);
52e4c61999-12-13Martin Stjernholm  set_init_callback(init_mutex_obj); set_exit_callback(exit_mutex_obj);
5c2f2f2018-09-16Henrik Grubbström (Grubba)  mutex_program = Pike_compiler->new_program; add_ref(mutex_program);
52e4c61999-12-13Martin Stjernholm  end_class("mutex", 0);
cff4172020-06-13Henrik Grubbström (Grubba)  START_NEW_PROGRAM_ID(THREAD_RWMUTEX_KEY);
ca85822019-08-21Henrik Grubbström (Grubba)  rwmutex_key_offset = ADD_STORAGE(struct rwmutex_key_storage); /* This is needed to allow the gc to find the possible circular reference. * It also allows a thread to take over ownership of a key. */ PIKE_MAP_VARIABLE("_owner", rwmutex_key_offset + OFFSETOF(rwmutex_key_storage, owner_obj), tObjIs_THREAD_ID, T_OBJECT, 0); PIKE_MAP_VARIABLE("_mutex", rwmutex_key_offset + OFFSETOF(rwmutex_key_storage, rwmutex_obj), tObjIs_THREAD_MUTEX, T_OBJECT, ID_PROTECTED|ID_PRIVATE); set_init_callback(init_rwmutex_key_obj);
cff4172020-06-13Henrik Grubbström (Grubba)  set_exit_callback(exit_rwmutex_key_obj); ADD_FUNCTION("create", f_rwmutex_key_create, tFunc(tOr(tObjIs_THREAD_RWMUTEX, tVoid) tOr(tInt02, tVoid), tVoid),
ca85822019-08-21Henrik Grubbström (Grubba)  ID_PROTECTED);
e944042020-06-14Henrik Grubbström (Grubba)  ADD_FUNCTION("downgrade", f_rwmutex_key_downgrade, tFunc(tNone, tVoid), 0); ADD_FUNCTION("upgrade", f_rwmutex_key_upgrade, tFunc(tNone, tVoid), 0);
3d1d6e2020-06-23Henrik Grubbström (Grubba)  ADD_FUNCTION("try_upgrade", f_rwmutex_key_try_upgrade, tFunc(tNone, tInt01), 0);
cff4172020-06-13Henrik Grubbström (Grubba)  ADD_FUNCTION("_sprintf", f_rwmutex_key__sprintf,
ca85822019-08-21Henrik Grubbström (Grubba)  tFunc(tOr(tInt, tVoid), tStr), ID_PROTECTED);
cff4172020-06-13Henrik Grubbström (Grubba)  rwmutex_key_program = Pike_compiler->new_program; add_ref(rwmutex_key_program); end_class("RWKey", 0); rwmutex_key_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE;
ca85822019-08-21Henrik Grubbström (Grubba)  START_NEW_PROGRAM_ID(THREAD_RWMUTEX); ADD_STORAGE(struct rwmutex_storage); ADD_FUNCTION("read_lock", f_rwmutex_read_lock,
cff4172020-06-13Henrik Grubbström (Grubba)  tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0);
3d1d6e2020-06-23Henrik Grubbström (Grubba)  ADD_FUNCTION("try_read_lock", f_rwmutex_try_read_lock, tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0);
ca85822019-08-21Henrik Grubbström (Grubba)  ADD_FUNCTION("write_lock", f_rwmutex_write_lock,
cff4172020-06-13Henrik Grubbström (Grubba)  tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0);
3d1d6e2020-06-23Henrik Grubbström (Grubba)  ADD_FUNCTION("try_write_lock", f_rwmutex_try_write_lock, tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0);
1adec62020-12-09Henrik Grubbström (Grubba)  ADD_FUNCTION("current_locking_threads", f_rwmutex_current_locking_threads, tFunc(tNone, tArr(tObjImpl_THREAD_ID)), 0); ADD_FUNCTION("current_locking_keys", f_rwmutex_current_locking_keys, tFunc(tNone, tArr(tObjImpl_THREAD_RWMUTEX_KEY)), 0);
ca85822019-08-21Henrik Grubbström (Grubba)  ADD_FUNCTION("_sprintf", f_rwmutex__sprintf, tFunc(tOr(tInt, tVoid), tStr), ID_PROTECTED); set_init_callback(init_rwmutex_obj); set_exit_callback(exit_rwmutex_obj); rwmutex_program = Pike_compiler->new_program; add_ref(rwmutex_program); end_class("RWMutex", 0);
3f87be1999-12-14Martin Stjernholm  START_NEW_PROGRAM_ID(THREAD_CONDITION);
8349b42006-01-29Henrik Grubbström (Grubba)  ADD_STORAGE(struct pike_cond);
5c2f2f2018-09-16Henrik Grubbström (Grubba)  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);
3f87be1999-12-14Martin Stjernholm  ADD_FUNCTION("wait",f_cond_wait,
4a5f542009-01-25Henrik Grubbström (Grubba)  tOr(tFunc(tObjIs_THREAD_MUTEX_KEY tOr3(tVoid, tIntPos, tFloat), tVoid), tFunc(tObjIs_THREAD_MUTEX_KEY tIntPos tIntPos, tVoid)),0);
b93e6e1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("signal",f_cond_signal,tFunc(tNone,tVoid),0); ADD_FUNCTION("broadcast",f_cond_broadcast,tFunc(tNone,tVoid),0);
508ef02019-09-06Henrik Grubbström (Grubba)  ADD_FUNCTION("_sprintf", f_cond__sprintf, tFunc(tOr(tInt, tVoid), tStr), ID_PROTECTED);
07513e1996-10-04Fredrik Hübinette (Hubbe)  set_init_callback(init_cond_obj); set_exit_callback(exit_cond_obj);
f327f22018-09-23Henrik Grubbström (Grubba)  condition_program = Pike_compiler->new_program; add_ref(condition_program);
b98eb31997-02-11Henrik Grubbström (Grubba)  end_class("condition", 0);
13670c2015-05-25Martin Nilsson 
de413f1998-03-26Per Hedbor  { struct program *tmp;
3f87be1999-12-14Martin Stjernholm  START_NEW_PROGRAM_ID(THREAD_DISABLE_THREADS);
de413f1998-03-26Per Hedbor  set_init_callback(init_threads_disable); set_exit_callback(exit_threads_disable);
bad5162000-06-23Fredrik Hübinette (Hubbe)  tmp = Pike_compiler->new_program;
3f87be1999-12-14Martin Stjernholm  add_ref(tmp); end_class("threads_disabled", 0);
de413f1998-03-26Per Hedbor  tmp->flags|=PROGRAM_DESTRUCT_IMMEDIATE; add_global_program("_disable_threads", tmp);
56a6961998-04-05Fredrik Hübinette (Hubbe)  free_program(tmp);
de413f1998-03-26Per Hedbor  }
6d1a5e1996-10-07Fredrik Hübinette (Hubbe) 
3f87be1999-12-14Martin Stjernholm  START_NEW_PROGRAM_ID(THREAD_LOCAL);
621e752015-10-19Per Hedbor  ADD_STORAGE(struct thread_local_var);
b93e6e1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("get",f_thread_local_get,tFunc(tNone,tMix),0);
b21d9e2000-11-06Martin Stjernholm  ADD_FUNCTION("set",f_thread_local_set,tFunc(tSetvar(1,tMix),tVar(1)),0);
d4e6372001-02-06Henrik Grubbström (Grubba)  ADD_FUNCTION("create", f_thread_local_create,
4c4b752017-06-24Henrik Grubbström (Grubba)  tFunc(tNone,tVoid), ID_PROTECTED);
d9d6f02001-06-30Martin Stjernholm #ifdef PIKE_DEBUG set_gc_check_callback(gc_check_thread_local); #endif
bad5162000-06-23Fredrik Hübinette (Hubbe)  thread_local_prog=Pike_compiler->new_program;
3f87be1999-12-14Martin Stjernholm  add_ref(thread_local_prog); end_class("thread_local", 0);
d4e6372001-02-06Henrik Grubbström (Grubba)  ADD_EFUN("thread_local", f_thread_local,
3f87be1999-12-14Martin Stjernholm  tFunc(tNone,tObjIs_THREAD_LOCAL),
d4e6372001-02-06Henrik Grubbström (Grubba)  OPT_EXTERNAL_DEPEND);
d86cd71998-08-24Marcus Comstedt 
3f87be1999-12-14Martin Stjernholm  START_NEW_PROGRAM_ID(THREAD_ID);
17f08c2000-07-06Fredrik Hübinette (Hubbe)  thread_storage_offset=ADD_STORAGE(struct thread_state);
0431312003-02-15Henrik Grubbström (Grubba)  PIKE_MAP_VARIABLE("result", OFFSETOF(thread_state, result), tMix, T_MIXED, 0); ADD_FUNCTION("create",f_thread_create,
761bb02015-08-20Henrik Grubbström (Grubba)  tFuncV(tMixed,tMixed,tVoid),
95489a2008-06-29Martin Nilsson  ID_PROTECTED);
671ae82019-08-04Henrik Grubbström (Grubba)  ADD_FUNCTION("backtrace", f_thread_backtrace, tFunc(tOr(tVoid,tInt01),tArray), 0);
b93e6e1999-06-19Fredrik Hübinette (Hubbe)  ADD_FUNCTION("wait",f_thread_id_result,tFunc(tNone,tMix),0); ADD_FUNCTION("status",f_thread_id_status,tFunc(tNone,tInt),0);
19330c2020-06-20Stephen R. van den Berg  ADD_FUNCTION("gethrvtime",f_thread_id_gethrvtime,tFunc(tNone,tInt),0);
4c4b752017-06-24Henrik Grubbström (Grubba)  ADD_FUNCTION("_sprintf",f_thread_id__sprintf,tFunc(tInt,tStr),0);
5e50ef2001-09-25Henrik Grubbström (Grubba)  ADD_FUNCTION("id_number",f_thread_id_id_number,tFunc(tNone,tInt),0);
9595b92004-08-12Henrik Grubbström (Grubba)  ADD_FUNCTION("interrupt", f_thread_id_interrupt, tFunc(tOr(tVoid,tStr), tVoid), 0);
286b312004-12-30Henrik Grubbström (Grubba)  ADD_FUNCTION("kill", f_thread_id_kill, tFunc(tNone, tVoid), 0);
a5bd2b2000-06-10Martin Stjernholm  set_gc_recurse_callback(thread_was_recursed);
d86cd71998-08-24Marcus Comstedt  set_gc_check_callback(thread_was_checked);
a7fef41997-09-03Per Hedbor  set_init_callback(init_thread_obj);
a2db6b1998-01-02Fredrik Hübinette (Hubbe)  set_exit_callback(exit_thread_obj);
bad5162000-06-23Fredrik Hübinette (Hubbe)  thread_id_prog=Pike_compiler->new_program;
95363a2000-04-11Fredrik Hübinette (Hubbe)  thread_id_prog->flags |= PROGRAM_NO_EXPLICIT_DESTRUCT;
3f87be1999-12-14Martin Stjernholm  add_ref(thread_id_prog); end_class("thread_id", 0);
0431312003-02-15Henrik Grubbström (Grubba)  /* Backward compat... */ add_global_program("thread_create", thread_id_prog);
3f87be1999-12-14Martin Stjernholm  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);
52e4c61999-12-13Martin Stjernholm 
7a52982015-09-09Henrik Grubbström (Grubba)  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);
5d2f121998-08-27Henrik Grubbström (Grubba)  /* 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);
d273ca2014-12-07Henrik Grubbström (Grubba)  add_integer_constant("THREAD_ABORTED", THREAD_ABORTED, 0);
5d2f121998-08-27Henrik Grubbström (Grubba) 
d97eb72011-07-10Henrik Grubbström (Grubba)  original_interpreter = Pike_interpreter_pointer;
1d456f2003-02-20Henrik Grubbström (Grubba)  backend_thread_obj = fast_clone_object(thread_id_prog); INIT_THREAD_STATE((struct thread_state *)(backend_thread_obj->storage +
b867f92003-02-16Martin Stjernholm  thread_storage_offset));
0431312003-02-15Henrik Grubbström (Grubba)  thread_table_insert(Pike_interpreter.thread_state);
6d1a5e1996-10-07Fredrik Hübinette (Hubbe) }
09a5732008-08-05Martin Stjernholm #ifdef DO_PIKE_CLEANUP void cleanup_all_other_threads (void) { int i, num_kills = num_pending_interrupts; time_t timeout = time (NULL) + 2; mt_lock (&thread_table_lock); for (i = 0; i < THREAD_TABLE_SIZE; i++) { struct thread_state *th; for (th = thread_table_chains[i]; th; th = th->hashlink) if (th != Pike_interpreter.thread_state) { low_thread_kill (th); num_kills++; } } mt_unlock (&thread_table_lock); while (num_pending_interrupts && time (NULL) < timeout) { THREADS_ALLOW();
3714062010-02-18Stephen R. van den Berg  sysleep(1.0);
09a5732008-08-05Martin Stjernholm  THREADS_DISALLOW(); }
0a2bf62008-08-05Martin Stjernholm #if 0
09a5732008-08-05Martin Stjernholm  if (num_kills) {
fa77662016-02-11Martin Nilsson  WERR("Killed %d thread(s) in exit cleanup", num_kills - num_pending_interrupts);
09a5732008-08-05Martin Stjernholm  if (num_pending_interrupts)
fa77662016-02-11Martin Nilsson  WERR(", %d more haven't responded", num_pending_interrupts);
09a5732008-08-05Martin Stjernholm  fputs (".\n", stderr); }
0a2bf62008-08-05Martin Stjernholm #endif
09a5732008-08-05Martin Stjernholm } #endif
be478c1997-08-30Henrik Grubbström (Grubba) void th_cleanup(void)
6d1a5e1996-10-07Fredrik Hübinette (Hubbe) {
b32ef12000-04-19Martin Stjernholm  th_running = 0;
0431312003-02-15Henrik Grubbström (Grubba)  if (Pike_interpreter.thread_state) { thread_table_delete(Pike_interpreter.thread_state); Pike_interpreter.thread_state = NULL; }
1d456f2003-02-20Henrik Grubbström (Grubba)  if(backend_thread_obj)
c3b9952000-02-15Henrik Grubbström (Grubba)  {
d97eb72011-07-10Henrik Grubbström (Grubba)  /* Switch back to the original interpreter struct. */ *original_interpreter = Pike_interpreter;
de78e62020-03-02Henrik Grubbström (Grubba)  /* NB: We know that mutex_key doesn't have an lfun:_destruct() * that inhibits our destruct(). */
1d456f2003-02-20Henrik Grubbström (Grubba)  destruct(backend_thread_obj); free_object(backend_thread_obj); backend_thread_obj = NULL;
d97eb72011-07-10Henrik Grubbström (Grubba)  Pike_interpreter_pointer = original_interpreter;
56f4f42001-09-18Fredrik Hübinette (Hubbe)  destruct_objects_to_destruct_cb();
c3b9952000-02-15Henrik Grubbström (Grubba)  }
f327f22018-09-23Henrik Grubbström (Grubba)  if(condition_program) { free_program(condition_program); condition_program = NULL; }
ca85822019-08-21Henrik Grubbström (Grubba)  if(rwmutex_program) { free_program(rwmutex_program); rwmutex_program = NULL; }
cff4172020-06-13Henrik Grubbström (Grubba)  if(rwmutex_key_program)
ca85822019-08-21Henrik Grubbström (Grubba)  {
cff4172020-06-13Henrik Grubbström (Grubba)  free_program(rwmutex_key_program); rwmutex_key_program = NULL;
ca85822019-08-21Henrik Grubbström (Grubba)  }
5c2f2f2018-09-16Henrik Grubbström (Grubba)  if(mutex_program) { free_program(mutex_program); mutex_program = NULL; }
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  if(mutex_key) { free_program(mutex_key); mutex_key=0; }
d86cd71998-08-24Marcus Comstedt  if(thread_local_prog) { free_program(thread_local_prog); thread_local_prog=0; }
6d1a5e1996-10-07Fredrik Hübinette (Hubbe)  if(thread_id_prog) { free_program(thread_id_prog); thread_id_prog=0; }
58580c2001-11-12Martin Stjernholm  #ifdef PIKE_USE_OWN_ATFORK free_callback_list(&atfork_prepare_callback); free_callback_list(&atfork_parent_callback); free_callback_list(&atfork_child_callback); #endif
07513e1996-10-04Fredrik Hübinette (Hubbe) }
83b1842003-02-08Martin Stjernholm #endif /* !CONFIGURE_TEST */
ab95012021-06-19Henrik Grubbström (Grubba) #else /* !_REENTRANT */
2b902c2021-06-21Henrik Grubbström (Grubba) #ifndef CONFIGURE_TEST
ab95012021-06-19Henrik Grubbström (Grubba) PMOD_EXPORT void call_with_interpreter(void (*func)(void *ctx), void *ctx) { JMP_BUF back; if(SETJMP(back)) { if(throw_severity <= THROW_ERROR) { 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); }
2b902c2021-06-21Henrik Grubbström (Grubba) #endif /* !CONFIGURE_TEST */
ab95012021-06-19Henrik Grubbström (Grubba)  #endif /* _REENTRANT */