librfn
An ad-hoc utility library
fibretest.c
Go to the documentation of this file.
1 /*
2  * fibretest.c
3  *
4  * Part of librfn (a general utility library from redfelineninja.org.uk)
5  *
6  * Copyright (C) 2013 Daniel Thompson <daniel@redfelineninja.org.uk>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation; either version 3 of the License, or
11  * (at your option) any later version.
12  */
13 
14 #undef NDEBUG
15 
16 #include <assert.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 
20 #include <librfn.h>
21 
22 typedef struct {
23  uint32_t time;
24  uint32_t max_time;
25  uint32_t step;
28 
30 {
32 
33  PT_BEGIN_FIBRE(f);
34 
35  while (s->time < s->max_time) {
36  s->time += s->step;
38  }
39 
40  PT_END();
41 }
42 
43 typedef struct {
44  uint32_t count;
45  uint32_t max_count;
48 
50 {
52 
53  PT_BEGIN_FIBRE(f);
54 
55  while (s->count < s->max_count) {
56  s->count++;
57  PT_YIELD();
58  }
59 
60  PT_END();
61 }
62 
63 /* identical behaviour as a yield_fibre but differently realized */
65 {
67 
68  PT_BEGIN_FIBRE(f);
69 
70  if (s->count < s->max_count) {
71  s->count++;
72  fibre_run(f);
73  }
74 
75  PT_END();
76 }
77 
78 /* identical behaviour as a yield_fibre but differently realized */
80 {
82 
83  PT_BEGIN_FIBRE(f);
84 
85  if (s->count < s->max_count) {
86  s->count++;
88  }
89 
90  PT_END();
91 }
92 
93 
94 typedef struct {
95  int id;
97 
98 typedef struct {
101 } eventq_t;
102 
104 {
106  eventq_t *evtq = containerof(fevtq, eventq_t, evtq);
107 
108  PT_BEGIN_FIBRE(f);
109 
110  while (true) {
111  event_descriptor_t *evt;
112 
113  PT_WAIT_UNTIL(NULL != (evt = fibre_eventq_receive(fevtq)));
114  evtq->last_event = *evt;
115  fibre_eventq_release(fevtq, evt);
116  }
117 
118  PT_END();
119 }
120 
121 
122 /*
123  * This is a somewhat unfocused test of general scheduler behavior.
124  */
125 static void basic_test()
126 {
127  static sleep_fibre_t sleeper = {
128  .max_time = 50,
129  .step = 10,
130  .fibre = FIBRE_VAR_INIT(sleep_fibre)
131  };
132 
133  static yield_fibre_t yielder = {
134  .count = 0,
135  .max_count = 5,
136  .fibre = FIBRE_VAR_INIT(yield_fibre)
137  };
138 
139  static event_descriptor_t events[8];
140  static eventq_t handler = {
142  events, sizeof(events), sizeof(events[0]))
143  };
144 
145  fibre_run(&sleeper.fibre);
146 
147  /* only one fibre is running, it sleeps making system go idle */
148  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == &sleeper.fibre);
149  verify( 10 == fibre_scheduler_next( 5) && fibre_self() == NULL);
150  verify( 10 == sleeper.time && 0 == yielder.count);
151  verify( 20 == fibre_scheduler_next( 10) && fibre_self() == &sleeper.fibre);
152  verify( 20 == sleeper.time && 0 == yielder.count);
153 
154  /* two fibres are running, one sleeps, the other yields so system
155  * remains busy
156  */
157  fibre_run(&yielder.fibre);
158  verify( 20 == fibre_scheduler_next( 20) && fibre_self() == &yielder.fibre); // sleeper runnable
159  verify( 20 == sleeper.time && 1 == yielder.count);
160  verify( 21 == fibre_scheduler_next( 21) && fibre_self() == &sleeper.fibre);
161  verify( 30 == sleeper.time && 1 == yielder.count);
162  verify( 22 == fibre_scheduler_next( 22) && fibre_self() == &yielder.fibre);
163  verify( 30 == sleeper.time && 2 == yielder.count);
164  verify( 31 == fibre_scheduler_next( 31) && fibre_self() == &yielder.fibre); // sleeper runnable
165  verify( 30 == sleeper.time && 3 == yielder.count);
166  verify( 42 == fibre_scheduler_next( 42) && fibre_self() == &sleeper.fibre); // sleeper loops twice
167  verify( 50 == sleeper.time && 3 == yielder.count);
168  verify( 44 == fibre_scheduler_next( 44) && fibre_self() == &yielder.fibre);
169  verify( 50 == sleeper.time && 4 == yielder.count);
170  verify( 45 == fibre_scheduler_next( 45) && fibre_self() == &yielder.fibre);
171  verify( 50 == sleeper.time && 5 == yielder.count);
172  verify( 50 == fibre_scheduler_next( 46) && fibre_self() == &yielder.fibre); // fibre completes
173  // ^^^^ note difference in idle time
174  verify( 50 == sleeper.time && 5 == yielder.count);
175  verify( 50 == fibre_scheduler_next( 47) && fibre_self() == NULL); // idle
176  verify( 50 == sleeper.time && 5 == yielder.count);
178  == fibre_scheduler_next(50) && fibre_self() == &sleeper.fibre);
179  verify( 50 == sleeper.time && 5 == yielder.count);
180 
181  fibre_run(&handler.evtq.fibre);
183  fibre_scheduler_next(60)); // handler runs to wait
184  assert(0 == handler.last_event.id);
185  event_descriptor_t *pdesc;
186  pdesc = fibre_eventq_claim(&handler.evtq);
187  pdesc->id = 1;
188  fibre_eventq_send(&handler.evtq, pdesc);
190  fibre_scheduler_next(70)); // handler runs to wait
191  assert(1 == handler.last_event.id);
192 }
193 
194 /*
195  * A test that runs fibres from varying positions within the timerq.
196  */
197 static void sleep_test()
198 {
199  static sleep_fibre_t sleeper[3] = {
200  {
201  .max_time = 50,
202  .step = 10,
203  .fibre = FIBRE_VAR_INIT(sleep_fibre)
204  },
205  {
206  .max_time = 50,
207  .step = 10,
208  .fibre = FIBRE_VAR_INIT(sleep_fibre)
209  },
210  {
211  .time = 5,
212  .max_time = 50,
213  .step = 10,
214  .fibre = FIBRE_VAR_INIT(sleep_fibre)
215  }
216  };
217 
218  /* All three fibres are launched and executed until the system becomes idle */
219  fibre_run(&sleeper[0].fibre);
220  fibre_run(&sleeper[1].fibre);
221  fibre_run(&sleeper[2].fibre);
222  verify( 0 == fibre_scheduler_next( 0) && fibre_self() == &sleeper[0].fibre);
223  verify( 0 == fibre_scheduler_next( 0) && fibre_self() == &sleeper[1].fibre);
224  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == &sleeper[2].fibre);
225  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == NULL);
226 
227  /* Run sleeper[0] and schedule until idle */
228  fibre_run(&sleeper[0].fibre);
229  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == &sleeper[0].fibre);
230  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == NULL);
231 
232  /* Run sleeper[1] and schedule until idle */
233  fibre_run(&sleeper[1].fibre);
234  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == &sleeper[1].fibre);
235  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == NULL);
236 
237  /* Run sleeper[2] and schedule until idle */
238  fibre_run(&sleeper[2].fibre);
239  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == &sleeper[2].fibre);
240  verify( 10 == fibre_scheduler_next( 0) && fibre_self() == NULL);
241 
242  /* Allow time to pass causing #0 and #1 to be runnable. Ensure #0 runs first since
243  * it was starting waiting first.
244  */
245  verify( 10 == fibre_scheduler_next( 10) && fibre_self() == &sleeper[0].fibre);
246  verify( 15 == fibre_scheduler_next( 10) && fibre_self() == &sleeper[1].fibre);
247  verify( 15 == fibre_scheduler_next( 10) && fibre_self() == NULL);
248 
249  /* Allow time to pass causing #2 to be runnable */
250  verify( 20 == fibre_scheduler_next( 15) && fibre_self() == &sleeper[2].fibre);
251  verify( 20 == fibre_scheduler_next( 15) && fibre_self() == NULL);
252 
253  /* Make the fibres runnable in reverse order and allow time to pass, verify
254  * that the fibre_run() imposes the execution order.
255  */
256  fibre_run(&sleeper[2].fibre);
257  fibre_run(&sleeper[1].fibre);
258  fibre_run(&sleeper[0].fibre);
259  verify( 39 == fibre_scheduler_next( 39) && fibre_self() == &sleeper[2].fibre);
260  verify( 39 == fibre_scheduler_next( 39) && fibre_self() == &sleeper[1].fibre);
261  verify( 40 == fibre_scheduler_next( 39) && fibre_self() == &sleeper[0].fibre);
262  verify( 40 == fibre_scheduler_next( 39) && fibre_self() == NULL);
263 
264  /* Allow sufficient time to pass for the fibres to complete. Here #1 will run
265  * before #0 since it joined the timer queue first.
266  */
267  verify( 60 == fibre_scheduler_next( 60) && fibre_self() == &sleeper[1].fibre);
268  verify( 60 == fibre_scheduler_next( 60) && fibre_self() == &sleeper[0].fibre);
270  == fibre_scheduler_next( 60) && fibre_self() == &sleeper[2].fibre);
272  == fibre_scheduler_next( 60) && fibre_self() == NULL);
273 }
274 
275 static void yield_test()
276 {
277  static yield_fibre_t yielder[3] = {
278  {
279  .max_count = 10,
280  .fibre = FIBRE_VAR_INIT(atomic_fibre) // <--
281  },
282  {
283  .max_count = 15,
284  .fibre = FIBRE_VAR_INIT(yield_fibre) // <--
285  },
286  {
287  .max_count = 10,
288  .fibre = FIBRE_VAR_INIT(exit_fibre) // <--
289  }
290  };
291 
292  /* Launch the fibres */
293  fibre_run(&yielder[0].fibre);
294  fibre_run(&yielder[1].fibre);
295  fibre_run(&yielder[2].fibre);
296 
297  for (int i=0; i<10; i++) {
298  /* no change to run count */
299  verify(yielder[0].count == i);
300  verify(yielder[1].count == i);
301  verify(yielder[2].count == i);
302 
303  /* schedule yielder 0 */
304  verify(i == fibre_scheduler_next(i) &&
305  fibre_self() == &yielder[0].fibre);
306  verify(yielder[0].count == i+1);
307  verify(yielder[1].count == i);
308  verify(yielder[2].count == i);
309 
310  /* schedule yielder 1 */
311  verify(i == fibre_scheduler_next(i) &&
312  fibre_self() == &yielder[1].fibre);
313  verify(yielder[0].count == i+1);
314  verify(yielder[1].count == i+1);
315  verify(yielder[2].count == i);
316 
317  /* schedule yielder 2 */
318  verify(i == fibre_scheduler_next(i) &&
319  fibre_self() == &yielder[2].fibre);
320  verify(yielder[0].count == i+1);
321  verify(yielder[1].count == i+1);
322  verify(yielder[2].count == i+1);
323  }
324 
325  verify(10 == fibre_scheduler_next(10) &&
326  fibre_self() == &yielder[0].fibre); // exits
327  verify(10 == fibre_scheduler_next(10) &&
328  fibre_self() == &yielder[1].fibre);
329  verify(10 == fibre_scheduler_next(10) &&
330  fibre_self() == &yielder[2].fibre); // exits
331 
332  for (int i=11; i<15; i++) {
333  /* no change to run count */
334  verify(yielder[0].count == 10);
335  verify(yielder[1].count == i);
336  verify(yielder[2].count == 10);
337 
338  verify(i == fibre_scheduler_next(i) &&
339  fibre_self() == &yielder[1].fibre);
340  verify(yielder[1].count == i+1);
341  }
342 
344  fibre_self() == &yielder[1].fibre); // exits
346  fibre_self() == NULL);
347 }
348 
349 int main()
350 {
351  basic_test();
352  sleep_test();
353  yield_test();
354 
355  return 0;
356 }
#define containerof(ptr, type, member)
Definition: util.h:35
event_descriptor_t last_event
Definition: fibretest.c:99
fibre_t fibre
Definition: fibretest.c:26
#define FIBRE_VAR_INIT(fn)
Static initializer for a fibre descriptor.
Definition: fibre.h:75
fibre_t fibre
Definition: fibre.h:87
void * fibre_eventq_claim(fibre_eventq_t *evtq)
Request memory resources to send an event to a fibre.
Definition: fibre.c:226
int sleep_fibre(fibre_t *f)
Definition: fibretest.c:29
uint32_t fibre_scheduler_next(uint32_t time)
Schedule the next fibre.
Definition: fibre.c:134
#define PT_WAIT_UNTIL(c)
Definition: protothreads.h:116
int exit_fibre(fibre_t *f)
Definition: fibretest.c:64
int yield_fibre(fibre_t *f)
Definition: fibretest.c:49
fibre_eventq_t evtq
Definition: fibretest.c:100
#define PT_YIELD()
Definition: protothreads.h:124
uint32_t time
Definition: fibretest.c:23
bool fibre_eventq_send(fibre_eventq_t *evtq, void *evtp)
Send an event to a fibre.
Definition: fibre.c:234
#define PT_END()
Definition: protothreads.h:100
void fibre_eventq_release(fibre_eventq_t *evtq, void *evtp)
Release a message previously received by a fibre.
Definition: fibre.c:250
void * fibre_eventq_receive(fibre_eventq_t *evtq)
Recevied a message previously send to the fibre.
Definition: fibre.c:245
uint32_t step
Definition: fibretest.c:25
uint32_t count
Definition: fibretest.c:44
fibre_t fibre
Definition: fibretest.c:46
#define verify(x)
Definition: util.h:55
bool fibre_run_atomic(fibre_t *f)
Definition: fibre.c:183
#define FIBRE_EVENTQ_VAR_INIT(fn, basep, base_len, msg_len)
Static initializer for a fibre and eventq descriptor.
Definition: fibre.h:94
int event_handler(fibre_t *f)
Definition: fibretest.c:103
#define FIBRE_UNBOUNDED_SLEEP
An approximation of infinitely far in the future.
Definition: fibre.h:56
uint32_t max_time
Definition: fibretest.c:24
Fibre and eventq descriptor.
Definition: fibre.h:86
fibre_t * fibre_self(void)
Returns the currently active fibre descriptor.
Definition: fibre.c:129
#define PT_BEGIN_FIBRE(f)
Fibre aware alternative to PT_BEGIN().
Definition: fibre.h:103
uint32_t max_count
Definition: fibretest.c:45
Fibre descriptor.
Definition: fibre.h:64
void fibre_run(fibre_t *f)
Definition: fibre.c:173
bool fibre_timeout(uint32_t duetime)
Definition: fibre.c:208
int atomic_fibre(fibre_t *f)
Definition: fibretest.c:79
int main()
Definition: fibretest.c:349