1 /* A simple test of the rx event layer */
9 #include <tests/tap/basic.h>
11 #include "rx/rx_event.h"
12 #include "rx/rx_clock.h"
14 #define NUMEVENTS 10000
16 /* Mutexes and condvars for the scheduler */
17 static int rescheduled
= 0;
18 static pthread_mutex_t eventMutex
;
19 static pthread_cond_t eventCond
;
21 /* Mutexes and condvars for the event list */
23 static pthread_mutex_t eventListMutex
;
25 struct rxevent
*event
;
30 static struct testEvent events
[NUMEVENTS
];
35 pthread_mutex_lock(&eventMutex
);
36 pthread_cond_signal(&eventCond
);
38 pthread_mutex_unlock(&eventMutex
);
43 eventSub(struct rxevent
*event
, void *arg
, void *arg1
, int arg2
)
45 struct testEvent
*evrecord
= arg
;
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
67 pthread_mutex_lock(&eventListMutex
);
68 if (evrecord
->event
!= NULL
)
69 rxevent_Put(&evrecord
->event
);
70 evrecord
->event
= NULL
;
72 pthread_mutex_unlock(&eventListMutex
);
77 reportSub(struct rxevent
*event
, void *arg
, void *arg1
, int arg2
)
79 printf("Event fired\n");
83 eventHandler(void *dummy
) {
84 struct timespec nextEvent
;
88 pthread_mutex_lock(&eventMutex
);
90 pthread_mutex_unlock(&eventMutex
);
95 rxevent_RaiseEvents(&next
);
97 pthread_mutex_lock(&eventMutex
);
99 /* If we were rescheduled whilst running the event queue,
100 * process the queue again */
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
);
111 pthread_mutex_unlock(&eventMutex
);
119 int when
, counter
, fail
, fired
, cancelled
;
120 struct clock now
, eventTime
;
121 struct rxevent
*event
;
126 pthread_mutex_init(&eventMutex
, NULL
);
127 pthread_cond_init(&eventCond
, NULL
);
129 memset(events
, 0, sizeof(events
));
130 pthread_mutex_init(&eventListMutex
, NULL
);
132 /* Start up the event system */
133 rxevent_Init(20, reschedule
);
134 ok(1, "Started event subsystem");
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");
144 ok(pthread_create(&handler
, NULL
, eventHandler
, NULL
) == 0,
145 "Created handler thread");
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. */
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 */
157 clock_Addmsec(&eventTime
, when
);
158 pthread_mutex_lock(&eventListMutex
);
159 events
[counter
].event
160 = rxevent_Post(&eventTime
, &now
, eventSub
, &events
[counter
], NULL
, 0);
162 /* A 10% chance that we will schedule another event at the same time */
163 if (counter
< (NUMEVENTS
- 1) && random() % 10 == 0) {
165 events
[counter
].event
166 = rxevent_Post(&eventTime
, &now
, eventSub
, &events
[counter
],
170 /* A 25% chance that we will cancel a random event */
171 if (random() % 4 == 0) {
172 int victim
= random() % counter
;
174 if (rxevent_Cancel(&events
[victim
].event
))
175 events
[victim
].cancelled
= 1;
177 pthread_mutex_unlock(&eventListMutex
);
180 ok(1, "Added %d events", NUMEVENTS
);
187 for (counter
= 0; counter
< NUMEVENTS
; counter
++) {
188 if (events
[counter
].fired
)
190 if (events
[counter
].cancelled
)
192 if (events
[counter
].cancelled
&& events
[counter
].fired
)
195 ok(!fail
, "Didn't fire any cancelled events");
196 ok(fired
+cancelled
== NUMEVENTS
,
197 "Number of fired and cancelled events sum to correct total");