test_scheduler.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460
  1. /* Copyright (c) 2014, The Tor Project, Inc. */
  2. /* See LICENSE for licensing information */
  3. #include <math.h>
  4. #include "orconfig.h"
  5. /* Libevent stuff */
  6. #ifdef HAVE_EVENT2_EVENT_H
  7. #include <event2/event.h>
  8. #else
  9. #include <event.h>
  10. #endif
  11. #define TOR_CHANNEL_INTERNAL_
  12. #define CHANNEL_PRIVATE_
  13. #include "or.h"
  14. #include "compat_libevent.h"
  15. #include "channel.h"
  16. #define SCHEDULER_PRIVATE_
  17. #include "scheduler.h"
  18. /* Test suite stuff */
  19. #include "test.h"
  20. #include "fakechans.h"
  21. /* Statics in scheduler.c exposed to the test suite */
  22. extern smartlist_t *channels_pending;
  23. extern struct event *run_sched_ev;
  24. extern uint64_t queue_heuristic;
  25. extern time_t queue_heuristic_timestamp;
  26. /* Event base for scheduelr tests */
  27. static struct event_base *mock_event_base = NULL;
  28. /* Statics controlling mocks */
  29. static circuitmux_t *mock_ccm_tgt_1 = NULL;
  30. static circuitmux_t *mock_ccm_tgt_2 = NULL;
  31. static circuitmux_t *mock_cgp_tgt_1 = NULL;
  32. static const circuitmux_policy_t *mock_cgp_val_1 = NULL;
  33. static circuitmux_t *mock_cgp_tgt_2 = NULL;
  34. static const circuitmux_policy_t *mock_cgp_val_2 = NULL;
  35. static int scheduler_compare_channels_mock_ctr = 0;
  36. static int scheduler_run_mock_ctr = 0;
  37. /* Setup for mock event stuff */
  38. static void mock_event_free_all(void);
  39. static void mock_event_init(void);
  40. /* Mocks used by scheduler tests */
  41. static int circuitmux_compare_muxes_mock(circuitmux_t *cmux_1,
  42. circuitmux_t *cmux_2);
  43. static const circuitmux_policy_t * circuitmux_get_policy_mock(
  44. circuitmux_t *cmux);
  45. static int scheduler_compare_channels_mock(const void *c1_v,
  46. const void *c2_v);
  47. static void scheduler_run_noop_mock(void);
  48. static struct event_base * tor_libevent_get_base_mock(void);
  49. /* Scheduler test cases */
  50. static void test_scheduler_channel_states(void *arg);
  51. static void test_scheduler_compare_channels(void *arg);
  52. static void test_scheduler_initfree(void *arg);
  53. static void test_scheduler_queue_heuristic(void *arg);
  54. /* Mock event init/free */
  55. /* Shamelessly stolen from compat_libevent.c */
  56. #define V(major, minor, patch) \
  57. (((major) << 24) | ((minor) << 16) | ((patch) << 8))
  58. static void
  59. mock_event_free_all(void)
  60. {
  61. test_assert(mock_event_base != NULL);
  62. if (mock_event_base) {
  63. event_base_free(mock_event_base);
  64. mock_event_base = NULL;
  65. }
  66. test_eq(mock_event_base, NULL);
  67. done:
  68. return;
  69. }
  70. static void
  71. mock_event_init(void)
  72. {
  73. #ifdef HAVE_EVENT2_EVENT_H
  74. struct event_config *cfg = NULL;
  75. #endif
  76. test_eq(mock_event_base, NULL);
  77. /*
  78. * Really cut down from tor_libevent_initialize of
  79. * src/common/compat_libevent.c to kill config dependencies
  80. */
  81. if (!mock_event_base) {
  82. #ifdef HAVE_EVENT2_EVENT_H
  83. cfg = event_config_new();
  84. #if LIBEVENT_VERSION_NUMBER >= V(2,0,9)
  85. /* We can enable changelist support with epoll, since we don't give
  86. * Libevent any dup'd fds. This lets us avoid some syscalls. */
  87. event_config_set_flag(cfg, EVENT_BASE_FLAG_EPOLL_USE_CHANGELIST);
  88. #endif
  89. mock_event_base = event_base_new_with_config(cfg);
  90. event_config_free(cfg);
  91. #else
  92. mock_event_base = event_init();
  93. #endif
  94. }
  95. test_assert(mock_event_base != NULL);
  96. done:
  97. return;
  98. }
  99. /* Mocks */
  100. static int
  101. circuitmux_compare_muxes_mock(circuitmux_t *cmux_1,
  102. circuitmux_t *cmux_2)
  103. {
  104. int result = 0;
  105. test_assert(cmux_1 != NULL);
  106. test_assert(cmux_2 != NULL);
  107. if (cmux_1 != cmux_2) {
  108. if (cmux_1 == mock_ccm_tgt_1 && cmux_2 == mock_ccm_tgt_2) result = -1;
  109. else if (cmux_1 == mock_ccm_tgt_2 && cmux_2 == mock_ccm_tgt_1) {
  110. result = 1;
  111. } else {
  112. if (cmux_1 == mock_ccm_tgt_1 || cmux_1 == mock_ccm_tgt_1) result = -1;
  113. else if (cmux_2 == mock_ccm_tgt_1 || cmux_2 == mock_ccm_tgt_2) {
  114. result = 1;
  115. } else {
  116. result = circuitmux_compare_muxes__real(cmux_1, cmux_2);
  117. }
  118. }
  119. }
  120. /* else result = 0 always */
  121. done:
  122. return result;
  123. }
  124. static const circuitmux_policy_t *
  125. circuitmux_get_policy_mock(circuitmux_t *cmux)
  126. {
  127. const circuitmux_policy_t *result = NULL;
  128. test_assert(cmux != NULL);
  129. if (cmux) {
  130. if (cmux == mock_cgp_tgt_1) result = mock_cgp_val_1;
  131. else if (cmux == mock_cgp_tgt_2) result = mock_cgp_val_2;
  132. else result = circuitmux_get_policy__real(cmux);
  133. }
  134. done:
  135. return result;
  136. }
  137. static int
  138. scheduler_compare_channels_mock(const void *c1_v,
  139. const void *c2_v)
  140. {
  141. uintptr_t p1, p2;
  142. p1 = (uintptr_t)(c1_v);
  143. p2 = (uintptr_t)(c2_v);
  144. ++scheduler_compare_channels_mock_ctr;
  145. if (p1 == p2) return 0;
  146. else if (p1 < p2) return 1;
  147. else return -1;
  148. }
  149. static void
  150. scheduler_run_noop_mock(void)
  151. {
  152. ++scheduler_run_mock_ctr;
  153. }
  154. static struct event_base *
  155. tor_libevent_get_base_mock(void)
  156. {
  157. return mock_event_base;
  158. }
  159. /* Test cases */
  160. static void
  161. test_scheduler_channel_states(void *arg)
  162. {
  163. channel_t *ch1 = NULL, *ch2 = NULL;
  164. int old_count;
  165. (void)arg;
  166. /* Set up libevent and scheduler */
  167. mock_event_init();
  168. MOCK(tor_libevent_get_base, tor_libevent_get_base_mock);
  169. scheduler_init();
  170. /*
  171. * Install the compare channels mock so we can test
  172. * scheduler_touch_channel().
  173. */
  174. MOCK(scheduler_compare_channels, scheduler_compare_channels_mock);
  175. /*
  176. * Disable scheduler_run so we can just check the state transitions
  177. * without having to make everything it might call work too.
  178. */
  179. MOCK(scheduler_run, scheduler_run_noop_mock);
  180. test_eq(smartlist_len(channels_pending), 0);
  181. /* Set up a fake channel */
  182. ch1 = new_fake_channel();
  183. test_assert(ch1);
  184. /* Start it off in OPENING */
  185. ch1->state = CHANNEL_STATE_OPENING;
  186. /* We'll need a cmux */
  187. ch1->cmux = circuitmux_alloc();
  188. /* Try to register it */
  189. channel_register(ch1);
  190. test_assert(ch1->registered);
  191. /* It should start off in SCHED_CHAN_IDLE */
  192. test_eq(ch1->scheduler_state, SCHED_CHAN_IDLE);
  193. /* Now get another one */
  194. ch2 = new_fake_channel();
  195. test_assert(ch2);
  196. ch2->state = CHANNEL_STATE_OPENING;
  197. ch2->cmux = circuitmux_alloc();
  198. channel_register(ch2);
  199. test_assert(ch2->registered);
  200. /* Send it to SCHED_CHAN_WAITING_TO_WRITE */
  201. scheduler_channel_has_waiting_cells(ch1);
  202. test_eq(ch1->scheduler_state, SCHED_CHAN_WAITING_TO_WRITE);
  203. /* This should send it to SCHED_CHAN_PENDING */
  204. scheduler_channel_wants_writes(ch1);
  205. test_eq(ch1->scheduler_state, SCHED_CHAN_PENDING);
  206. test_eq(smartlist_len(channels_pending), 1);
  207. /* Now send ch2 to SCHED_CHAN_WAITING_FOR_CELLS */
  208. scheduler_channel_wants_writes(ch2);
  209. test_eq(ch2->scheduler_state, SCHED_CHAN_WAITING_FOR_CELLS);
  210. /* Drop ch2 back to idle */
  211. scheduler_channel_doesnt_want_writes(ch2);
  212. test_eq(ch2->scheduler_state, SCHED_CHAN_IDLE);
  213. /* ...and back to SCHED_CHAN_WAITING_FOR_CELLS */
  214. scheduler_channel_wants_writes(ch2);
  215. test_eq(ch2->scheduler_state, SCHED_CHAN_WAITING_FOR_CELLS);
  216. /* ...and this should kick ch2 into SCHED_CHAN_PENDING */
  217. scheduler_channel_has_waiting_cells(ch2);
  218. test_eq(ch2->scheduler_state, SCHED_CHAN_PENDING);
  219. test_eq(smartlist_len(channels_pending), 2);
  220. /* This should send ch2 to SCHED_CHAN_WAITING_TO_WRITE */
  221. scheduler_channel_doesnt_want_writes(ch2);
  222. test_eq(ch2->scheduler_state, SCHED_CHAN_WAITING_TO_WRITE);
  223. test_eq(smartlist_len(channels_pending), 1);
  224. /* ...and back to SCHED_CHAN_PENDING */
  225. scheduler_channel_wants_writes(ch2);
  226. test_eq(ch2->scheduler_state, SCHED_CHAN_PENDING);
  227. test_eq(smartlist_len(channels_pending), 2);
  228. /* Now we exercise scheduler_touch_channel */
  229. old_count = scheduler_compare_channels_mock_ctr;
  230. scheduler_touch_channel(ch1);
  231. test_assert(scheduler_compare_channels_mock_ctr > old_count);
  232. /* Close */
  233. channel_mark_for_close(ch1);
  234. test_eq(ch1->state, CHANNEL_STATE_CLOSING);
  235. channel_mark_for_close(ch2);
  236. test_eq(ch2->state, CHANNEL_STATE_CLOSING);
  237. channel_closed(ch1);
  238. test_eq(ch1->state, CHANNEL_STATE_CLOSED);
  239. ch1 = NULL;
  240. channel_closed(ch2);
  241. test_eq(ch2->state, CHANNEL_STATE_CLOSED);
  242. ch2 = NULL;
  243. /* Shut things down */
  244. channel_free_all();
  245. scheduler_free_all();
  246. mock_event_free_all();
  247. done:
  248. tor_free(ch1);
  249. tor_free(ch2);
  250. UNMOCK(scheduler_compare_channels);
  251. UNMOCK(scheduler_run);
  252. UNMOCK(tor_libevent_get_base);
  253. return;
  254. }
  255. static void
  256. test_scheduler_compare_channels(void *arg)
  257. {
  258. /* We don't actually need whole fake channels... */
  259. channel_t c1, c2;
  260. /* ...and some dummy circuitmuxes too */
  261. circuitmux_t *cm1 = NULL, *cm2 = NULL;
  262. int result;
  263. (void)arg;
  264. /* We can't actually see sizeof(circuitmux_t) from here */
  265. cm1 = tor_malloc_zero(sizeof(void *));
  266. cm2 = tor_malloc_zero(sizeof(void *));
  267. c1.cmux = cm1;
  268. c2.cmux = cm2;
  269. /* Configure circuitmux_get_policy() mock */
  270. mock_cgp_tgt_1 = cm1;
  271. /*
  272. * This is to test the different-policies case, which uses the policy
  273. * cast to an intptr_t as an arbitrary but definite thing to compare.
  274. */
  275. mock_cgp_val_1 = (const circuitmux_policy_t *)(1);
  276. mock_cgp_tgt_2 = cm2;
  277. mock_cgp_val_2 = (const circuitmux_policy_t *)(2);
  278. MOCK(circuitmux_get_policy, circuitmux_get_policy_mock);
  279. /* Now set up circuitmux_compare_muxes() mock using cm1/cm2 */
  280. mock_ccm_tgt_1 = cm1;
  281. mock_ccm_tgt_2 = cm2;
  282. MOCK(circuitmux_compare_muxes, circuitmux_compare_muxes_mock);
  283. /* Equal-channel case */
  284. result = scheduler_compare_channels(&c1, &c1);
  285. test_eq(result, 0);
  286. /* Distinct channels, distinct policies */
  287. result = scheduler_compare_channels(&c1, &c2);
  288. test_eq(result, -1);
  289. result = scheduler_compare_channels(&c2, &c1);
  290. test_eq(result, 1);
  291. /* Distinct channels, same policy */
  292. mock_cgp_val_2 = mock_cgp_val_1;
  293. result = scheduler_compare_channels(&c1, &c2);
  294. test_eq(result, -1);
  295. result = scheduler_compare_channels(&c2, &c1);
  296. test_eq(result, 1);
  297. done:
  298. UNMOCK(circuitmux_compare_muxes);
  299. mock_ccm_tgt_1 = NULL;
  300. mock_ccm_tgt_2 = NULL;
  301. UNMOCK(circuitmux_get_policy);
  302. mock_cgp_tgt_1 = NULL;
  303. mock_cgp_val_1 = NULL;
  304. mock_cgp_tgt_2 = NULL;
  305. mock_cgp_val_2 = NULL;
  306. tor_free(cm1);
  307. tor_free(cm2);
  308. return;
  309. }
  310. static void
  311. test_scheduler_initfree(void *arg)
  312. {
  313. (void)arg;
  314. test_eq(channels_pending, NULL);
  315. test_eq(run_sched_ev, NULL);
  316. mock_event_init();
  317. MOCK(tor_libevent_get_base, tor_libevent_get_base_mock);
  318. scheduler_init();
  319. test_assert(channels_pending != NULL);
  320. test_assert(run_sched_ev != NULL);
  321. scheduler_free_all();
  322. UNMOCK(tor_libevent_get_base);
  323. mock_event_free_all();
  324. test_eq(channels_pending, NULL);
  325. test_eq(run_sched_ev, NULL);
  326. done:
  327. return;
  328. }
  329. static void
  330. test_scheduler_queue_heuristic(void *arg)
  331. {
  332. time_t now = approx_time();
  333. uint64_t qh;
  334. (void)arg;
  335. queue_heuristic = 0;
  336. queue_heuristic_timestamp = 0;
  337. /* Not yet inited case */
  338. scheduler_update_queue_heuristic(now - 180);
  339. test_eq(queue_heuristic, 0);
  340. test_eq(queue_heuristic_timestamp, now - 180);
  341. queue_heuristic = 1000000000L;
  342. queue_heuristic_timestamp = now - 120;
  343. scheduler_update_queue_heuristic(now - 119);
  344. test_eq(queue_heuristic, 500000000L);
  345. test_eq(queue_heuristic_timestamp, now - 119);
  346. scheduler_update_queue_heuristic(now - 116);
  347. test_eq(queue_heuristic, 62500000L);
  348. test_eq(queue_heuristic_timestamp, now - 116);
  349. qh = scheduler_get_queue_heuristic();
  350. test_eq(qh, 0);
  351. done:
  352. return;
  353. }
  354. struct testcase_t scheduler_tests[] = {
  355. { "channel_states", test_scheduler_channel_states, TT_FORK, NULL, NULL },
  356. { "compare_channels", test_scheduler_compare_channels,
  357. TT_FORK, NULL, NULL },
  358. { "initfree", test_scheduler_initfree, TT_FORK, NULL, NULL },
  359. { "queue_heuristic", test_scheduler_queue_heuristic,
  360. TT_FORK, NULL, NULL },
  361. END_OF_TESTCASES
  362. };