#include <unistd.h>
#include <sys/mman.h>
-#include "urcu/wfqueue.h"
+#include "urcu/wfcqueue.h"
#include "urcu/map/urcu-bp.h"
#include "urcu/static/urcu-bp.h"
#include "urcu-pointer.h"
+#include "urcu/tls-compat.h"
+
+#include "urcu-die.h"
/* Do not #define _LGPL_SOURCE to ensure we can emit the wrapper symbols */
#undef _LGPL_SOURCE
#define MAP_ANONYMOUS MAP_ANON
#endif
-#ifndef __linux__
+#ifdef __linux__
+static
+void *mremap_wrapper(void *old_address, size_t old_size,
+ size_t new_size, int flags)
+{
+ return mremap(old_address, old_size, new_size, flags);
+}
+#else
#define MREMAP_MAYMOVE 1
#define MREMAP_FIXED 2
* mremap wrapper for non-Linux systems. Maps a RW, anonymous private mapping.
* This is not generic.
*/
-void *mremap(void *old_address, size_t old_size, size_t new_size, int flags)
+static
+void *mremap_wrapper(void *old_address, size_t old_size,
+ size_t new_size, int flags)
{
void *new_address;
static pthread_mutex_t rcu_gp_lock = PTHREAD_MUTEX_INITIALIZER;
#ifdef DEBUG_YIELD
-unsigned int yield_active;
-unsigned int __thread rand_yield;
+unsigned int rcu_yield_active;
+DEFINE_URCU_TLS(unsigned int, rcu_rand_yield);
#endif
-/*
- * Global grace period counter.
- * Contains the current RCU_GP_CTR_PHASE.
- * Also has a RCU_GP_COUNT of 1, to accelerate the reader fast path.
- * Written to only by writer with mutex taken. Read by both writer and readers.
- */
-long rcu_gp_ctr = RCU_GP_COUNT;
+struct rcu_gp rcu_gp = { .ctr = RCU_GP_COUNT };
/*
* Pointer to registry elements. Written to only by each individual reader. Read
* by both the reader and the writers.
*/
-struct rcu_reader __thread *rcu_reader;
+DEFINE_URCU_TLS(struct rcu_reader *, rcu_reader);
static CDS_LIST_HEAD(registry);
#ifndef DISTRUST_SIGNALS_EXTREME
ret = pthread_mutex_lock(mutex);
- if (ret) {
- perror("Error in pthread mutex lock");
- exit(-1);
- }
+ if (ret)
+ urcu_die(ret);
#else /* #ifndef DISTRUST_SIGNALS_EXTREME */
while ((ret = pthread_mutex_trylock(mutex)) != 0) {
- if (ret != EBUSY && ret != EINTR) {
- printf("ret = %d, errno = %d\n", ret, errno);
- perror("Error in pthread mutex lock");
- exit(-1);
- }
+ if (ret != EBUSY && ret != EINTR)
+ urcu_die(ret);
poll(NULL,0,10);
}
#endif /* #else #ifndef DISTRUST_SIGNALS_EXTREME */
int ret;
ret = pthread_mutex_unlock(mutex);
- if (ret) {
- perror("Error in pthread mutex unlock");
- exit(-1);
- }
+ if (ret)
+ urcu_die(ret);
}
-void update_counter_and_wait(void)
+static void wait_for_readers(struct cds_list_head *input_readers,
+ struct cds_list_head *cur_snap_readers,
+ struct cds_list_head *qsreaders)
{
- CDS_LIST_HEAD(qsreaders);
int wait_loops = 0;
struct rcu_reader *index, *tmp;
- /* Switch parity: 0 -> 1, 1 -> 0 */
- CMM_STORE_SHARED(rcu_gp_ctr, rcu_gp_ctr ^ RCU_GP_CTR_PHASE);
-
/*
- * Must commit qparity update to memory before waiting for other parity
- * quiescent state. Failure to do so could result in the writer waiting
- * forever while new readers are always accessing data (no progress).
- * Ensured by CMM_STORE_SHARED and CMM_LOAD_SHARED.
- */
-
- /*
- * Adding a cmm_smp_mb() which is _not_ formally required, but makes the
- * model easier to understand. It does not have a big performance impact
- * anyway, given this is the write-side.
- */
- cmm_smp_mb();
-
- /*
- * Wait for each thread rcu_reader.ctr count to become 0.
+ * Wait for each thread URCU_TLS(rcu_reader).ctr to either
+ * indicate quiescence (not nested), or observe the current
+ * rcu_gp.ctr value.
*/
for (;;) {
wait_loops++;
- cds_list_for_each_entry_safe(index, tmp, ®istry, node) {
- if (!rcu_old_gp_ongoing(&index->ctr))
- cds_list_move(&index->node, &qsreaders);
+ cds_list_for_each_entry_safe(index, tmp, input_readers, node) {
+ switch (rcu_reader_state(&index->ctr)) {
+ case RCU_READER_ACTIVE_CURRENT:
+ if (cur_snap_readers) {
+ cds_list_move(&index->node,
+ cur_snap_readers);
+ break;
+ }
+ /* Fall-through */
+ case RCU_READER_INACTIVE:
+ cds_list_move(&index->node, qsreaders);
+ break;
+ case RCU_READER_ACTIVE_OLD:
+ /*
+ * Old snapshot. Leaving node in
+ * input_readers will make us busy-loop
+ * until the snapshot becomes current or
+ * the reader becomes inactive.
+ */
+ break;
+ }
}
- if (cds_list_empty(®istry)) {
+ if (cds_list_empty(input_readers)) {
break;
} else {
if (wait_loops == RCU_QS_ACTIVE_ATTEMPTS)
caa_cpu_relax();
}
}
- /* put back the reader list in the registry */
- cds_list_splice(&qsreaders, ®istry);
}
void synchronize_rcu(void)
{
+ CDS_LIST_HEAD(cur_snap_readers);
+ CDS_LIST_HEAD(qsreaders);
sigset_t newmask, oldmask;
int ret;
- ret = sigemptyset(&newmask);
+ ret = sigfillset(&newmask);
assert(!ret);
- ret = pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
+ ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);
assert(!ret);
mutex_lock(&rcu_gp_lock);
rcu_gc_registry();
/*
- * Wait for previous parity to be empty of readers.
+ * Wait for readers to observe original parity or be quiescent.
*/
- update_counter_and_wait(); /* 0 -> 1, wait readers in parity 0 */
+ wait_for_readers(®istry, &cur_snap_readers, &qsreaders);
/*
* Adding a cmm_smp_mb() which is _not_ formally required, but makes the
*/
cmm_smp_mb();
+ /* Switch parity: 0 -> 1, 1 -> 0 */
+ CMM_STORE_SHARED(rcu_gp.ctr, rcu_gp.ctr ^ RCU_GP_CTR_PHASE);
+
+ /*
+ * Must commit qparity update to memory before waiting for other parity
+ * quiescent state. Failure to do so could result in the writer waiting
+ * forever while new readers are always accessing data (no progress).
+ * Ensured by CMM_STORE_SHARED and CMM_LOAD_SHARED.
+ */
+
/*
- * Wait for previous parity to be empty of readers.
+ * Adding a cmm_smp_mb() which is _not_ formally required, but makes the
+ * model easier to understand. It does not have a big performance impact
+ * anyway, given this is the write-side.
*/
- update_counter_and_wait(); /* 1 -> 0, wait readers in parity 1 */
+ cmm_smp_mb();
+
+ /*
+ * Wait for readers to observe new parity or be quiescent.
+ */
+ wait_for_readers(&cur_snap_readers, NULL, &qsreaders);
+
+ /*
+ * Put quiescent reader list back into registry.
+ */
+ cds_list_splice(&qsreaders, ®istry);
/*
* Finish waiting for reader threads before letting the old ptr being
_rcu_read_unlock();
}
+int rcu_read_ongoing(void)
+{
+ return _rcu_read_ongoing();
+}
+
/*
* only grow for now.
*/
MAP_ANONYMOUS | MAP_PRIVATE,
-1, 0);
else
- new_arena = mremap(arena->p, arena->len,
- len, MREMAP_MAYMOVE);
+ new_arena = mremap_wrapper(arena->p, arena->len,
+ len, MREMAP_MAYMOVE);
assert(new_arena != MAP_FAILED);
/*
rcu_reader_reg->tid = pthread_self();
assert(rcu_reader_reg->ctr == 0);
cds_list_add(&rcu_reader_reg->node, ®istry);
- rcu_reader = rcu_reader_reg;
+ URCU_TLS(rcu_reader) = rcu_reader_reg;
}
/* Called with signals off and mutex locked */
sigset_t newmask, oldmask;
int ret;
- ret = sigemptyset(&newmask);
+ ret = sigfillset(&newmask);
assert(!ret);
- ret = pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
+ ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);
assert(!ret);
/*
* Check if a signal concurrently registered our thread since
* the check in rcu_read_lock(). */
- if (rcu_reader)
+ if (URCU_TLS(rcu_reader))
goto end;
mutex_lock(&rcu_gp_lock);
sigset_t newmask, oldmask;
int ret;
- ret = sigemptyset(&newmask);
+ ret = sigfillset(&newmask);
assert(!ret);
- ret = pthread_sigmask(SIG_SETMASK, &newmask, &oldmask);
+ ret = pthread_sigmask(SIG_BLOCK, &newmask, &oldmask);
assert(!ret);
mutex_lock(&rcu_gp_lock);
saved_fork_signal_mask = oldmask;
assert(!ret);
}
+void *rcu_dereference_sym_bp(void *p)
+{
+ return _rcu_dereference(p);
+}
+
void *rcu_set_pointer_sym_bp(void **p, void *v)
{
cmm_wmb();
- return uatomic_set(p, v);
+ uatomic_set(p, v);
+ return v;
}
void *rcu_xchg_pointer_sym_bp(void **p, void *v)
return uatomic_cmpxchg(p, old, _new);
}
+DEFINE_RCU_FLAVOR(rcu_flavor);
+
#include "urcu-call-rcu-impl.h"
#include "urcu-defer-impl.h"