Branch: Tag:

2020-06-29

2020-06-29 11:17:55 by Henrik Grubbström (Grubba) <grubba@grubba.org>

Thread.Mutex: Improved behavior for {try_,}shared_lock().

If the current thread already holds a shared lock, a new one
is created without waiting. This avoids some dead locks.

2334:    *! @endint    *!    *! @note +  *! 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    *! 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.    *! -  *! @note -  *! A thread may hold several shared locks concurrently. -  *! +     *! @seealso    *! @[lock()], @[trylock()], @[try_shared_lock()]    */   void f_mutex_shared_lock(INT32 args)   {    struct mutex_storage *m; -  struct key_storage *key; -  struct object *o; -  INT_TYPE type; +  struct key_storage *key, *prev = NULL; +  struct object *o, *prev_o = NULL; +  INT_TYPE type = 0;       DEBUG_CHECK_THREAD();       m=THIS_MUTEX; -  if(!args) -  type=0; -  else -  get_all_args(NULL, args, "%i", &type); +  get_all_args(NULL, args, ".%i", &type);    -  switch(type) -  { -  default: +  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); +  "Unknown mutex locking style: %"PRINTPIKEINT"d\n", type); +  }    -  -  case 0: -  case 2: -  if (m->key && (m->key->kind >= KEY_PENDING)) { -  /* There are active or waiting exclusive locks. */ +     for (key = m->key; key; key = key->next) { -  if(key->owner == Pike_interpreter.thread_state) -  { +  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);    -  if(type==0) { +  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;    }    } -  } -  case 1: +  +  prev = key; +  prev_o = prev->self; +  add_ref(prev_o);    break;    } -  +  }       /* Needs to be cloned here, since create()    * might use threads.
2401:       DEBUG_CHECK_THREAD();    +  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; +  } +  } +  } +  +  if (!prev) {    while (m->key && (m->key->kind >= KEY_PENDING)) {    if(threads_disabled)    {
2418:       m->num_waiting--;    } +  }      #ifdef PICKY_MUTEX    if (!Pike_fp->current_object->prog) {
2431:    }   #endif    -  /* Two cases here: +  /* Three cases here:    * -  * mut->key == NULL -  * mut->key && mut->key->kind == KEY_READ +  * prev != NULL +  * prev == NULL && m->key == NULL +  * prev == NULL && m->key && m->key->kind == KEY_READ    */    key = OB2KEY(o);    key->mut = m;    add_ref (key->mutex_obj = Pike_fp->current_object); -  +  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; -  +  }    key->kind = KEY_SHARED;       DEBUG_CHECK_THREAD();
2543:   void f_mutex_try_shared_lock(INT32 args)   {    struct mutex_storage *m; -  struct key_storage *key; +  struct key_storage *key, *prev = NULL;    struct object *o; -  INT_TYPE type; +  INT_TYPE type = 0;    int i=0;       /* No reason to release the interpreter lock here
2554:       m=THIS_MUTEX;    -  if(!args) -  type=0; -  else -  get_all_args(NULL, args, "%i", &type); +  get_all_args(NULL, args, ".%i", &type);    -  switch(type) -  { -  default: +  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); -  +  }    -  case 0: +  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);    Pike_error("Recursive mutex locks!\n");    } -  +  continue;    } -  -  case 2: -  case 1: +  prev = key;    break;    } -  +  }    -  o=clone_object(mutex_key,0); +     key = OB2KEY(o);    -  if (!m->key || (m->key->kind < KEY_PENDING)) -  { +  if (prev) {    key->mut = m;    add_ref (key->mutex_obj = Pike_fp->current_object); -  +  key->prev = prev; +  if ((key->next = prev)) { +  prev->next->prev = key; +  } +  prev->next = key; +  key->kind = KEY_SHARED; +  i=1; +  } else if (!m->key || (m->key->kind < KEY_PENDING)) { +  key->mut = m; +  add_ref (key->mutex_obj = Pike_fp->current_object);    if ((key->next = m->key)) {    key->next->prev = key;    }