test-timeout.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <assert.h>
  5. #include <limits.h>
  6. #include "timeout.h"
  7. #define THE_END_OF_TIME ((timeout_t)-1)
  8. static int check_misc(void) {
  9. if (TIMEOUT_VERSION != timeout_version())
  10. return 1;
  11. if (TIMEOUT_V_REL != timeout_v_rel())
  12. return 1;
  13. if (TIMEOUT_V_API != timeout_v_api())
  14. return 1;
  15. if (TIMEOUT_V_ABI != timeout_v_abi())
  16. return 1;
  17. if (strcmp(timeout_vendor(), TIMEOUT_VENDOR))
  18. return 1;
  19. return 0;
  20. }
  21. static int check_open_close(timeout_t hz_set, timeout_t hz_expect) {
  22. int err=0;
  23. struct timeouts *tos = timeouts_open(hz_set, &err);
  24. if (!tos)
  25. return 1;
  26. if (err)
  27. return 1;
  28. if (hz_expect != timeouts_hz(tos))
  29. return 1;
  30. timeouts_close(tos);
  31. return 0;
  32. }
  33. /* Not very random */
  34. static timeout_t random_to(timeout_t min, timeout_t max)
  35. {
  36. if (max <= min)
  37. return min;
  38. /* Not actually all that random, but should exercise the code. */
  39. timeout_t rand64 = random() * (timeout_t)INT_MAX + random();
  40. return min + (rand64 % (max-min));
  41. }
  42. /* configuration for check_randomized */
  43. struct rand_cfg {
  44. /* When creating timeouts, smallest possible delay */
  45. timeout_t min_timeout;
  46. /* When creating timeouts, largest possible delay */
  47. timeout_t max_timeout;
  48. /* First time to start the clock at. */
  49. timeout_t start_at;
  50. /* Do not advance the clock past this time. */
  51. timeout_t end_at;
  52. /* Number of timeouts to create and monitor. */
  53. int n_timeouts;
  54. /* Advance the clock by no more than this each step. */
  55. timeout_t max_step;
  56. /* Use relative timers and stepping */
  57. int relative;
  58. /* Every time the clock ticks, try removing this many timeouts at
  59. * random. */
  60. int try_removing;
  61. /* When we're done, advance the clock to the end of time. */
  62. int finalize;
  63. };
  64. static int check_randomized(const struct rand_cfg *cfg)
  65. {
  66. #define FAIL() do { \
  67. printf("Failure on line %d\n", __LINE__); \
  68. goto done; \
  69. } while (0)
  70. int i, err;
  71. int rv = 1;
  72. struct timeout *t = calloc(cfg->n_timeouts, sizeof(struct timeout));
  73. timeout_t *timeouts = calloc(cfg->n_timeouts, sizeof(timeout_t));
  74. uint8_t *fired = calloc(cfg->n_timeouts, sizeof(uint8_t));
  75. uint8_t *found = calloc(cfg->n_timeouts, sizeof(uint8_t));
  76. uint8_t *deleted = calloc(cfg->n_timeouts, sizeof(uint8_t));
  77. struct timeouts *tos = timeouts_open(0, &err);
  78. timeout_t now = cfg->start_at;
  79. int n_added_pending = 0, cnt_added_pending = 0;
  80. int n_added_expired = 0, cnt_added_expired = 0;
  81. struct timeouts_it it_p, it_e, it_all;
  82. int p_done = 0, e_done = 0, all_done = 0;
  83. struct timeout *to = NULL;
  84. const int rel = cfg->relative;
  85. if (!t || !timeouts || !tos || !fired || !found || !deleted)
  86. FAIL();
  87. timeouts_update(tos, cfg->start_at);
  88. for (i = 0; i < cfg->n_timeouts; ++i) {
  89. if (&t[i] != timeout_init(&t[i], rel ? 0 : TIMEOUT_ABS))
  90. FAIL();
  91. if (timeout_pending(&t[i]))
  92. FAIL();
  93. if (timeout_expired(&t[i]))
  94. FAIL();
  95. timeouts[i] = random_to(cfg->min_timeout, cfg->max_timeout);
  96. timeouts_add(tos, &t[i], timeouts[i] - (rel ? now : 0));
  97. if (timeouts[i] <= cfg->start_at) {
  98. if (timeout_pending(&t[i]))
  99. FAIL();
  100. if (! timeout_expired(&t[i]))
  101. FAIL();
  102. ++n_added_expired;
  103. } else {
  104. if (! timeout_pending(&t[i]))
  105. FAIL();
  106. if (timeout_expired(&t[i]))
  107. FAIL();
  108. ++n_added_pending;
  109. }
  110. }
  111. if (!!n_added_pending != timeouts_pending(tos))
  112. FAIL();
  113. if (!!n_added_expired != timeouts_expired(tos))
  114. FAIL();
  115. /* Test foreach, interleaving a few iterators. */
  116. TIMEOUTS_IT_INIT(&it_p, TIMEOUTS_PENDING);
  117. TIMEOUTS_IT_INIT(&it_e, TIMEOUTS_EXPIRED);
  118. TIMEOUTS_IT_INIT(&it_all, TIMEOUTS_ALL);
  119. while (! (p_done && e_done && all_done)) {
  120. if (!p_done) {
  121. to = timeouts_next(tos, &it_p);
  122. if (to) {
  123. i = to - &t[0];
  124. ++found[i];
  125. ++cnt_added_pending;
  126. } else {
  127. p_done = 1;
  128. }
  129. }
  130. if (!e_done) {
  131. to = timeouts_next(tos, &it_e);
  132. if (to) {
  133. i = to - &t[0];
  134. ++found[i];
  135. ++cnt_added_expired;
  136. } else {
  137. e_done = 1;
  138. }
  139. }
  140. if (!all_done) {
  141. to = timeouts_next(tos, &it_all);
  142. if (to) {
  143. i = to - &t[0];
  144. ++found[i];
  145. } else {
  146. all_done = 1;
  147. }
  148. }
  149. }
  150. for (i = 0; i < cfg->n_timeouts; ++i) {
  151. if (found[i] != 2)
  152. FAIL();
  153. }
  154. if (cnt_added_expired != n_added_expired)
  155. FAIL();
  156. if (cnt_added_pending != n_added_pending)
  157. FAIL();
  158. while (NULL != (to = timeouts_get(tos))) {
  159. i = to - &t[0];
  160. assert(&t[i] == to);
  161. if (timeouts[i] > cfg->start_at)
  162. FAIL(); /* shouldn't have happened yet */
  163. --n_added_expired; /* drop expired timeouts. */
  164. ++fired[i];
  165. }
  166. if (n_added_expired != 0)
  167. FAIL();
  168. while (now < cfg->end_at) {
  169. int n_fired_this_time = 0;
  170. timeout_t first_at = timeouts_timeout(tos) + now;
  171. timeout_t oldtime = now;
  172. timeout_t step = random_to(1, cfg->max_step);
  173. int another;
  174. now += step;
  175. if (rel)
  176. timeouts_step(tos, step);
  177. else
  178. timeouts_update(tos, now);
  179. for (i = 0; i < cfg->try_removing; ++i) {
  180. int idx = random() % cfg->n_timeouts;
  181. if (! fired[idx]) {
  182. timeout_del(&t[idx]);
  183. ++deleted[idx];
  184. }
  185. }
  186. another = (timeouts_timeout(tos) == 0);
  187. while (NULL != (to = timeouts_get(tos))) {
  188. if (! another)
  189. FAIL(); /* Thought we saw the last one! */
  190. i = to - &t[0];
  191. assert(&t[i] == to);
  192. if (timeouts[i] > now)
  193. FAIL(); /* shouldn't have happened yet */
  194. if (timeouts[i] <= oldtime)
  195. FAIL(); /* should have happened already */
  196. if (timeouts[i] < first_at)
  197. FAIL(); /* first_at should've been earlier */
  198. fired[i]++;
  199. n_fired_this_time++;
  200. another = (timeouts_timeout(tos) == 0);
  201. }
  202. if (n_fired_this_time && first_at > now)
  203. FAIL(); /* first_at should've been earlier */
  204. if (another)
  205. FAIL(); /* Huh? We think there are more? */
  206. if (!timeouts_check(tos, stderr))
  207. FAIL();
  208. }
  209. for (i = 0; i < cfg->n_timeouts; ++i) {
  210. if (fired[i] > 1)
  211. FAIL(); /* Nothing fired twice. */
  212. if (timeouts[i] <= now) {
  213. if (!(fired[i] || deleted[i]))
  214. FAIL();
  215. } else {
  216. if (fired[i])
  217. FAIL();
  218. }
  219. if (fired[i] && deleted[i])
  220. FAIL();
  221. if (cfg->finalize > 1) {
  222. if (!fired[i])
  223. timeout_del(&t[i]);
  224. }
  225. }
  226. /* Now nothing more should fire between now and the end of time. */
  227. if (cfg->finalize) {
  228. timeouts_update(tos, THE_END_OF_TIME);
  229. if (cfg->finalize > 1) {
  230. if (timeouts_get(tos))
  231. FAIL();
  232. TIMEOUTS_FOREACH(to, tos, TIMEOUTS_ALL)
  233. FAIL();
  234. }
  235. }
  236. rv = 0;
  237. done:
  238. if (tos) timeouts_close(tos);
  239. if (t) free(t);
  240. if (timeouts) free(timeouts);
  241. if (fired) free(fired);
  242. if (found) free(found);
  243. if (deleted) free(deleted);
  244. return rv;
  245. }
  246. struct intervals_cfg {
  247. const timeout_t *timeouts;
  248. int n_timeouts;
  249. timeout_t start_at;
  250. timeout_t end_at;
  251. timeout_t skip;
  252. };
  253. int
  254. check_intervals(struct intervals_cfg *cfg)
  255. {
  256. int i, err;
  257. int rv = 1;
  258. struct timeout *to;
  259. struct timeout *t = calloc(cfg->n_timeouts, sizeof(struct timeout));
  260. unsigned *fired = calloc(cfg->n_timeouts, sizeof(unsigned));
  261. struct timeouts *tos = timeouts_open(0, &err);
  262. timeout_t now = cfg->start_at;
  263. if (!t || !tos || !fired)
  264. FAIL();
  265. timeouts_update(tos, now);
  266. for (i = 0; i < cfg->n_timeouts; ++i) {
  267. if (&t[i] != timeout_init(&t[i], TIMEOUT_INT))
  268. FAIL();
  269. if (timeout_pending(&t[i]))
  270. FAIL();
  271. if (timeout_expired(&t[i]))
  272. FAIL();
  273. timeouts_add(tos, &t[i], cfg->timeouts[i]);
  274. if (! timeout_pending(&t[i]))
  275. FAIL();
  276. if (timeout_expired(&t[i]))
  277. FAIL();
  278. }
  279. while (now < cfg->end_at) {
  280. timeout_t delay = timeouts_timeout(tos);
  281. if (cfg->skip && delay < cfg->skip)
  282. delay = cfg->skip;
  283. timeouts_step(tos, delay);
  284. now += delay;
  285. while (NULL != (to = timeouts_get(tos))) {
  286. i = to - &t[0];
  287. assert(&t[i] == to);
  288. fired[i]++;
  289. if (0 != (to->expires - cfg->start_at) % cfg->timeouts[i])
  290. FAIL();
  291. if (to->expires <= now)
  292. FAIL();
  293. if (to->expires > now + cfg->timeouts[i])
  294. FAIL();
  295. }
  296. if (!timeouts_check(tos, stderr))
  297. FAIL();
  298. }
  299. timeout_t duration = now - cfg->start_at;
  300. for (i = 0; i < cfg->n_timeouts; ++i) {
  301. if (cfg->skip) {
  302. if (fired[i] > duration / cfg->timeouts[i])
  303. FAIL();
  304. } else {
  305. if (fired[i] != duration / cfg->timeouts[i])
  306. FAIL();
  307. }
  308. if (!timeout_pending(&t[i]))
  309. FAIL();
  310. }
  311. rv = 0;
  312. done:
  313. if (t) free(t);
  314. if (fired) free(fired);
  315. if (tos) free(tos);
  316. return rv;
  317. }
  318. int
  319. main(int argc, char **argv)
  320. {
  321. int j;
  322. int n_failed = 0;
  323. #define DO(fn) do { \
  324. printf("."); fflush(stdout); \
  325. if (fn) { \
  326. ++n_failed; \
  327. printf("%s failed\n", #fn); \
  328. } \
  329. } while (0)
  330. #define DO_N(n, fn) do { \
  331. for (j = 0; j < (n); ++j) { \
  332. DO(fn); \
  333. } \
  334. } while (0)
  335. DO(check_misc());
  336. DO(check_open_close(1000, 1000));
  337. DO(check_open_close(0, TIMEOUT_mHZ));
  338. struct rand_cfg cfg1 = {
  339. .min_timeout = 1,
  340. .max_timeout = 100,
  341. .start_at = 5,
  342. .end_at = 1000,
  343. .n_timeouts = 1000,
  344. .max_step = 10,
  345. .relative = 0,
  346. .try_removing = 0,
  347. .finalize = 2,
  348. };
  349. DO_N(300,check_randomized(&cfg1));
  350. struct rand_cfg cfg2 = {
  351. .min_timeout = 20,
  352. .max_timeout = 1000,
  353. .start_at = 10,
  354. .end_at = 100,
  355. .n_timeouts = 1000,
  356. .max_step = 5,
  357. .relative = 1,
  358. .try_removing = 0,
  359. .finalize = 2,
  360. };
  361. DO_N(300,check_randomized(&cfg2));
  362. struct rand_cfg cfg2b = {
  363. .min_timeout = 20,
  364. .max_timeout = 1000,
  365. .start_at = 10,
  366. .end_at = 100,
  367. .n_timeouts = 1000,
  368. .max_step = 5,
  369. .relative = 1,
  370. .try_removing = 0,
  371. .finalize = 1,
  372. };
  373. DO_N(300,check_randomized(&cfg2b));
  374. struct rand_cfg cfg2c = {
  375. .min_timeout = 20,
  376. .max_timeout = 1000,
  377. .start_at = 10,
  378. .end_at = 100,
  379. .n_timeouts = 1000,
  380. .max_step = 5,
  381. .relative = 1,
  382. .try_removing = 0,
  383. .finalize = 0,
  384. };
  385. DO_N(300,check_randomized(&cfg2c));
  386. struct rand_cfg cfg3 = {
  387. .min_timeout = 2000,
  388. .max_timeout = ((uint64_t)1) << 50,
  389. .start_at = 100,
  390. .end_at = ((uint64_t)1) << 49,
  391. .n_timeouts = 1000,
  392. .max_step = 1<<31,
  393. .relative = 0,
  394. .try_removing = 0,
  395. .finalize = 2,
  396. };
  397. DO_N(10,check_randomized(&cfg3));
  398. struct rand_cfg cfg3b = {
  399. .min_timeout = ((uint64_t)1) << 50,
  400. .max_timeout = ((uint64_t)1) << 52,
  401. .start_at = 100,
  402. .end_at = ((uint64_t)1) << 53,
  403. .n_timeouts = 1000,
  404. .max_step = ((uint64_t)1)<<48,
  405. .relative = 0,
  406. .try_removing = 0,
  407. .finalize = 2,
  408. };
  409. DO_N(10,check_randomized(&cfg3b));
  410. struct rand_cfg cfg4 = {
  411. .min_timeout = 2000,
  412. .max_timeout = ((uint64_t)1) << 30,
  413. .start_at = 100,
  414. .end_at = ((uint64_t)1) << 26,
  415. .n_timeouts = 10000,
  416. .max_step = 1<<16,
  417. .relative = 0,
  418. .try_removing = 3,
  419. .finalize = 2,
  420. };
  421. DO_N(10,check_randomized(&cfg4));
  422. const timeout_t primes[] = {
  423. 2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,
  424. 59,61,67,71,73,79,83,89,97
  425. };
  426. const timeout_t factors_of_1337[] = {
  427. 1, 7, 191, 1337
  428. };
  429. const timeout_t multiples_of_five[] = {
  430. 5, 10, 15, 20, 25, 30, 35, 40, 45, 50
  431. };
  432. struct intervals_cfg icfg1 = {
  433. .timeouts = primes,
  434. .n_timeouts = sizeof(primes)/sizeof(timeout_t),
  435. .start_at = 50,
  436. .end_at = 5322,
  437. .skip = 0,
  438. };
  439. DO(check_intervals(&icfg1));
  440. struct intervals_cfg icfg2 = {
  441. .timeouts = factors_of_1337,
  442. .n_timeouts = sizeof(factors_of_1337)/sizeof(timeout_t),
  443. .start_at = 50,
  444. .end_at = 50000,
  445. .skip = 0,
  446. };
  447. DO(check_intervals(&icfg2));
  448. struct intervals_cfg icfg3 = {
  449. .timeouts = multiples_of_five,
  450. .n_timeouts = sizeof(multiples_of_five)/sizeof(timeout_t),
  451. .start_at = 49,
  452. .end_at = 5333,
  453. .skip = 0,
  454. };
  455. DO(check_intervals(&icfg3));
  456. struct intervals_cfg icfg4 = {
  457. .timeouts = primes,
  458. .n_timeouts = sizeof(primes)/sizeof(timeout_t),
  459. .start_at = 50,
  460. .end_at = 5322,
  461. .skip = 16,
  462. };
  463. DO(check_intervals(&icfg4));
  464. if (n_failed) {
  465. puts("\nFAIL");
  466. } else {
  467. puts("\nOK");
  468. }
  469. return !!n_failed;
  470. }
  471. /* TODO:
  472. * Solve PR#3.
  473. * Investigate whether any untaken branches are possible.
  474. */