Adjust rx-event test to exercise cancel/fire race
[openafs.git] / tests / rx / event-t.c
1 /* A simple test of the rx event layer */
2
3 #include <afsconfig.h>
4 #include <afs/param.h>
5
6 #include <roken.h>
7 #include <pthread.h>
8
9 #include <tests/tap/basic.h>
10
11 #include "rx/rx_event.h"
12 #include "rx/rx_clock.h"
13
14 #define NUMEVENTS 10000
15
16 /* Mutexes and condvars for the scheduler */
17 static int rescheduled = 0;
18 static pthread_mutex_t eventMutex;
19 static pthread_cond_t eventCond;
20
21 /* Mutexes and condvars for the event list */
22
23 static pthread_mutex_t eventListMutex;
24 struct testEvent {
25     struct rxevent *event;
26     int fired;
27     int cancelled;
28 };
29
30 static struct testEvent events[NUMEVENTS];
31
32 static void
33 reschedule(void)
34 {
35     pthread_mutex_lock(&eventMutex);
36     pthread_cond_signal(&eventCond);
37     rescheduled = 1;
38     pthread_mutex_unlock(&eventMutex);
39     return;
40 }
41
42 static void
43 eventSub(struct rxevent *event, void *arg, void *arg1, int arg2)
44 {
45     struct testEvent *evrecord = arg;
46
47     pthread_mutex_lock(&eventListMutex);
48     rxevent_Put(&evrecord->event);
49     evrecord->event = NULL;
50     evrecord->fired = 1;
51     pthread_mutex_unlock(&eventListMutex);
52     return;
53 }
54
55 static void
56 reportSub(struct rxevent *event, void *arg, void *arg1, int arg2)
57 {
58     printf("Event fired\n");
59 }
60
61 static void *
62 eventHandler(void *dummy) {
63     struct timespec nextEvent;
64     struct clock cv;
65     struct clock next;
66
67     pthread_mutex_lock(&eventMutex);
68     while (1) {
69         pthread_mutex_unlock(&eventMutex);
70
71         next.sec = 30;
72         next.usec = 0;
73         clock_GetTime(&cv);
74         rxevent_RaiseEvents(&next);
75
76         pthread_mutex_lock(&eventMutex);
77
78         /* If we were rescheduled whilst running the event queue,
79          * process the queue again */
80         if (rescheduled) {
81             rescheduled = 0;
82             continue;
83         }
84
85         clock_Add(&cv, &next);
86         nextEvent.tv_sec = cv.sec;
87         nextEvent.tv_nsec = cv.usec * 1000;
88         pthread_cond_timedwait(&eventCond, &eventMutex, &nextEvent);
89     }
90     pthread_mutex_unlock(&eventMutex);
91
92     return NULL;
93 }
94
95 int
96 main(void)
97 {
98     int when, counter, fail, fired, cancelled;
99     struct clock now, eventTime;
100     struct rxevent *event;
101     pthread_t handler;
102
103     plan(8);
104
105     pthread_mutex_init(&eventMutex, NULL);
106     pthread_cond_init(&eventCond, NULL);
107
108     memset(events, 0, sizeof(events));
109     pthread_mutex_init(&eventListMutex, NULL);
110
111     /* Start up the event system */
112     rxevent_Init(20, reschedule);
113     ok(1, "Started event subsystem");
114
115     clock_GetTime(&now);
116     /* Test for a problem when there is only a single event in the tree */
117     event = rxevent_Post(&now, &now, reportSub, NULL, NULL, 0);
118     ok(event != NULL, "Created a single event");
119     rxevent_Cancel(&event);
120     ok(1, "Cancelled a single event");
121     rxevent_RaiseEvents(&now);
122     ok(1, "RaiseEvents happened without error");
123
124     ok(pthread_create(&handler, NULL, eventHandler, NULL) == 0,
125        "Created handler thread");
126
127     /* Add 1000 random events to fire over the next 3 seconds, but front-loaded
128      * a bit so that we can exercise the cancel/fire race path. */
129
130     for (counter = 0; counter < NUMEVENTS; counter++) {
131         when = random() % 4000;
132         /* Put 1/4 of events "right away" so we cancel them as they fire */
133         if (when >= 3000)
134             when = random() % 5;
135         clock_GetTime(&now);
136         eventTime = now;
137         clock_Addmsec(&eventTime, when);
138         pthread_mutex_lock(&eventListMutex);
139         events[counter].event
140             = rxevent_Post(&eventTime, &now, eventSub, &events[counter], NULL, 0);
141
142         /* A 10% chance that we will schedule another event at the same time */
143         if (counter!=999 && random() % 10 == 0) {
144              counter++;
145              events[counter].event
146                  = rxevent_Post(&eventTime, &now, eventSub, &events[counter],
147                                 NULL, 0);
148         }
149
150         /* A 25% chance that we will cancel a random event */
151         if (random() % 4 == 0) {
152             int victim = random() % counter;
153
154             if (events[victim].event != NULL) {
155                 rxevent_Cancel(&events[victim].event);
156                 events[victim].cancelled = 1;
157             }
158         }
159         pthread_mutex_unlock(&eventListMutex);
160     }
161
162     ok(1, "Added %d events", NUMEVENTS);
163
164     sleep(3);
165
166     fired = 0;
167     cancelled = 0;
168     fail = 0;
169     for (counter = 0; counter < NUMEVENTS; counter++) {
170         if (events[counter].fired)
171             fired++;
172         if (events[counter].cancelled)
173             cancelled++;
174         if (events[counter].cancelled && events[counter].fired)
175             fail = 1;
176     }
177     ok(!fail, "Didn't fire any cancelled events");
178     ok(fired+cancelled == NUMEVENTS,
179         "Number of fired and cancelled events sum to correct total");
180
181     return 0;
182 }