#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
+#include <assert.h>
#include "urcu.h"
-#define NR_READ 10
-#define NR_WRITE 4
+struct test_array {
+ int a;
+ int b;
+ char c[200];
+};
+
+static struct test_array *test_rcu_pointer;
+
+#define NR_READ 1000
+#define NR_WRITE 50
void *thr_reader(void *arg)
{
+ int qparity, i;
+ struct test_array *local_ptr;
+
printf("thread %s, thread id : %lu, pid %lu\n",
"reader", pthread_self(), getpid());
sleep(2);
urcu_register_thread();
-
+ for (i = 0; i < 1000; i++) {
+ qparity = rcu_read_lock();
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ if (local_ptr) {
+ assert(local_ptr->a == 8);
+ assert(local_ptr->b == 12);
+ assert(local_ptr->c[55] == 2);
+ }
+ rcu_read_unlock(qparity);
+ }
urcu_unregister_thread();
+
return ((void*)1);
}
void *thr_writer(void *arg)
{
int i;
+ struct test_array *new, *old;
printf("thread %s, thread id : %lu, pid %lu\n",
"writer", pthread_self(), getpid());
sleep(2);
for (i = 0; i < 1000; i++) {
+ rcu_write_lock();
+ new = malloc(sizeof(struct test_array));
+ old = test_rcu_pointer;
+ if (old) {
+ assert(old->a == 8);
+ assert(old->b == 12);
+ assert(old->c[55] == 2);
+ }
+ assert(new->a = 8);
+ assert(new->b = 12);
+ assert(new->c[55] = 2);
+ old = urcu_publish_content(&test_rcu_pointer, new);
+ rcu_write_unlock();
+ /* can be done after unlock */
+ free(old);
}
return ((void*)2);
static int num_readers, alloc_readers;
static int sig_done;
+void rcu_write_lock(void)
+{
+ int ret;
+ ret = pthread_mutex_lock(&urcu_mutex);
+ if (ret) {
+ perror("Error in pthread mutex lock");
+ exit(-1);
+ }
+}
+
+void rcu_write_unlock(void)
+{
+ int ret;
+
+ ret = pthread_mutex_unlock(&urcu_mutex);
+ if (ret) {
+ perror("Error in pthread mutex unlock");
+ exit(-1);
+ }
+}
+
/*
* called with urcu_mutex held.
*/
/*
* Return old pointer, OK to free, no more reference exist.
+ * Called under rcu_write_lock.
*/
void *urcu_publish_content(void **ptr, void *new)
{
int ret, prev_parity;
void *oldptr;
- ret = pthread_mutex_lock(&urcu_mutex);
- if (ret) {
- perror("Error in pthread mutex lock");
- exit(-1);
- }
-
/*
* We can publish the new pointer before we change the current qparity.
* Readers seeing the new pointer while being in the previous qparity
* Deleting old data is ok !
*/
- ret = pthread_mutex_unlock(&urcu_mutex);
- if (ret) {
- perror("Error in pthread mutex lock");
- exit(-1);
- }
return oldptr;
}
void urcu_register_thread(void)
{
- pthread_t self = pthread_self();
- int ret;
-
- ret = pthread_mutex_lock(&urcu_mutex);
- if (ret) {
- perror("Error in pthread mutex lock");
- exit(-1);
- }
-
- urcu_add_reader(self);
-
-
- ret = pthread_mutex_unlock(&urcu_mutex);
- if (ret) {
- perror("Error in pthread mutex unlock");
- exit(-1);
- }
+ rcu_write_lock();
+ urcu_add_reader(pthread_self());
+ rcu_write_unlock();
}
void urcu_unregister_thread(void)
{
pthread_t self = pthread_self();
- int ret;
-
- ret = pthread_mutex_lock(&urcu_mutex);
- if (ret) {
- perror("Error in pthread mutex lock");
- exit(-1);
- }
-
- urcu_remove_reader(self);
-
- ret = pthread_mutex_unlock(&urcu_mutex);
- if (ret) {
- perror("Error in pthread mutex unlock");
- exit(-1);
- }
-
+ rcu_write_lock();
+ urcu_remove_reader(pthread_self());
+ rcu_write_unlock();
}
void sigurcu_handler(int signo, siginfo_t *siginfo, void *context)
/* Nop everywhere except on alpha. */
#define smp_read_barrier_depends()
+/*
+ * Prevent the compiler from merging or refetching accesses. The compiler
+ * is also forbidden from reordering successive instances of ACCESS_ONCE(),
+ * but only when the compiler is aware of some particular ordering. One way
+ * to make the compiler aware of ordering is to put the two invocations of
+ * ACCESS_ONCE() in different C statements.
+ *
+ * This macro does absolutely -nothing- to prevent the CPU from reordering,
+ * merging, or refetching absolutely anything at any time. Its main intended
+ * use is to mediate communication between process-level code and irq/NMI
+ * handlers, all running on the same CPU.
+ */
+#define ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))
+
+/**
+ * rcu_dereference - fetch an RCU-protected pointer in an
+ * RCU read-side critical section. This pointer may later
+ * be safely dereferenced.
+ *
+ * Inserts memory barriers on architectures that require them
+ * (currently only the Alpha), and, more importantly, documents
+ * exactly which pointers are protected by RCU.
+ */
+
+#define rcu_dereference(p) ({ \
+ typeof(p) _________p1 = ACCESS_ONCE(p); \
+ smp_read_barrier_depends(); \
+ (_________p1); \
+ })
+
#define SIGURCU SIGUSR1
/* Global quiescent period parity */
urcu_active_readers[urcu_parity]--;
}
+extern void rcu_write_lock(void);
+extern void rcu_write_unlock(void);
+
extern void *urcu_publish_content(void **ptr, void *new);
/*