/contrib/bind9/lib/isc/ratelimiter.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 328 lines · 251 code · 41 blank · 36 comment · 57 complexity · 9f243d1900ebf7f3348d634c72f3aea0 MD5 · raw file

  1. /*
  2. * Copyright (C) 2004, 2005, 2007 Internet Systems Consortium, Inc. ("ISC")
  3. * Copyright (C) 1999-2002 Internet Software Consortium.
  4. *
  5. * Permission to use, copy, modify, and/or distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
  10. * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  11. * AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  12. * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  13. * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  14. * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  15. * PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /* $Id: ratelimiter.c,v 1.25 2007/06/19 23:47:17 tbox Exp $ */
  18. /*! \file */
  19. #include <config.h>
  20. #include <isc/mem.h>
  21. #include <isc/ratelimiter.h>
  22. #include <isc/task.h>
  23. #include <isc/time.h>
  24. #include <isc/timer.h>
  25. #include <isc/util.h>
  26. typedef enum {
  27. isc_ratelimiter_stalled = 0,
  28. isc_ratelimiter_ratelimited = 1,
  29. isc_ratelimiter_idle = 2,
  30. isc_ratelimiter_shuttingdown = 3
  31. } isc_ratelimiter_state_t;
  32. struct isc_ratelimiter {
  33. isc_mem_t * mctx;
  34. isc_mutex_t lock;
  35. int refs;
  36. isc_task_t * task;
  37. isc_timer_t * timer;
  38. isc_interval_t interval;
  39. isc_uint32_t pertic;
  40. isc_ratelimiter_state_t state;
  41. isc_event_t shutdownevent;
  42. ISC_LIST(isc_event_t) pending;
  43. };
  44. #define ISC_RATELIMITEREVENT_SHUTDOWN (ISC_EVENTCLASS_RATELIMITER + 1)
  45. static void
  46. ratelimiter_tick(isc_task_t *task, isc_event_t *event);
  47. static void
  48. ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event);
  49. isc_result_t
  50. isc_ratelimiter_create(isc_mem_t *mctx, isc_timermgr_t *timermgr,
  51. isc_task_t *task, isc_ratelimiter_t **ratelimiterp)
  52. {
  53. isc_result_t result;
  54. isc_ratelimiter_t *rl;
  55. INSIST(ratelimiterp != NULL && *ratelimiterp == NULL);
  56. rl = isc_mem_get(mctx, sizeof(*rl));
  57. if (rl == NULL)
  58. return ISC_R_NOMEMORY;
  59. rl->mctx = mctx;
  60. rl->refs = 1;
  61. rl->task = task;
  62. isc_interval_set(&rl->interval, 0, 0);
  63. rl->timer = NULL;
  64. rl->pertic = 1;
  65. rl->state = isc_ratelimiter_idle;
  66. ISC_LIST_INIT(rl->pending);
  67. result = isc_mutex_init(&rl->lock);
  68. if (result != ISC_R_SUCCESS)
  69. goto free_mem;
  70. result = isc_timer_create(timermgr, isc_timertype_inactive,
  71. NULL, NULL, rl->task, ratelimiter_tick,
  72. rl, &rl->timer);
  73. if (result != ISC_R_SUCCESS)
  74. goto free_mutex;
  75. /*
  76. * Increment the reference count to indicate that we may
  77. * (soon) have events outstanding.
  78. */
  79. rl->refs++;
  80. ISC_EVENT_INIT(&rl->shutdownevent,
  81. sizeof(isc_event_t),
  82. 0, NULL, ISC_RATELIMITEREVENT_SHUTDOWN,
  83. ratelimiter_shutdowncomplete, rl, rl, NULL, NULL);
  84. *ratelimiterp = rl;
  85. return (ISC_R_SUCCESS);
  86. free_mutex:
  87. DESTROYLOCK(&rl->lock);
  88. free_mem:
  89. isc_mem_put(mctx, rl, sizeof(*rl));
  90. return (result);
  91. }
  92. isc_result_t
  93. isc_ratelimiter_setinterval(isc_ratelimiter_t *rl, isc_interval_t *interval) {
  94. isc_result_t result = ISC_R_SUCCESS;
  95. LOCK(&rl->lock);
  96. rl->interval = *interval;
  97. /*
  98. * If the timer is currently running, change its rate.
  99. */
  100. if (rl->state == isc_ratelimiter_ratelimited) {
  101. result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
  102. &rl->interval, ISC_FALSE);
  103. }
  104. UNLOCK(&rl->lock);
  105. return (result);
  106. }
  107. void
  108. isc_ratelimiter_setpertic(isc_ratelimiter_t *rl, isc_uint32_t pertic) {
  109. if (pertic == 0)
  110. pertic = 1;
  111. rl->pertic = pertic;
  112. }
  113. isc_result_t
  114. isc_ratelimiter_enqueue(isc_ratelimiter_t *rl, isc_task_t *task,
  115. isc_event_t **eventp)
  116. {
  117. isc_result_t result = ISC_R_SUCCESS;
  118. isc_event_t *ev;
  119. REQUIRE(eventp != NULL && *eventp != NULL);
  120. REQUIRE(task != NULL);
  121. ev = *eventp;
  122. REQUIRE(ev->ev_sender == NULL);
  123. LOCK(&rl->lock);
  124. if (rl->state == isc_ratelimiter_ratelimited ||
  125. rl->state == isc_ratelimiter_stalled) {
  126. isc_event_t *ev = *eventp;
  127. ev->ev_sender = task;
  128. ISC_LIST_APPEND(rl->pending, ev, ev_link);
  129. *eventp = NULL;
  130. } else if (rl->state == isc_ratelimiter_idle) {
  131. result = isc_timer_reset(rl->timer, isc_timertype_ticker, NULL,
  132. &rl->interval, ISC_FALSE);
  133. if (result == ISC_R_SUCCESS) {
  134. ev->ev_sender = task;
  135. rl->state = isc_ratelimiter_ratelimited;
  136. }
  137. } else {
  138. INSIST(rl->state == isc_ratelimiter_shuttingdown);
  139. result = ISC_R_SHUTTINGDOWN;
  140. }
  141. UNLOCK(&rl->lock);
  142. if (*eventp != NULL && result == ISC_R_SUCCESS)
  143. isc_task_send(task, eventp);
  144. return (result);
  145. }
  146. static void
  147. ratelimiter_tick(isc_task_t *task, isc_event_t *event) {
  148. isc_result_t result = ISC_R_SUCCESS;
  149. isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
  150. isc_event_t *p;
  151. isc_uint32_t pertic;
  152. UNUSED(task);
  153. isc_event_free(&event);
  154. pertic = rl->pertic;
  155. while (pertic != 0) {
  156. pertic--;
  157. LOCK(&rl->lock);
  158. p = ISC_LIST_HEAD(rl->pending);
  159. if (p != NULL) {
  160. /*
  161. * There is work to do. Let's do it after unlocking.
  162. */
  163. ISC_LIST_UNLINK(rl->pending, p, ev_link);
  164. } else {
  165. /*
  166. * No work left to do. Stop the timer so that we don't
  167. * waste resources by having it fire periodically.
  168. */
  169. result = isc_timer_reset(rl->timer,
  170. isc_timertype_inactive,
  171. NULL, NULL, ISC_FALSE);
  172. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  173. rl->state = isc_ratelimiter_idle;
  174. pertic = 0; /* Force the loop to exit. */
  175. }
  176. UNLOCK(&rl->lock);
  177. if (p != NULL) {
  178. isc_task_t *evtask = p->ev_sender;
  179. isc_task_send(evtask, &p);
  180. }
  181. INSIST(p == NULL);
  182. }
  183. }
  184. void
  185. isc_ratelimiter_shutdown(isc_ratelimiter_t *rl) {
  186. isc_event_t *ev;
  187. isc_task_t *task;
  188. LOCK(&rl->lock);
  189. rl->state = isc_ratelimiter_shuttingdown;
  190. (void)isc_timer_reset(rl->timer, isc_timertype_inactive,
  191. NULL, NULL, ISC_FALSE);
  192. while ((ev = ISC_LIST_HEAD(rl->pending)) != NULL) {
  193. ISC_LIST_UNLINK(rl->pending, ev, ev_link);
  194. ev->ev_attributes |= ISC_EVENTATTR_CANCELED;
  195. task = ev->ev_sender;
  196. isc_task_send(task, &ev);
  197. }
  198. isc_timer_detach(&rl->timer);
  199. /*
  200. * Send an event to our task. The delivery of this event
  201. * indicates that no more timer events will be delivered.
  202. */
  203. ev = &rl->shutdownevent;
  204. isc_task_send(rl->task, &ev);
  205. UNLOCK(&rl->lock);
  206. }
  207. static void
  208. ratelimiter_shutdowncomplete(isc_task_t *task, isc_event_t *event) {
  209. isc_ratelimiter_t *rl = (isc_ratelimiter_t *)event->ev_arg;
  210. UNUSED(task);
  211. isc_ratelimiter_detach(&rl);
  212. }
  213. static void
  214. ratelimiter_free(isc_ratelimiter_t *rl) {
  215. DESTROYLOCK(&rl->lock);
  216. isc_mem_put(rl->mctx, rl, sizeof(*rl));
  217. }
  218. void
  219. isc_ratelimiter_attach(isc_ratelimiter_t *source, isc_ratelimiter_t **target) {
  220. REQUIRE(source != NULL);
  221. REQUIRE(target != NULL && *target == NULL);
  222. LOCK(&source->lock);
  223. REQUIRE(source->refs > 0);
  224. source->refs++;
  225. INSIST(source->refs > 0);
  226. UNLOCK(&source->lock);
  227. *target = source;
  228. }
  229. void
  230. isc_ratelimiter_detach(isc_ratelimiter_t **rlp) {
  231. isc_ratelimiter_t *rl = *rlp;
  232. isc_boolean_t free_now = ISC_FALSE;
  233. LOCK(&rl->lock);
  234. REQUIRE(rl->refs > 0);
  235. rl->refs--;
  236. if (rl->refs == 0)
  237. free_now = ISC_TRUE;
  238. UNLOCK(&rl->lock);
  239. if (free_now)
  240. ratelimiter_free(rl);
  241. *rlp = NULL;
  242. }
  243. isc_result_t
  244. isc_ratelimiter_stall(isc_ratelimiter_t *rl) {
  245. isc_result_t result = ISC_R_SUCCESS;
  246. LOCK(&rl->lock);
  247. switch (rl->state) {
  248. case isc_ratelimiter_shuttingdown:
  249. result = ISC_R_SHUTTINGDOWN;
  250. break;
  251. case isc_ratelimiter_ratelimited:
  252. result = isc_timer_reset(rl->timer, isc_timertype_inactive,
  253. NULL, NULL, ISC_FALSE);
  254. RUNTIME_CHECK(result == ISC_R_SUCCESS);
  255. case isc_ratelimiter_idle:
  256. case isc_ratelimiter_stalled:
  257. rl->state = isc_ratelimiter_stalled;
  258. break;
  259. }
  260. UNLOCK(&rl->lock);
  261. return (result);
  262. }
  263. isc_result_t
  264. isc_ratelimiter_release(isc_ratelimiter_t *rl) {
  265. isc_result_t result = ISC_R_SUCCESS;
  266. LOCK(&rl->lock);
  267. switch (rl->state) {
  268. case isc_ratelimiter_shuttingdown:
  269. result = ISC_R_SHUTTINGDOWN;
  270. break;
  271. case isc_ratelimiter_stalled:
  272. if (!ISC_LIST_EMPTY(rl->pending)) {
  273. result = isc_timer_reset(rl->timer,
  274. isc_timertype_ticker, NULL,
  275. &rl->interval, ISC_FALSE);
  276. if (result == ISC_R_SUCCESS)
  277. rl->state = isc_ratelimiter_ratelimited;
  278. } else
  279. rl->state = isc_ratelimiter_idle;
  280. break;
  281. case isc_ratelimiter_ratelimited:
  282. case isc_ratelimiter_idle:
  283. break;
  284. }
  285. UNLOCK(&rl->lock);
  286. return (result);
  287. }