test_exponential_backoff.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  1. /*
  2. This file is part of GNUnet.
  3. (C) 2009 Christian Grothoff (and other contributing authors)
  4. GNUnet is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published
  6. by the Free Software Foundation; either version 3, or (at your
  7. option) any later version.
  8. GNUnet is distributed in the hope that it will be useful, but
  9. WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with GNUnet; see the file COPYING. If not, write to the
  14. Free Software Foundation, Inc., 59 Temple Place - Suite 330,
  15. Boston, MA 02111-1307, USA.
  16. */
  17. /**
  18. * @file arm/test_exponential_backoff.c
  19. * @brief testcase for gnunet-service-arm.c
  20. */
  21. #include "platform.h"
  22. #include "gnunet_arm_service.h"
  23. #include "gnunet_client_lib.h"
  24. #include "gnunet_configuration_lib.h"
  25. #include "gnunet_program_lib.h"
  26. #include "gnunet_protocols.h"
  27. #define VERBOSE GNUNET_NO
  28. #define START_ARM GNUNET_YES
  29. #define LOG_BACKOFF GNUNET_NO
  30. #define TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 10)
  31. #define SERVICE_TEST_TIMEOUT GNUNET_TIME_UNIT_FOREVER_REL
  32. #define FIVE_MILLISECONDS GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 5)
  33. static const struct GNUNET_CONFIGURATION_Handle *cfg;
  34. static struct GNUNET_ARM_Handle *arm;
  35. static int ok = 1;
  36. static int trialCount;
  37. static struct GNUNET_TIME_Absolute startedWaitingAt;
  38. struct GNUNET_TIME_Relative waitedFor;
  39. #if LOG_BACKOFF
  40. static FILE *killLogFilePtr;
  41. static char *killLogFileName;
  42. #endif
  43. /**
  44. * Context for handling the shutdown of a service.
  45. */
  46. struct ShutdownContext
  47. {
  48. /**
  49. * Connection to the service that is being shutdown.
  50. */
  51. struct GNUNET_CLIENT_Connection *sock;
  52. /**
  53. * Time allowed for shutdown to happen.
  54. */
  55. struct GNUNET_TIME_Absolute timeout;
  56. /**
  57. * Task set up to cancel the shutdown request on timeout.
  58. */
  59. GNUNET_SCHEDULER_TaskIdentifier cancel_task;
  60. /**
  61. * Task to call once shutdown complete
  62. */
  63. GNUNET_CLIENT_ShutdownTask cont;
  64. /**
  65. * Closure for shutdown continuation
  66. */
  67. void *cont_cls;
  68. /**
  69. * We received a confirmation that the service will shut down.
  70. */
  71. int confirmed;
  72. };
  73. /**
  74. * Handler receiving response to service shutdown requests.
  75. * First call with NULL: service misbehaving, or something.
  76. * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
  77. * - service will shutdown
  78. * Second call with NULL:
  79. * - service has now really shut down.
  80. *
  81. * @param cls closure
  82. * @param msg NULL, indicating socket closure.
  83. */
  84. static void
  85. service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
  86. {
  87. struct ShutdownContext *shutdown_ctx = cls;
  88. if ((msg == NULL) && (shutdown_ctx->confirmed != GNUNET_YES))
  89. {
  90. /* Means the other side closed the connection and never confirmed a shutdown */
  91. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  92. "Service handle shutdown before ACK!\n");
  93. if (shutdown_ctx->cont != NULL)
  94. shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
  95. GNUNET_SCHEDULER_cancel(shutdown_ctx->cancel_task);
  96. GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
  97. GNUNET_free(shutdown_ctx);
  98. }
  99. else if ((msg == NULL) && (shutdown_ctx->confirmed == GNUNET_YES))
  100. {
  101. #if VERBOSE
  102. GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
  103. "Service shutdown complete.\n");
  104. #endif
  105. if (shutdown_ctx->cont != NULL)
  106. shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_NO);
  107. GNUNET_SCHEDULER_cancel(shutdown_ctx->cancel_task);
  108. GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
  109. GNUNET_free(shutdown_ctx);
  110. }
  111. else
  112. {
  113. GNUNET_assert(ntohs(msg->size) == sizeof(struct GNUNET_MessageHeader));
  114. switch (ntohs(msg->type))
  115. {
  116. case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
  117. #if VERBOSE
  118. GNUNET_log(GNUNET_ERROR_TYPE_DEBUG,
  119. "Received confirmation for service shutdown.\n");
  120. #endif
  121. shutdown_ctx->confirmed = GNUNET_YES;
  122. GNUNET_CLIENT_receive (shutdown_ctx->sock,
  123. &service_shutdown_handler,
  124. shutdown_ctx,
  125. GNUNET_TIME_UNIT_FOREVER_REL);
  126. break;
  127. default: /* Fall through */
  128. GNUNET_log(GNUNET_ERROR_TYPE_WARNING,
  129. "Service shutdown refused!\n");
  130. if (shutdown_ctx->cont != NULL)
  131. shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_YES);
  132. GNUNET_SCHEDULER_cancel(shutdown_ctx->cancel_task);
  133. GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
  134. GNUNET_free(shutdown_ctx);
  135. break;
  136. }
  137. }
  138. }
  139. /**
  140. * Shutting down took too long, cancel receive and return error.
  141. *
  142. * @param cls closure
  143. * @param tc context information (why was this task triggered now)
  144. */
  145. void service_shutdown_cancel (void *cls,
  146. const struct GNUNET_SCHEDULER_TaskContext * tc)
  147. {
  148. struct ShutdownContext *shutdown_ctx = cls;
  149. GNUNET_log(GNUNET_ERROR_TYPE_WARNING, "service_shutdown_cancel called!\n");
  150. shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
  151. GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
  152. GNUNET_free(shutdown_ctx);
  153. }
  154. /**
  155. * If possible, write a shutdown message to the target
  156. * buffer and destroy the client connection.
  157. *
  158. * @param cls the "struct GNUNET_CLIENT_Connection" to destroy
  159. * @param size number of bytes available in buf
  160. * @param buf NULL on error, otherwise target buffer
  161. * @return number of bytes written to buf
  162. */
  163. static size_t
  164. write_shutdown (void *cls, size_t size, void *buf)
  165. {
  166. struct GNUNET_MessageHeader *msg;
  167. struct ShutdownContext *shutdown_ctx = cls;
  168. if (size < sizeof (struct GNUNET_MessageHeader))
  169. {
  170. GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
  171. _("Failed to transmit shutdown request to client.\n"));
  172. shutdown_ctx->cont(shutdown_ctx->cont_cls, GNUNET_SYSERR);
  173. GNUNET_CLIENT_disconnect (shutdown_ctx->sock, GNUNET_NO);
  174. GNUNET_free(shutdown_ctx);
  175. return 0; /* client disconnected */
  176. }
  177. GNUNET_CLIENT_receive (shutdown_ctx->sock,
  178. &service_shutdown_handler, shutdown_ctx,
  179. GNUNET_TIME_UNIT_FOREVER_REL);
  180. shutdown_ctx->cancel_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining(shutdown_ctx->timeout),
  181. &service_shutdown_cancel,
  182. shutdown_ctx);
  183. msg = (struct GNUNET_MessageHeader *) buf;
  184. msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN);
  185. msg->size = htons (sizeof (struct GNUNET_MessageHeader));
  186. return sizeof (struct GNUNET_MessageHeader);
  187. }
  188. /**
  189. * Request that the service should shutdown.
  190. * Afterwards, the connection will automatically be
  191. * disconnected. Hence the "sock" should not
  192. * be used by the caller after this call
  193. * (calling this function frees "sock" after a while).
  194. *
  195. * @param sock the socket connected to the service
  196. * @param timeout how long to wait before giving up on transmission
  197. * @param cont continuation to call once the service is really down
  198. * @param cont_cls closure for continuation
  199. *
  200. */
  201. static void
  202. arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
  203. struct GNUNET_TIME_Relative timeout,
  204. GNUNET_CLIENT_ShutdownTask cont,
  205. void *cont_cls)
  206. {
  207. struct ShutdownContext *shutdown_ctx;
  208. shutdown_ctx = GNUNET_malloc(sizeof(struct ShutdownContext));
  209. shutdown_ctx->cont = cont;
  210. shutdown_ctx->cont_cls = cont_cls;
  211. shutdown_ctx->sock = sock;
  212. shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute(timeout);
  213. GNUNET_CLIENT_notify_transmit_ready (sock,
  214. sizeof (struct
  215. GNUNET_MessageHeader),
  216. timeout,
  217. GNUNET_NO,
  218. &write_shutdown, shutdown_ctx);
  219. }
  220. static void
  221. arm_notify_stop (void *cls, int success)
  222. {
  223. GNUNET_assert (success == GNUNET_NO);
  224. #if START_ARM
  225. GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, NULL, NULL);
  226. #endif
  227. }
  228. static void
  229. kill_task (void *cbData,
  230. const struct GNUNET_SCHEDULER_TaskContext *tc);
  231. static void
  232. do_nothing_notify (void *cls, int success)
  233. {
  234. GNUNET_assert (success == GNUNET_YES);
  235. ok = 1;
  236. GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS,
  237. &kill_task, NULL);
  238. }
  239. static void
  240. arm_notify (void *cls, int success)
  241. {
  242. GNUNET_assert (success == GNUNET_YES);
  243. GNUNET_ARM_start_service (arm,
  244. "do-nothing", TIMEOUT,
  245. &do_nothing_notify, NULL);
  246. }
  247. static void
  248. kill_task (void *cbData,
  249. const struct GNUNET_SCHEDULER_TaskContext *tc);
  250. static void
  251. do_nothing_restarted_notify_task (void *cls,
  252. const struct GNUNET_SCHEDULER_TaskContext *tc)
  253. {
  254. static char a;
  255. trialCount++;
  256. #if LOG_BACKOFF
  257. if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0)
  258. {
  259. fprintf(killLogFilePtr,
  260. "%d.Reason is shutdown!\n",
  261. trialCount);
  262. }
  263. else if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
  264. {
  265. fprintf(killLogFilePtr,
  266. "%d.Reason is timeout!\n",
  267. trialCount);
  268. }
  269. else if ((tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE) != 0)
  270. {
  271. fprintf(killLogFilePtr,
  272. "%d.Service is running!\n",
  273. trialCount);
  274. }
  275. #endif
  276. GNUNET_SCHEDULER_add_now (&kill_task, &a);
  277. }
  278. static void
  279. do_test (void *cbData,
  280. const struct GNUNET_SCHEDULER_TaskContext *tc)
  281. {
  282. GNUNET_CLIENT_service_test("do-nothing",
  283. cfg, TIMEOUT,
  284. &do_nothing_restarted_notify_task, NULL);
  285. }
  286. static void
  287. shutdown_cont (void *cls, int reason)
  288. {
  289. trialCount++;
  290. startedWaitingAt = GNUNET_TIME_absolute_get();
  291. GNUNET_SCHEDULER_add_delayed (waitedFor,
  292. &do_test,
  293. NULL);
  294. }
  295. static void
  296. kill_task (void *cbData,
  297. const struct GNUNET_SCHEDULER_TaskContext *tc)
  298. {
  299. static struct GNUNET_CLIENT_Connection * doNothingConnection = NULL;
  300. if (NULL != cbData)
  301. {
  302. waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt);
  303. #if LOG_BACKOFF
  304. fprintf(killLogFilePtr,
  305. "Waited for: %llu ms\n",
  306. (unsigned long long) waitedFor.rel_value);
  307. #endif
  308. }
  309. else
  310. {
  311. waitedFor.rel_value = 0;
  312. }
  313. /* Connect to the doNothing task */
  314. doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg);
  315. GNUNET_assert (doNothingConnection != NULL);
  316. if (trialCount == 12) {
  317. GNUNET_CLIENT_disconnect (doNothingConnection, GNUNET_NO);
  318. GNUNET_ARM_stop_service (arm,
  319. "do-nothing",
  320. TIMEOUT,
  321. &arm_notify_stop, NULL);
  322. ok = 0;
  323. return;
  324. }
  325. /* Use the created connection to kill the doNothingTask */
  326. arm_service_shutdown(doNothingConnection,
  327. TIMEOUT,
  328. &shutdown_cont, NULL);
  329. }
  330. static void
  331. task (void *cls,
  332. char *const *args,
  333. const char *cfgfile,
  334. const struct GNUNET_CONFIGURATION_Handle *c)
  335. {
  336. cfg = c;
  337. arm = GNUNET_ARM_connect (cfg,NULL);
  338. #if START_ARM
  339. GNUNET_ARM_start_service (arm, "arm", GNUNET_TIME_UNIT_ZERO, &arm_notify, NULL);
  340. #else
  341. arm_do_nothing (NULL, GNUNET_YES);
  342. #endif
  343. }
  344. static int
  345. check ()
  346. {
  347. char *const argv[] = {
  348. "test-arm-api",
  349. "-c", "test_arm_api_data.conf",
  350. #if VERBOSE
  351. "-L", "DEBUG",
  352. #endif
  353. NULL
  354. };
  355. struct GNUNET_GETOPT_CommandLineOption options[] = {
  356. GNUNET_GETOPT_OPTION_END
  357. };
  358. /* Running ARM and running the do_nothing task */
  359. GNUNET_assert (GNUNET_OK ==
  360. GNUNET_PROGRAM_run ((sizeof (argv) / sizeof (char *)) - 1,
  361. argv,
  362. "test-exponential-backoff",
  363. "nohelp", options, &task, NULL));
  364. return ok;
  365. }
  366. static int
  367. init()
  368. {
  369. #if LOG_BACKOFF
  370. killLogFileName = GNUNET_DISK_mktemp("exponential-backoff-waiting.log");
  371. if (NULL == (killLogFilePtr = FOPEN(killLogFileName, "w"))) {
  372. GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, "fopen", killLogFileName);
  373. GNUNET_free (killLogFileName);
  374. return GNUNET_SYSERR;
  375. }
  376. #endif
  377. return GNUNET_OK;
  378. }
  379. static void
  380. houseKeep()
  381. {
  382. #if LOG_BACKOFF
  383. GNUNET_assert (0 == fclose (killLogFilePtr));
  384. GNUNET_free(killLogFileName);
  385. #endif
  386. }
  387. int
  388. main (int argc, char *argv[])
  389. {
  390. int ret;
  391. GNUNET_log_setup ("test-exponential-backoff",
  392. #if VERBOSE
  393. "DEBUG",
  394. #else
  395. "WARNING",
  396. #endif
  397. NULL);
  398. init();
  399. ret = check ();
  400. houseKeep();
  401. return ret;
  402. }
  403. /* end of test_exponential_backoff.c */