]> granicus.if.org Git - libevent/commitdiff
Add a unit test for conditions
authorNick Mathewson <nickm@torproject.org>
Tue, 17 Aug 2010 02:55:45 +0000 (22:55 -0400)
committerNick Mathewson <nickm@torproject.org>
Tue, 17 Aug 2010 17:21:05 +0000 (13:21 -0400)
test/regress.h
test/regress_pthread.c

index 210b79a75cd40d04793c53e40380ee5b080dc173..f66b4ce02af59a384c63483afcadf8923055c452 100644 (file)
@@ -110,8 +110,11 @@ int _test_ai_eq(const struct evutil_addrinfo *ai, const char *sockaddr_port,
                        goto end;                                       \
        } while (0)
 
+#define test_timeval_diff_leq(tv1, tv2, diff, tolerance)               \
+       tt_int_op(abs(timeval_msec_diff((tv1), (tv2)) - diff), <=, tolerance)
+
 #define test_timeval_diff_eq(tv1, tv2, diff)                           \
-       tt_int_op(abs(timeval_msec_diff((tv1), (tv2)) - diff), <=, 50)
+       test_timeval_diff_leq((tv1), (tv2), (diff), 50)
 
 long timeval_msec_diff(const struct timeval *start, const struct timeval *end);
 
index 4eeef8fedfb5a6f3347dedc2293bf5385f0d9620..c918312c050f49281411297f90fe6b81cec1cef0 100644 (file)
@@ -29,6 +29,7 @@
 #include <sys/types.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 #ifdef _EVENT_HAVE_PTHREADS
 #include <pthread.h>
@@ -69,7 +70,7 @@ struct cond_wait {
 };
 
 static void
-basic_timeout(evutil_socket_t fd, short what, void *arg)
+wake_all_timeout(evutil_socket_t fd, short what, void *arg)
 {
        struct cond_wait *cw = arg;
        EVLOCK_LOCK(cw->lock, 0);
@@ -78,7 +79,17 @@ basic_timeout(evutil_socket_t fd, short what, void *arg)
 
 }
 
+static void
+wake_one_timeout(evutil_socket_t fd, short what, void *arg)
+{
+       struct cond_wait *cw = arg;
+       EVLOCK_LOCK(cw->lock, 0);
+       EVTHREAD_COND_SIGNAL(cw->cond);
+       EVLOCK_UNLOCK(cw->lock, 0);
+}
+
 #define NUM_THREADS    100
+#define NUM_ITERATIONS  100
 void *count_lock;
 static int count;
 
@@ -95,13 +106,15 @@ basic_thread(void *arg)
        assert(cw.lock);
        assert(cw.cond);
 
-       evtimer_assign(&ev, base, basic_timeout, &cw);
-       for (i = 0; i < 100; i++) {
+       evtimer_assign(&ev, base, wake_all_timeout, &cw);
+       for (i = 0; i < NUM_ITERATIONS; i++) {
                struct timeval tv;
                evutil_timerclear(&tv);
+               tv.tv_sec = 0;
+               tv.tv_usec = 3000;
 
                EVLOCK_LOCK(cw.lock, 0);
-               /* we need to make sure that even does not happen before
+               /* we need to make sure that event does not happen before
                 * we get to wait on the conditional variable */
                assert(evtimer_add(&ev, &tv) == 0);
 
@@ -157,14 +170,151 @@ thread_basic(void *arg)
                THREAD_JOIN(threads[i]);
 
        event_del(&ev);
+
+       tt_int_op(count, ==, NUM_THREADS * NUM_ITERATIONS);
+
        EVTHREAD_FREE_LOCK(count_lock, 0);
 end:
        ;
 }
 
+#undef NUM_THREADS
+#define NUM_THREADS 10
+
+struct alerted_record {
+       struct cond_wait *cond;
+       struct timeval delay;
+       struct timeval alerted_at;
+       int timed_out;
+};
+
+static THREAD_FN
+wait_for_condition(void *arg)
+{
+       struct alerted_record *rec = arg;
+       int r;
+
+       EVLOCK_LOCK(rec->cond->lock, 0);
+       if (rec->delay.tv_sec || rec->delay.tv_usec) {
+               r = EVTHREAD_COND_WAIT_TIMED(rec->cond->cond, rec->cond->lock,
+                   &rec->delay);
+       } else {
+               r = EVTHREAD_COND_WAIT(rec->cond->cond, rec->cond->lock);
+       }
+       EVLOCK_UNLOCK(rec->cond->lock, 0);
+
+       evutil_gettimeofday(&rec->alerted_at, NULL);
+       if (r == 1)
+               rec->timed_out = 1;
+
+       THREAD_RETURN();
+}
+
+static void
+thread_conditions_simple(void *arg)
+{
+       struct timeval tv_signal, tv_timeout, tv_broadcast;
+       struct alerted_record alerted[NUM_THREADS];
+       THREAD_T threads[NUM_THREADS];
+       struct cond_wait cond;
+       int i;
+       struct timeval launched_at;
+       struct event wake_one;
+       struct event wake_all;
+       struct basic_test_data *data = arg;
+       struct event_base *base = data->base;
+       int n_timed_out=0, n_signal=0, n_broadcast=0;
+
+       tv_signal.tv_sec = tv_timeout.tv_sec = tv_broadcast.tv_sec = 0;
+       tv_signal.tv_usec = 30*1000;
+       tv_timeout.tv_usec = 150*1000;
+       tv_broadcast.tv_usec = 500*1000;
+
+       EVTHREAD_ALLOC_LOCK(cond.lock, EVTHREAD_LOCKTYPE_RECURSIVE);
+       EVTHREAD_ALLOC_COND(cond.cond);
+       tt_assert(cond.lock);
+       tt_assert(cond.cond);
+       for (i = 0; i < NUM_THREADS; ++i) {
+               memset(&alerted[i], 0, sizeof(struct alerted_record));
+               alerted[i].cond = &cond;
+       }
+
+       /* Threads 5 and 6 will be allowed to time out */
+       memcpy(&alerted[5].delay, &tv_timeout, sizeof(tv_timeout));
+       memcpy(&alerted[6].delay, &tv_timeout, sizeof(tv_timeout));
+
+       evtimer_assign(&wake_one, base, wake_one_timeout, &cond);
+       evtimer_assign(&wake_all, base, wake_all_timeout, &cond);
+
+       evutil_gettimeofday(&launched_at, NULL);
+
+       /* Launch the threads... */
+       for (i = 0; i < NUM_THREADS; ++i) {
+               THREAD_START(threads[i], wait_for_condition, &alerted[i]);
+       }
+
+       /* Start the timers... */
+       tt_int_op(event_add(&wake_one, &tv_signal), ==, 0);
+       tt_int_op(event_add(&wake_all, &tv_broadcast), ==, 0);
+
+       /* And run for a bit... */
+       event_base_dispatch(base);
+
+       /* And wait till the threads are done. */
+       for (i = 0; i < NUM_THREADS; ++i)
+               THREAD_JOIN(threads[i]);
+
+       /* Now, let's see what happened. At least one of 5 or 6 should
+        * have timed out. */
+       n_timed_out = alerted[5].timed_out + alerted[6].timed_out;
+       tt_int_op(n_timed_out, >=, 1);
+       tt_int_op(n_timed_out, <=, 2);
+
+       for (i = 0; i < NUM_THREADS; ++i) {
+               const struct timeval *target_delay;
+               struct timeval target_time, actual_delay;
+               if (alerted[i].timed_out) {
+                       TT_BLATHER(("%d looks like a timeout\n", i));
+                       target_delay = &tv_timeout;
+                       tt_assert(i == 5 || i == 6);
+               } else if (evutil_timerisset(&alerted[i].alerted_at)) {
+                       long diff1,diff2;
+                       evutil_timersub(&alerted[i].alerted_at,
+                           &launched_at, &actual_delay);
+                       diff1 = timeval_msec_diff(&actual_delay,
+                           &tv_signal);
+                       diff2 = timeval_msec_diff(&actual_delay,
+                           &tv_broadcast);
+                       if (abs(diff1) < abs(diff2)) {
+                               TT_BLATHER(("%d looks like a signal\n", i));
+                               target_delay = &tv_signal;
+                               ++n_signal;
+                       } else {
+                               TT_BLATHER(("%d looks like a broadcast\n", i));
+                               target_delay = &tv_broadcast;
+                               ++n_broadcast;
+                       }
+               } else {
+                       TT_FAIL(("Thread %d never got woken", i));
+                       continue;
+               }
+               evutil_timeradd(target_delay, &launched_at, &target_time);
+               test_timeval_diff_leq(&target_time, &alerted[i].alerted_at,
+                   0, 50);
+       }
+       tt_int_op(n_broadcast + n_signal + n_timed_out, ==, NUM_THREADS);
+       tt_int_op(n_signal, ==, 1);
+
+end:
+       ;
+}
+
+#define TEST(name)                                                     \
+           { #name, thread_##name, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE, \
+             &basic_setup, NULL }
 struct testcase_t thread_testcases[] = {
-       { "basic", thread_basic, TT_FORK|TT_NEED_THREADS|TT_NEED_BASE,
-         &basic_setup, NULL },
+       TEST(basic),
+       TEST(conditions_simple),
        END_OF_TESTCASES
 };