struct test_array {
int a;
- int b;
- char c[200];
};
static struct test_array *test_rcu_pointer;
+static unsigned long duration;
+static time_t start_time;
+static unsigned long __thread duration_interval;
+#define DURATION_TEST_DELAY 100
+
+/*
+ * returns 0 if test should end.
+ */
+static int test_duration(void)
+{
+ if (duration_interval++ >= DURATION_TEST_DELAY) {
+ duration_interval = 0;
+ if (time(NULL) - start_time >= duration)
+ return 0;
+ }
+ return 1;
+}
+
#define NR_READ 10
#define NR_WRITE 9
void *thr_reader(void *arg)
{
- int qparity, i, j;
+ int qparity;
struct test_array *local_ptr;
- printf("thread %s, thread id : %lx, tid %lu\n",
+ printf("thread_begin %s, thread id : %lx, tid %lu\n",
"reader", pthread_self(), (unsigned long)gettid());
- sleep(2);
urcu_register_thread();
- for (i = 0; i < 100000; i++) {
- for (j = 0; j < 100000000; j++) {
- rcu_read_lock(&qparity);
- 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);
- }
+ for (;;) {
+ rcu_read_lock(&qparity);
+ local_ptr = rcu_dereference(test_rcu_pointer);
+ if (local_ptr)
+ assert(local_ptr->a == 8);
+ rcu_read_unlock(&qparity);
+ if (!test_duration())
+ break;
}
urcu_unregister_thread();
+ printf("thread_end %s, thread id : %lx, tid %lu\n",
+ "reader", pthread_self(), (unsigned long)gettid());
return ((void*)1);
}
void *thr_writer(void *arg)
{
- int i;
struct test_array *new, *old;
- printf("thread %s, thread id : %lx, tid %lu\n",
+ printf("thread_begin %s, thread id : %lx, tid %lu\n",
"writer", pthread_self(), (unsigned long)gettid());
- sleep(2);
- for (i = 0; i < 10000000; i++) {
+ for (;;) {
new = malloc(sizeof(struct test_array));
rcu_copy_mutex_lock();
old = test_rcu_pointer;
- if (old) {
+ if (old)
assert(old->a == 8);
- assert(old->b == 12);
- assert(old->c[55] == 2);
- }
- new->c[55] = 2;
- new->b = 12;
new->a = 8;
old = urcu_publish_content((void **)&test_rcu_pointer, new);
rcu_copy_mutex_unlock();
/* can be done after unlock */
- if (old) {
+ if (old)
old->a = 0;
- old->b = 0;
- old->c[55] = 0;
- }
free(old);
+ if (!test_duration())
+ break;
usleep(1);
}
+ printf("thread_end %s, thread id : %lx, tid %lu\n",
+ "writer", pthread_self(), (unsigned long)gettid());
return ((void*)2);
}
-int main()
+int main(int argc, char **argv)
{
int err;
pthread_t tid_reader[NR_READ], tid_writer[NR_WRITE];
void *tret;
int i;
+ if (argc < 2) {
+ printf("Usage : %s duration (s) [-r] [-w] "
+ "(yield reader and/or writer)\n", argv[0]);
+ return -1;
+ }
+
+ err = sscanf(argv[1], "%lu", &duration);
+ if (err != 1) {
+ printf("Usage : %s duration (s) [-r] [-w] "
+ "(yield reader and/or writer)\n", argv[0]);
+ return -1;
+ }
+
+#ifdef DEBUG_YIELD
+ for (i = 2; i < argc; i++) {
+ if (argv[i][0] != '-')
+ continue;
+ switch (argv[i][1]) {
+ case 'r':
+ yield_active |= YIELD_READ;
+ break;
+ case 'w':
+ yield_active |= YIELD_WRITE;
+ break;
+ }
+ }
+#endif
+
+ printf("running test for %lu seconds.\n", duration);
+ start_time = time(NULL);
printf("thread %-6s, thread id : %lx, tid %lu\n",
"main", pthread_self(), (unsigned long)gettid());
exit(1);
}
- sleep(10);
-
for (i = 0; i < NR_READ; i++) {
err = pthread_join(tid_reader[i], &tret);
if (err != 0)
int *urcu_active_readers;
};
+#ifdef DEBUG_YIELD
+int yield_active;
+#endif
+
static struct reader_data *reader_data;
static int num_readers, alloc_readers;
static int sig_done;
*/
if (!reader_data)
return;
+ debug_yield_write();
sig_done = 0;
+ debug_yield_write();
mb(); /* write sig_done before sending the signals */
- for (index = reader_data; index < reader_data + num_readers; index++)
+ debug_yield_write();
+ for (index = reader_data; index < reader_data + num_readers; index++) {
pthread_kill(index->tid, SIGURCU);
+ debug_yield_write();
+ }
/*
* Wait for sighandler (and thus mb()) to execute on every thread.
* BUSY-LOOP.
*/
while (sig_done < num_readers)
barrier();
+ debug_yield_write();
mb(); /* read sig_done before ending the barrier */
+ debug_yield_write();
}
void wait_for_quiescent_state(int parity)
/* All threads should read qparity before accessing data structure. */
/* Write ptr before changing the qparity */
force_mb_all_threads();
+ debug_yield_write();
prev_parity = switch_next_urcu_qparity();
+ debug_yield_write();
/*
* Wait for previous parity to be empty of readers.
void synchronize_rcu(void)
{
+ debug_yield_write();
internal_urcu_lock();
+ debug_yield_write();
switch_qparity();
+ debug_yield_write();
switch_qparity();
+ debug_yield_write();
internal_urcu_lock();
+ debug_yield_write();
}
/*
{
void *oldptr;
+ debug_yield_write();
internal_urcu_lock();
+ debug_yield_write();
/*
* We can publish the new pointer before we change the current qparity.
* Readers seeing the new pointer while being in the previous qparity
* when the next quiescent state window will be over.
*/
oldptr = *ptr;
+ debug_yield_write();
*ptr = new;
+ debug_yield_write();
switch_qparity();
+ debug_yield_write();
switch_qparity();
+ debug_yield_write();
internal_urcu_unlock();
+ debug_yield_write();
return oldptr;
}
#define SIGURCU SIGUSR1
+#ifdef DEBUG_YIELD
+#include <sched.h>
+
+#define YIELD_READ (1 << 0)
+#define YIELD_WRITE (1 << 1)
+
+extern int yield_active;
+
+static inline void debug_yield_read(void)
+{
+ if (yield_active & YIELD_READ)
+ sched_yield();
+}
+
+static inline void debug_yield_write(void)
+{
+ if (yield_active & YIELD_WRITE)
+ sched_yield();
+}
+#else
+static inline void debug_yield_read(void)
+{
+}
+
+static inline void debug_yield_write(void)
+{
+}
+#endif
+
/* Global quiescent period parity */
extern int urcu_qparity;
*/
static inline void rcu_read_lock(int *urcu_parity)
{
+ debug_yield_read();
*urcu_parity = get_urcu_qparity();
+ debug_yield_read();
urcu_active_readers[*urcu_parity]++;
+ debug_yield_read();
/*
* Increment active readers count before accessing the pointer.
* See force_mb_all_threads().
*/
barrier();
+ debug_yield_read();
}
static inline void rcu_read_unlock(int *urcu_parity)
{
+ debug_yield_read();
barrier();
+ debug_yield_read();
/*
* Finish using rcu before decrementing the pointer.
* See force_mb_all_threads().
*/
urcu_active_readers[*urcu_parity]--;
+ debug_yield_read();
}
extern void *urcu_publish_content(void **ptr, void *new);