pike.git / src / threads.c

version» Context lines:

pike.git/src/threads.c:2125:   {    COND_T condition;    struct key_storage *key;    int num_waiting;   };      enum key_kind   {    KEY_UNINITIALIZED = 0,    KEY_INITIALIZED, +  KEY_PENDING, +  KEY_EXCLUSIVE,   };      struct key_storage   {    struct object *self; /* NOT reference-counted! */    struct mutex_storage *mut;    struct object *mutex_obj;    struct thread_state *owner;    struct object *owner_obj; -  +  struct key_storage *prev; +  struct key_storage *next;    enum key_kind kind;   };      #define OB2KEY(X) ((struct key_storage *)((X)->storage))      /*! @class Mutex    *!    *! @[Mutex] is a class that implements mutual exclusion locks.    *! Mutex locks are used to prevent multiple threads from simultaneously    *! execute sections of code which access or change shared data. The basic
pike.git/src/threads.c:2243:    }    case 1:    break;    }       /* Needs to be cloned here, since create()    * might use threads.    */    o=fast_clone_object(mutex_key);    +  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; +     DEBUG_CHECK_THREAD();    -  if(m->key) +  if(key->next)    {    m->num_waiting++;    if(threads_disabled)    {    free_object(o);    Pike_error("Cannot wait for mutexes when threads are disabled!\n");    }    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(m->key); +  } while (key->next);    m->num_waiting--;    }      #ifdef PICKY_MUTEX    if (!Pike_fp->current_object->prog) {    free_object (o);    if (!m->num_waiting) {    co_destroy (&m->condition);    }    Pike_error ("Mutex was destructed while waiting for lock.\n");    }   #endif    -  m->key = OB2KEY(o); -  OB2KEY(o)->mut=m; -  add_ref (OB2KEY(o)->mutex_obj = Pike_fp->current_object); +  key->kind = KEY_EXCLUSIVE;       DEBUG_CHECK_THREAD();       THREADS_FPRINTF(1, "LOCK k:%p, m:%p(%p), t:%p\n", -  OB2KEY(o), m, OB2KEY(m->key)->mut, +  key, m, key->mut,    Pike_interpreter.thread_state);    pop_n_elems(args);    push_object(o);   }      /*! @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
pike.git/src/threads.c:2323:    else    get_all_args(NULL, args, "%i", &type);       switch(type)    {    default:    bad_arg_error("trylock", args, 2, "int(0..2)", Pike_sp+1-args,    "Unknown mutex locking style: %"PRINTPIKEINT"d\n",type);       case 0: -  if(m->key && m->key->owner == Pike_interpreter.thread_state) +  for (key = m->key; key; key = key->next) { +  if(key->owner == Pike_interpreter.thread_state)    {    Pike_error("Recursive mutex locks!\n");    } -  +  }       case 2:    case 1:    break;    }       o=clone_object(mutex_key,0);    key = OB2KEY(o);       if(!m->key)    {    key->mut = m;    add_ref (key->mutex_obj = Pike_fp->current_object);    m->key = key; -  +  key->kind = KEY_EXCLUSIVE;    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().
pike.git/src/threads.c:2369:    *!    *! 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    *! @[Thread()]    */   PMOD_EXPORT void f_mutex_locking_thread(INT32 args)   {    struct mutex_storage *m = THIS_MUTEX; +  struct key_storage *k;       pop_n_elems(args);    -  if (m->key && m->key->owner) +  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); -  else +  return; +  } +  }    push_int(0);   }      /*! @decl Thread.MutexKey current_locking_key()    *!    *! This mutex method returns the key object currently governing    *! the lock on this mutex. 0 is returned if the mutex isn't locked.    *!    *! @seealso    *! @[Thread()]    */   PMOD_EXPORT void f_mutex_locking_key(INT32 args)   {    struct mutex_storage *m = THIS_MUTEX; -  +  struct key_storage *k;       pop_n_elems(args);    -  if (m->key) +  for (k = m->key; k; k = k->next) { +  if (k->kind == KEY_PENDING) continue;    ref_push_object(m->key->self); -  else +  return; +  }    push_int(0);   }      /*! @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; -  +  struct key_storage *k;    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 (m->key && m->key->owner) { -  push_static_text("Thread.Mutex(/*locked by 0x"); +  +  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(3); +  } +  push_static_text(" */)"); +  f_add(Pike_sp - save_sp);    } else {    push_static_text("Thread.Mutex(/* Unlocked */)");    }   }      /*! @decl Condition cond()    *!    *! Create a @[Condition].    *!    *! This function is equivalent to
pike.git/src/threads.c:2475: Inside #if undefined(PICKY_MUTEX)
   }    else {   #ifdef PIKE_DEBUG    if (m->num_waiting)    Pike_error ("key/num_waiting confusion.\n");   #endif    co_destroy(& m->condition);    }   #else    if(key) { +  struct key_storage *next = key->next;    m->key=0; -  +  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. */ -  +  }    if(m->num_waiting)    {    THREADS_FPRINTF(1, "Destructed mutex is being waited on.\n");    THREADS_ALLOW();    /* exit_mutex_key_obj has already signalled, but since the    * waiting threads will throw an error instead of making a new    * lock we need to double it to a broadcast. The last thread    * that stops waiting will destroy m->condition. */    co_broadcast (&m->condition);   
pike.git/src/threads.c:2532:    *! @[Mutex], @[Condition]    */   #define THIS_KEY ((struct key_storage *)(CURRENT_STORAGE))   void init_mutex_key_obj(struct object *UNUSED(o))   {    THREADS_FPRINTF(1, "KEY k:%p, t:%p\n",    THIS_KEY, Pike_interpreter.thread_state);   #ifdef PIKE_NULL_IS_SPECIAL    THIS_KEY->mut=0;    THIS_KEY->mutex_obj = NULL; +  THIS_KEY->prev = NULL; +  THIS_KEY->next = NULL;   #endif    THIS_KEY->self = Pike_fp->current_object;    THIS_KEY->owner = Pike_interpreter.thread_state;    THIS_KEY->owner_obj = Pike_interpreter.thread_state->thread_obj;    if (THIS_KEY->owner_obj)    add_ref(THIS_KEY->owner_obj);    THIS_KEY->kind = KEY_INITIALIZED;   }      void exit_mutex_key_obj(struct object *DEBUGUSED(o))   {    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);    if(THIS_KEY->mut)    {    struct mutex_storage *mut = THIS_KEY->mut;    struct key_storage *key = THIS_KEY;    struct object *mutex_obj;    -  +  /* Unlink */ +  if (key->prev) {   #ifdef PIKE_DEBUG -  +  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", -  mut->key, key); +  key->mut->key, key);   #endif -  mut->key=0; +  if ((mut->key = key->next)) { +  key->next->prev = NULL; +  } +  } +  key->next = NULL; +     if (THIS_KEY->owner) {    THIS_KEY->owner = NULL;    }    if (THIS_KEY->owner_obj) {    free_object(THIS_KEY->owner_obj);    THIS_KEY->owner_obj=0;    }    THIS_KEY->mut=0;    THIS_KEY->kind = KEY_UNINITIALIZED;    mutex_obj = THIS_KEY->mutex_obj;    THIS_KEY->mutex_obj = NULL;       if (mut->num_waiting) {    THREADS_ALLOW(); -  co_signal(&mut->condition); +  co_broadcast(&mut->condition);       /* Try to wake up the waiting thread(s) immediately    * in an attempt to avoid starvation.    */   #ifdef HAVE_NO_YIELD    sleep(0);   #else /* HAVE_NO_YIELD */    th_yield();   #endif /* HAVE_NO_YIELD */    THREADS_DISALLOW();
pike.git/src/threads.c:3362:    Pike_error("Bad argument 1 to wait()\n");    }       if(args > 1) {    pop_n_elems(args - 1);    args = 1;    }       /* Unlock mutex */    mutex_obj = key->mutex_obj; -  mut->key=0; +  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;    key->mut = NULL;    key->mutex_obj = NULL; -  co_signal(& mut->condition); +  key->kind = KEY_INITIALIZED; +  co_broadcast(& mut->condition);       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);    }       /* Wait and allow mutex operations */    SWAP_OUT_CURRENT_THREAD();    c->wait_count++;
pike.git/src/threads.c:3390:    }    c->wait_count--;    SWAP_IN_CURRENT_THREAD();       if (!mutex_obj->prog) {    Pike_error("Mutex was destructed while waiting for cond.\n");    }       /* Lock mutex */    mut->num_waiting++; -  while(mut->key) { +  { +  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();    } -  mut->key=key; -  key->mut = mut; -  key->mutex_obj = mutex_obj; +  key->kind = KEY_EXCLUSIVE; +  }    mut->num_waiting--;       pop_stack();    return;   }      /*! @decl void signal()    *!    *! @[signal()] wakes up one of the threads currently waiting for the    *! condition.