tests: Accommodate c-tap-harness 4.7
[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     /*
48      * The eventListMutex protects the contents of fields in the global
49      * 'events' array, including reading/writing evrecord->event.
50      * However, in this test code, we have an additional guarantee that
51      * the events array will remain allocated for the duration of the test,
52      * and as such that it is safe to dereference |evrecord| at all.  In real
53      * application code where the passed args are pointers to allocated data
54      * structures with finite lifetime, the programmer must ensure that the
55      * firing event can safely access these fields (i.e., that the object
56      * lifetime does not permit the object to be destroyed while an event
57      * pointing to it is outstanding or in progress).  The simplest way to
58      * do this (for reference counted objects) is to have the pending event
59      * hold a reference on the pointed-to object. This reference should be
60      * dropped at the end of the event handler or if the event is
61      * (successfully!) cancelled before it fires.  Other strategies are also
62      * possible, such as deferring object destruction until after all pending
63      * events have run or gotten cancelled, noting that the calling code must
64      * take care to allow the event handler to obtain any needed locks and
65      * avoid deadlock.
66      */
67     pthread_mutex_lock(&eventListMutex);
68     if (evrecord->event != NULL)
69         rxevent_Put(&evrecord->event);
70     evrecord->event = NULL;
71     evrecord->fired = 1;
72     pthread_mutex_unlock(&eventListMutex);
73     return;
74 }
75
76 static void
77 reportSub(struct rxevent *event, void *arg, void *arg1, int arg2)
78 {
79     printf("Event fired\n");
80 }
81
82 static void *
83 eventHandler(void *dummy) {
84     struct timespec nextEvent;
85     struct clock cv;
86     struct clock next;
87
88     pthread_mutex_lock(&eventMutex);
89     while (1) {
90         pthread_mutex_unlock(&eventMutex);
91
92         next.sec = 30;
93         next.usec = 0;
94         clock_GetTime(&cv);
95         rxevent_RaiseEvents(&next);
96
97         pthread_mutex_lock(&eventMutex);
98
99         /* If we were rescheduled whilst running the event queue,
100          * process the queue again */
101         if (rescheduled) {
102             rescheduled = 0;
103             continue;
104         }
105
106         clock_Add(&cv, &next);
107         nextEvent.tv_sec = cv.sec;
108         nextEvent.tv_nsec = cv.usec * 1000;
109         pthread_cond_timedwait(&eventCond, &eventMutex, &nextEvent);
110     }
111     pthread_mutex_unlock(&eventMutex);
112
113     return NULL;
114 }
115
116 int
117 main(void)
118 {
119     int when, counter, fail, fired, cancelled;
120     struct clock now, eventTime;
121     struct rxevent *event;
122     pthread_t handler;
123
124     plan(8);
125
126     pthread_mutex_init(&eventMutex, NULL);
127     pthread_cond_init(&eventCond, NULL);
128
129     memset(events, 0, sizeof(events));
130     pthread_mutex_init(&eventListMutex, NULL);
131
132     /* Start up the event system */
133     rxevent_Init(20, reschedule);
134     ok(1, "Started event subsystem");
135
136     clock_GetTime(&now);
137     /* Test for a problem when there is only a single event in the tree */
138     event = rxevent_Post(&now, &now, reportSub, NULL, NULL, 0);
139     ok(event != NULL, "Created a single event");
140     ok(rxevent_Cancel(&event), "Cancelled a single event");
141     rxevent_RaiseEvents(&now);
142     ok(1, "RaiseEvents happened without error");
143
144     ok(pthread_create(&handler, NULL, eventHandler, NULL) == 0,
145        "Created handler thread");
146
147     /* Add a number of random events to fire over the next 3 seconds, but front-loaded
148      * a bit so that we can exercise the cancel/fire race path. */
149
150     for (counter = 0; counter < NUMEVENTS; counter++) {
151         when = random() % 4000;
152         /* Put 1/4 of events "right away" so we cancel them as they fire */
153         if (when >= 3000)
154             when = random() % 5;
155         clock_GetTime(&now);
156         eventTime = now;
157         clock_Addmsec(&eventTime, when);
158         pthread_mutex_lock(&eventListMutex);
159         events[counter].event
160             = rxevent_Post(&eventTime, &now, eventSub, &events[counter], NULL, 0);
161
162         /* A 10% chance that we will schedule another event at the same time */
163         if (counter < (NUMEVENTS - 1) && random() % 10 == 0) {
164              counter++;
165              events[counter].event
166                  = rxevent_Post(&eventTime, &now, eventSub, &events[counter],
167                                 NULL, 0);
168         }
169
170         /* A 25% chance that we will cancel a random event */
171         if (random() % 4 == 0) {
172             int victim = random() % counter;
173
174             if (rxevent_Cancel(&events[victim].event))
175                 events[victim].cancelled = 1;
176         }
177         pthread_mutex_unlock(&eventListMutex);
178     }
179
180     ok(1, "Added %d events", NUMEVENTS);
181
182     sleep(4);
183
184     fired = 0;
185     cancelled = 0;
186     fail = 0;
187     for (counter = 0; counter < NUMEVENTS; counter++) {
188         if (events[counter].fired)
189             fired++;
190         if (events[counter].cancelled)
191             cancelled++;
192         if (events[counter].cancelled && events[counter].fired)
193             fail = 1;
194     }
195     ok(!fail, "Didn't fire any cancelled events");
196     diag("fired %d/%d events", fired, NUMEVENTS);
197     diag("cancelled %d/%d events", cancelled, NUMEVENTS);
198     is_int(NUMEVENTS, fired+cancelled,
199         "Number of fired and cancelled events sum to correct total");
200
201     return 0;
202 }