pike.git / src / threads.c

version» Context lines:

pike.git/src/threads.c:2639:   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 ReadKey read_lock() + /*! @decl ReadKey read_lock(int(0..1)|void recursive)    *!    *! Obtain a lock for reading (shared lock).    *! -  +  *! @param recursive +  *! Support the same thread obtaining several locks +  *! (both read and write). +  *! +  *! @throws +  *! Throws an error if the current thread already has +  *! a lock (read or write) unless @[recursive] is @expr{1@}. +  *!    *! @seealso    *! @[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_object(clone_object(rwmutex_rkey_program, 1));   }    - /*! @decl WriteKey write_lock() + /*! @decl WriteKey write_lock(int(0..1)|void recursive)    *!    *! Obtain a lock for writing (exclusive lock).    *! -  +  *! @param recursive +  *! Support the same thread obtaining several locks +  *! (both read and write). +  *! +  *! @throws +  *! Throws an error if the current thread already has +  *! a lock (read or write) unless @[recursive] is @expr{1@}. +  *! +  *! If @[recursive] is @expr{1@} it will still throw an error +  *! if we have a read lock and another thread has started +  *! taking a write lock. +  *!    *! @seealso    *! @[read_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_object(clone_object(rwmutex_wkey_program, 1));   }
pike.git/src/threads.c:2765:    rwmutex->first_key = THIS_RWKEY->next;    }    if (THIS_RWKEY->next) {    THIS_RWKEY->next->prev = THIS_RWKEY->prev;    } else {    rwmutex->last_key = THIS_RWKEY->prev;    }    THIS_RWKEY->next = NULL;    THIS_RWKEY->prev = NULL;    -  if (!(--rwmutex->readers)) { +  if (!--rwmutex->readers) {    co_broadcast(&rwmutex->condition); -  +     if (rwmutex->writers) { -  /* There's a writer waiting for us to complete. +  /* There's probably a writer waiting for us to complete.    * Attempt to force a thread switch.    */    THREADS_ALLOW();   #ifdef HAVE_NO_YIELD    sleep(0);   #else    th_yield();   #endif    THREADS_DISALLOW();    }
pike.git/src/threads.c:2792:    free_object(rwmutex_obj);    }    }   }      static void f_rwmutex_rkey_create(INT32 args)   {    struct object *rwmutex_obj = NULL;    struct rwmutex_storage *rwmutex = NULL;    struct rwmutex_key_storage *m; +  int recursive = 0; +  int self_count = 0;    -  get_all_args("create", args, "%o", &rwmutex_obj); +  get_all_args("create", args, "%o.%d", &rwmutex_obj, &recursive);    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_rkey_obj(NULL); +  } +     for (m = rwmutex->first_key; m; m = m->next) {    if ((m->owner == Pike_interpreter.thread_state) && (m->owner)) { -  +  self_count++; +  } +  } +  +  if (self_count && recursive) {    /* 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");    } -  } +     -  while (THIS_RWKEY->rwmutex) { -  /* Not really a supported operation, but... */ -  exit_rwmutex_rkey_obj(NULL); -  } -  +     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->first_key)) {    rwmutex->first_key->prev = THIS_RWKEY;    }    rwmutex->first_key = THIS_RWKEY;    -  +  /* States: +  * +  * readers writers self_count Action +  * - 0 - Add reader. +  * (No 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.) +  * - >0 0 Wait for writers to go to 0. +  * (Some other thread has a write lock.) +  * - >0 >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.) +  */ +  +  if (!self_count) {    SWAP_OUT_CURRENT_THREAD();    while(rwmutex->writers) {    co_wait_interpreter(&rwmutex->condition);    }    SWAP_IN_CURRENT_THREAD(); -  +  }       rwmutex->readers++;   }      static void f_rwmutex_rkey__sprintf(INT32 args)   {    int c = 0;    get_all_args("_sprintf", args, "%d", &c);    if (c != 'O') {    push_undefined();
pike.git/src/threads.c:2890:    rwmutex->first_key = THIS_RWKEY->next;    }    if (THIS_RWKEY->next) {    THIS_RWKEY->next->prev = THIS_RWKEY->prev;    } else {    rwmutex->last_key = THIS_RWKEY->prev;    }    THIS_RWKEY->next = NULL;    THIS_RWKEY->prev = NULL;    -  rwmutex->writers = 0; +  rwmutex->writers--;    co_broadcast(&rwmutex->condition);       /* Attempt to force a thread switch in case there is -  * ayone waiting on us. +  * anyone waiting on us.    */    THREADS_ALLOW();   #ifdef HAVE_NO_YIELD    sleep(0);   #else    th_yield();   #endif    THREADS_DISALLOW();       if (rwmutex_obj) {    free_object(rwmutex_obj);    }    }   }      static void f_rwmutex_wkey_create(INT32 args)   {    struct object *rwmutex_obj = NULL;    struct rwmutex_storage *rwmutex = NULL; -  +  struct rwmutex_key_storage *m; +  int recursive = 0; +  int self_count = 0;    -  get_all_args("create", args, "%o", &rwmutex_obj); +  get_all_args("create", args, "%o.%d", &rwmutex_obj, &recursive);    if (!(rwmutex = get_storage(rwmutex_obj, rwmutex_program))) {    SIMPLE_ARG_TYPE_ERROR("create", 1, "RWMutex");    }    -  if (rwmutex->first_key) { -  struct rwmutex_key_storage *m; +  while (THIS_RWKEY->rwmutex) { +  /* Not really a supported operation, but... */ +  exit_rwmutex_wkey_obj(NULL); +  } +     for (m = rwmutex->first_key; m; m = m->next) {    if (m->owner && (m->owner = Pike_interpreter.thread_state)) { -  /* We already have a lock (read or write), and would -  * hang waiting on ourselves. -  */ -  Pike_error("Recursive mutex locks!\n"); +  self_count++;    }    } -  } +     -  while (THIS_RWKEY->rwmutex) { -  /* Not really a supported operation, but... */ -  exit_rwmutex_wkey_obj(NULL); +  if (self_count && (!recursive || (rwmutex->writers < 0))) { +  /* Either: We already have a lock (read or write), and +  * would hang waiting on ourselves. +  * +  * Or: We already have a lock and some other thread is +  * already attempting to get a write lock. This +  * would end up in a hang waiting on the other +  * thread while it is waiting on us. +  */ +  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->first_key)) {    rwmutex->first_key->prev = THIS_RWKEY;    }    rwmutex->first_key = THIS_RWKEY;    -  +  /* States: +  * +  * readers writers self_count Action +  * - 0 - Set writers to -1, wait for readers +  * to go to 0, Set writers to 1. +  * (No write lock active.) +  * - -1 0 Wait for writers to go to 0, then as above. +  * (Some other thread is attempting to +  * get a write lock.) +  * - >0 0 Wait for writers to go to 0, then as above. +  * (Some other thread has a write lock.) +  * - >0 >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(); -  +  if (!self_count) {    while(rwmutex->writers) {    co_wait_interpreter(&rwmutex->condition);    } -  +  }    -  +  if (!rwmutex->writers) {    rwmutex->writers = -1;    -  while(rwmutex->readers) { +  while(rwmutex->readers > self_count) {    co_wait_interpreter(&rwmutex->condition);    }       rwmutex->writers = 1; -  +  } else { +  /* We already hold a write lock. */ +  rwmutex->writers++; +  }    SWAP_IN_CURRENT_THREAD();   }      static void f_rwmutex_wkey__sprintf(INT32 args)   {    int c = 0;    get_all_args("_sprintf", args, "%d", &c);    if (c != 'O') {    push_undefined();    return;