pike.git / src / threads.c

version» Context lines:

pike.git/src/threads.c:392: Inside #if defined(INTERNAL_PROFILING)
  #ifdef INTERNAL_PROFILING   PMOD_EXPORT unsigned long thread_yields = 0;   #endif   static int th_running = 0;   static PIKE_MUTEX_T interpreter_lock;   static PIKE_MUTEX_T interpreter_lock_wanted;   PIKE_MUTEX_T thread_table_lock;   static PIKE_MUTEX_T interleave_lock;   static struct program *mutex_program = NULL;   static struct program *mutex_key = 0; - static struct program *rwmutex_program = NULL; - static struct program *rwmutex_key_program = NULL; +    static struct program *condition_program = NULL;   PMOD_EXPORT struct program *thread_id_prog = 0;   static struct program *thread_local_prog = 0;   PMOD_EXPORT ptrdiff_t thread_storage_offset;   static int live_threads = 0;   static COND_T live_threads_change;   static COND_T threads_disabled_change;      struct thread_local_var   {
pike.git/src/threads.c:3259:    }       key->kind = KEY_EXCLUSIVE;       push_int(1);   }      /*! @endclass    */    - enum rwmux_kind -  { -  RWMUX_NONE = 0, -  RWMUX_TRY_READ = RWMUX_NONE, -  RWMUX_READ, -  RWMUX_DOWNGRADED, -  RWMUX_TRY_WRITE = RWMUX_DOWNGRADED, -  RWMUX_UPGRADING, -  RWMUX_WRITE, -  }; -  - struct rwmutex_key_storage - { -  struct object *self; /* NOT ref-counted! */ -  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; -  enum rwmux_kind kind; - }; -  - /*! @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; -  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. -  */ -  struct rwmutex_key_storage *key; - }; -  - #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); - } -  - /*! @decl RWKey read_lock() -  *! -  *! Obtain a lock for reading (shared lock). -  *! -  *! @throws -  *! Throws an error if the current thread already has -  *! a lock (read or write). -  *! -  *! @seealso -  *! @[try_read_lock()], @[write_lock()] -  */ - 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); -  push_int(RWMUX_READ); -  push_object(clone_object(rwmutex_key_program, 2)); - } -  - /*! @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)); - } -  - /*! @decl RWKey write_lock() -  *! -  *! 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()], @[try_write_lock()] -  */ - 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); -  push_int(RWMUX_WRITE); -  push_object(clone_object(rwmutex_key_program, 2)); - } -  - /*! @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)); - } -  - /*! @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); - } -  - /*! @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); -  -  if (m->key) { -  push_static_text(" Locked by"); -  -  for (key = m->key; key; key = key->next) { -  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 - */ -  - /*! @class RWKey -  */ -  - #define THIS_RWKEY ((struct rwmutex_key_storage *)(CURRENT_STORAGE)) - static void init_rwmutex_key_obj(struct object *UNUSED(o)) - { -  /* 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; -  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); -  THIS_RWKEY->kind = RWMUX_NONE; - } -  - static void exit_rwmutex_key_obj(struct object *UNUSED(o)) - { -  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 { -  rwmutex->key = THIS_RWKEY->next; -  } -  if (THIS_RWKEY->next) { -  THIS_RWKEY->next->prev = THIS_RWKEY->prev; -  } -  THIS_RWKEY->next = NULL; -  THIS_RWKEY->prev = NULL; -  THIS_RWKEY->rwmutex = NULL; -  -  switch(THIS_RWKEY->kind) { -  case RWMUX_NONE: -  break; -  case RWMUX_READ: -  rwmutex->readers--; -  break; -  case RWMUX_DOWNGRADED: -  rwmutex->writers = 0; -  rwmutex->readers--; -  break; -  case RWMUX_WRITE: -  rwmutex->writers = 0; -  break; -  } -  -  THIS_RWKEY->kind = RWMUX_NONE; -  -  co_broadcast(&rwmutex->condition); -  -  if (rwmutex_obj) { -  free_object(rwmutex_obj); -  } -  -  /* 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(); -  } - } -  - static void f_rwmutex_key_create(INT32 args) - { -  struct object *rwmutex_obj = NULL; -  struct rwmutex_storage *rwmutex = NULL; -  struct rwmutex_key_storage *m; -  enum rwmux_kind kind = RWMUX_READ; -  int self_count = 0; -  -  get_all_args("create", args, "%o.%d", &rwmutex_obj, &kind); -  if (!(rwmutex = get_storage(rwmutex_obj, rwmutex_program))) { -  SIMPLE_ARG_TYPE_ERROR("create", 1, "RWMutex"); -  } -  -  while (THIS_RWKEY->rwmutex) { -  /* Not really a supported operation, but... */ -  exit_rwmutex_key_obj(NULL); -  } -  -  for (m = rwmutex->key; m; m = m->next) { -  if ((m->owner == Pike_interpreter.thread_state) && (m->owner)) { -  self_count++; -  } -  } -  -  if (self_count) { -  /* 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"); -  } -  -  init_rwmutex_key_obj(NULL); -  -  THIS_RWKEY->rwmutex = rwmutex; -  THIS_RWKEY->rwmutex_obj = rwmutex_obj; -  add_ref(rwmutex_obj); -  -  /* Link */ -  THIS_RWKEY->prev = NULL; -  if ((THIS_RWKEY->next = rwmutex->key)) { -  rwmutex->key->prev = THIS_RWKEY; -  } -  rwmutex->key = THIS_RWKEY; -  -  THIS_RWKEY->kind = RWMUX_NONE; /* No lock taken (yet). */ -  -  switch(kind) { -  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; -  -  case RWMUX_READ: -  /* States: -  * -  * readers writers self_count Action -  * - <=0 - Add reader. -  * (No active writers.) -  * 0 1 0 Wait for writers to go to 0. -  * (Some other thread is attempting to -  * take a write lock.) -  * >0 1 >0 Add reader. -  * (We already have a lock, which the other -  * thread is waiting for, so avoid dead lock.) -  * - >1 0 Wait for writers to go to 0. -  * (Some other thread has a write lock.) -  * - >1 >0 Add reader. -  * (As we have a lock and there is at least -  * one write lock, we must be the owners -  * of the write lock.) -  */ -  -  SWAP_OUT_CURRENT_THREAD(); -  while(rwmutex->writers > 0) { -  co_wait_interpreter(&rwmutex->condition); -  } -  SWAP_IN_CURRENT_THREAD(); -  -  rwmutex->readers++; -  THIS_RWKEY->kind = RWMUX_READ; -  break; -  -  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; -  -  case RWMUX_WRITE: -  /* States: -  * -  * readers writers self_count Action -  * - 0 - Set writers to 1, wait for readers -  * to go to 0, Set writers to 2. -  * (No write lock active.) -  * - -1 0 Wait for writers to go to 0, then as above. -  * (Some other thread has a degraded write -  * lock.) -  * - 1 0 Wait for writers to go to 0, then as above. -  * (Some other thread is attempting to -  * get a write lock.) -  * - >1 0 Wait for writers to go to 0, then as above. -  * (Some other thread has a write lock.) -  * - >1 >0 Add writer. -  * (As we have a lock and there is at least -  * one write lock, we must be the owners -  * of the write lock.) -  * -  * Problem: -  * >0 1 >0 (Some other thread is attempting to get -  * 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.) -  */ -  -  SWAP_OUT_CURRENT_THREAD(); -  while(rwmutex->writers) { -  co_wait_interpreter(&rwmutex->condition); -  } -  -  rwmutex->writers = 1; -  -  while(rwmutex->readers) { -  co_wait_interpreter(&rwmutex->condition); -  } -  -  rwmutex->writers = 2; -  SWAP_IN_CURRENT_THREAD(); -  THIS_RWKEY->kind = RWMUX_WRITE; -  break; -  } - } -  - /*! @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; -  -  co_broadcast(&THIS_RWKEY->rwmutex->condition); - } -  - /*! @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 -  *! @[try_upgrade()], @[downgrade()] -  */ - 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; - } -  - /*! @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); - } -  - static void f_rwmutex_key__sprintf(INT32 args) - { -  int c = 0; -  get_all_args("_sprintf", args, "%d", &c); -  if (c != 'O') { -  push_undefined(); -  return; -  } -  if (THIS_RWKEY->rwmutex_obj) { -  push_text("Thread.RWKey(/* %O, %s */)"); -  ref_push_object(THIS_RWKEY->rwmutex_obj); -  switch(THIS_RWKEY->kind) { -  case RWMUX_NONE: -  push_text("Unlocked"); -  break; -  case RWMUX_READ: -  push_text("Read-lock"); -  break; -  case RWMUX_DOWNGRADED: -  push_text("Downgraded-write-lock"); -  break; -  case RWMUX_WRITE: -  push_text("Write-lock"); -  break; -  } -  f_sprintf(3); -  } else { -  push_text("Thread.RWKey(/* Unlocked */)"); -  } - } -  - /*! @endclass -  */ -  +    /*! @class Condition    *!    *! Implementation of condition variables.    *!    *! Condition variables are used by threaded programs    *! to wait for events happening in other threads.    *!    *! 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
pike.git/src/threads.c:4917:    tFunc(tNone,tObjIs_THREAD_MUTEX_KEY), 0);    ADD_FUNCTION("_sprintf",f_mutex__sprintf,tFunc(tInt,tStr),0);    ADD_FUNCTION("condition", f_mutex_cond,    tFunc(tNone, tObjIs_THREAD_CONDITION), 0);    set_init_callback(init_mutex_obj);    set_exit_callback(exit_mutex_obj);    mutex_program = Pike_compiler->new_program;    add_ref(mutex_program);    end_class("mutex", 0);    -  START_NEW_PROGRAM_ID(THREAD_RWMUTEX_KEY); -  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); -  set_exit_callback(exit_rwmutex_key_obj); -  ADD_FUNCTION("create", f_rwmutex_key_create, -  tFunc(tOr(tObjIs_THREAD_RWMUTEX, tVoid) -  tOr(tInt02, tVoid), tVoid), -  ID_PROTECTED); -  ADD_FUNCTION("downgrade", f_rwmutex_key_downgrade, -  tFunc(tNone, tVoid), 0); -  ADD_FUNCTION("upgrade", f_rwmutex_key_upgrade, -  tFunc(tNone, tVoid), 0); -  ADD_FUNCTION("try_upgrade", f_rwmutex_key_try_upgrade, -  tFunc(tNone, tInt01), 0); -  ADD_FUNCTION("_sprintf", f_rwmutex_key__sprintf, -  tFunc(tOr(tInt, tVoid), tStr), ID_PROTECTED); -  rwmutex_key_program = Pike_compiler->new_program; -  add_ref(rwmutex_key_program); -  end_class("RWKey", 0); -  rwmutex_key_program->flags |= PROGRAM_DESTRUCT_IMMEDIATE; -  -  START_NEW_PROGRAM_ID(THREAD_RWMUTEX); -  ADD_STORAGE(struct rwmutex_storage); -  ADD_FUNCTION("read_lock", f_rwmutex_read_lock, -  tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0); -  ADD_FUNCTION("try_read_lock", f_rwmutex_try_read_lock, -  tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0); -  ADD_FUNCTION("write_lock", f_rwmutex_write_lock, -  tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0); -  ADD_FUNCTION("try_write_lock", f_rwmutex_try_write_lock, -  tFunc(tNone, tObjIs_THREAD_RWMUTEX_KEY), 0); -  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); -  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); -  +     START_NEW_PROGRAM_ID(THREAD_CONDITION);    ADD_STORAGE(struct pike_cond);    PIKE_MAP_VARIABLE("_mutex", OFFSETOF(pike_cond, mutex_obj),    tObjIs_THREAD_MUTEX, T_OBJECT, ID_PROTECTED|ID_PRIVATE);    ADD_FUNCTION("create", f_cond_create,    tFunc(tOr(tObjIs_THREAD_MUTEX, tVoid), tVoid),    ID_PROTECTED);    ADD_FUNCTION("wait",f_cond_wait,    tOr(tFunc(tObjIs_THREAD_MUTEX_KEY tOr3(tVoid, tIntPos, tFloat),    tVoid),
pike.git/src/threads.c:5145:       destruct_objects_to_destruct_cb();    }       if(condition_program)    {    free_program(condition_program);    condition_program = NULL;    }    -  if(rwmutex_program) -  { -  free_program(rwmutex_program); -  rwmutex_program = NULL; -  } -  -  if(rwmutex_key_program) -  { -  free_program(rwmutex_key_program); -  rwmutex_key_program = NULL; -  } -  +     if(mutex_program)    {    free_program(mutex_program);    mutex_program = NULL;    }       if(mutex_key)    {    free_program(mutex_key);    mutex_key=0;