Broadcast forwarder for ip-bound gameservers
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

event.c 8.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /***************************************************************************
  2. * Copyright (C) 03/2010 by Olaf Rempel *
  3. * razzor@kopf-tisch.de *
  4. * *
  5. * This program is free software; you can redistribute it and/or modify *
  6. * it under the terms of the GNU General Public License as published by *
  7. * the Free Software Foundation; version 2 of the License *
  8. * *
  9. * This program is distributed in the hope that it will be useful, *
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  12. * GNU General Public License for more details. *
  13. * *
  14. * You should have received a copy of the GNU General Public License *
  15. * along with this program; if not, write to the *
  16. * Free Software Foundation, Inc., *
  17. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  18. ***************************************************************************/
  19. #include <stdlib.h>
  20. #include <unistd.h>
  21. #include <string.h>
  22. #include <errno.h>
  23. #include <sys/time.h>
  24. #include <sys/types.h>
  25. #include "list.h"
  26. #include "logging.h"
  27. #include "event.h"
  28. static LIST_HEAD(event_fd_list);
  29. static LIST_HEAD(event_timeout_list);
  30. struct event_fd {
  31. struct list_head list;
  32. unsigned int flags;
  33. int fd;
  34. int (*read_cb)(int fd, void *privdata);
  35. int (*write_cb)(int fd, void *privdata);
  36. void *read_priv;
  37. void *write_priv;
  38. };
  39. struct event_timeout {
  40. struct list_head list;
  41. unsigned int flags;
  42. struct timeval intervall;
  43. struct timeval nextrun;
  44. int (*callback)(void *privdata);
  45. void *privdata;
  46. };
  47. struct event_fd * event_add_fd(
  48. struct event_fd *entry,
  49. int fd,
  50. int type,
  51. int (*callback)(int fd, void *privdata),
  52. void *privdata)
  53. {
  54. /* check valid filediskriptor */
  55. if (fd < 0 || fd > FD_SETSIZE) {
  56. log_print(LOG_ERROR, "event_add_fd(): invalid fd");
  57. return NULL;
  58. }
  59. /* check valid type (read/write) */
  60. if (!(type & FD_TYPES)) {
  61. log_print(LOG_ERROR, "event_add_fd(): invalid type");
  62. return NULL;
  63. }
  64. /* create new entry */
  65. if (entry == NULL) {
  66. entry = malloc(sizeof(struct event_fd));
  67. if (entry == NULL) {
  68. log_print(LOG_ERROR, "event_add_fd(): out of memory");
  69. return NULL;
  70. }
  71. memset(entry, 0, sizeof(struct event_fd));
  72. entry->flags |= EVENT_NEW;
  73. entry->fd = fd;
  74. /* put it on the list */
  75. list_add_tail(&entry->list, &event_fd_list);
  76. }
  77. if (type & FD_READ) {
  78. entry->flags = (callback != NULL) ? (entry->flags | FD_READ | EVENT_NEW) : (entry->flags & ~FD_READ);
  79. entry->read_cb = callback;
  80. entry->read_priv = privdata;
  81. } else if (type & FD_WRITE) {
  82. entry->flags = (callback != NULL) ? (entry->flags | FD_WRITE | EVENT_NEW) : (entry->flags & ~FD_WRITE);
  83. entry->write_cb = callback;
  84. entry->write_priv = privdata;
  85. }
  86. return entry;
  87. }
  88. int event_get_fd(struct event_fd *entry)
  89. {
  90. return (entry != NULL) ? entry->fd: -1;
  91. }
  92. void event_remove_fd(struct event_fd *entry)
  93. {
  94. /* mark the event as deleted -> remove in select() loop */
  95. entry->flags |= EVENT_DELETE;
  96. }
  97. static void add_timeval(struct timeval *ret, struct timeval *a, struct timeval *b)
  98. {
  99. ret->tv_usec = a->tv_usec + b->tv_usec;
  100. ret->tv_sec = a->tv_sec + b->tv_sec;
  101. if (ret->tv_usec >= 1000000) {
  102. ret->tv_usec -= 1000000;
  103. ret->tv_sec++;
  104. }
  105. }
  106. static void sub_timeval(struct timeval *ret, struct timeval *a, struct timeval *b)
  107. {
  108. ret->tv_usec = a->tv_usec - b->tv_usec;
  109. ret->tv_sec = a->tv_sec - b->tv_sec;
  110. if (ret->tv_usec < 0) {
  111. ret->tv_usec += 1000000;
  112. ret->tv_sec--;
  113. }
  114. }
  115. static int cmp_timeval(struct timeval *a, struct timeval *b)
  116. {
  117. if (a->tv_sec > b->tv_sec)
  118. return -1;
  119. if (a->tv_sec < b->tv_sec)
  120. return 1;
  121. if (a->tv_usec > b->tv_usec)
  122. return -1;
  123. if (a->tv_usec < b->tv_usec)
  124. return 1;
  125. return 0;
  126. }
  127. static void schedule_nextrun(struct event_timeout *entry, struct timeval *now)
  128. {
  129. add_timeval(&entry->nextrun, now, &entry->intervall);
  130. struct event_timeout *search;
  131. list_for_each_entry(search, &event_timeout_list, list) {
  132. if (search->nextrun.tv_sec > entry->nextrun.tv_sec) {
  133. list_add_tail(&entry->list, &search->list);
  134. return;
  135. } else if (search->nextrun.tv_sec == entry->nextrun.tv_sec &&
  136. search->nextrun.tv_usec > entry->nextrun.tv_usec) {
  137. list_add_tail(&entry->list, &search->list);
  138. return;
  139. }
  140. }
  141. list_add_tail(&entry->list, &event_timeout_list);
  142. }
  143. struct event_timeout * event_add_timeout(
  144. struct timeval *timeout,
  145. int (*callback)(void *privdata),
  146. void *privdata)
  147. {
  148. struct event_timeout *entry;
  149. entry = malloc(sizeof(struct event_timeout));
  150. if (entry == NULL) {
  151. log_print(LOG_ERROR, "event_add_timeout(): out of memory");
  152. return NULL;
  153. }
  154. entry->flags = 0;
  155. memcpy(&entry->intervall, timeout, sizeof(entry->intervall));
  156. entry->callback = callback;
  157. entry->privdata = privdata;
  158. struct timeval now;
  159. gettimeofday(&now, NULL);
  160. schedule_nextrun(entry, &now);
  161. return entry;
  162. }
  163. void event_remove_timeout(struct event_timeout *entry)
  164. {
  165. /* mark the event as deleted -> remove in select() loop */
  166. entry->flags |= EVENT_DELETE;
  167. }
  168. int event_loop(int (*pre_select_cb)(int *maxfd, void *readfds, void *writefds, struct timeval *timeout, void *privdata),
  169. int (*post_select_cb)(int retval, void *readfds, void *writefds, void *privdata),
  170. void *privdata)
  171. {
  172. while (1) {
  173. /* default value if no application timeout is present */
  174. struct timeval timeout = {
  175. .tv_sec = 3600,
  176. .tv_usec = 0,
  177. };
  178. if (!list_empty(&event_timeout_list)) {
  179. struct timeval now;
  180. gettimeofday(&now, NULL);
  181. struct event_timeout *entry, *tmp;
  182. list_for_each_entry_safe(entry, tmp, &event_timeout_list, list) {
  183. if (entry->flags & EVENT_DELETE) {
  184. list_del(&entry->list);
  185. free(entry);
  186. continue;
  187. }
  188. /* first timeout not elapsed, exit search (since list is sorted) */
  189. if (cmp_timeval(&entry->nextrun, &now) == -1)
  190. break;
  191. /* remove event from list */
  192. list_del(&entry->list);
  193. /* execute callback, when callback returns 0 -> schedule event again */
  194. if (entry->callback(entry->privdata)) {
  195. free(entry);
  196. } else {
  197. schedule_nextrun(entry, &now);
  198. }
  199. }
  200. if (!list_empty(&event_timeout_list)) {
  201. entry = list_entry(event_timeout_list.next, typeof(*entry), list);
  202. /* calc select() timeout */
  203. sub_timeval(&timeout, &entry->nextrun, &now);
  204. }
  205. }
  206. struct event_fd *entry, *tmp;
  207. int maxfd = -1;
  208. fd_set readfds, writefds;
  209. FD_ZERO(&readfds);
  210. FD_ZERO(&writefds);
  211. list_for_each_entry_safe(entry, tmp, &event_fd_list, list) {
  212. entry->flags &= ~EVENT_NEW;
  213. if (entry->flags & EVENT_DELETE) {
  214. list_del(&entry->list);
  215. free(entry);
  216. continue;
  217. }
  218. if (entry->flags & FD_READ)
  219. FD_SET(entry->fd, &readfds);
  220. if (entry->flags & FD_WRITE)
  221. FD_SET(entry->fd, &writefds);
  222. maxfd = (entry->fd > maxfd) ? entry->fd : maxfd;
  223. }
  224. maxfd++;
  225. /* exit loop if callback returns true */
  226. if (pre_select_cb != NULL && pre_select_cb(&maxfd, (void *)&readfds, (void *)&writefds, &timeout, privdata) != 0)
  227. break;
  228. int retval = select(maxfd, &readfds, &writefds, NULL, &timeout);
  229. if (retval < 0 && errno == EINTR) {
  230. errno = 0;
  231. continue;
  232. } else if (retval < 0) {
  233. log_print(LOG_ERROR, "event_loop(): select():");
  234. continue;
  235. }
  236. /* exit loop if callback returns true */
  237. if (post_select_cb != NULL && post_select_cb(retval, (void *)&readfds, (void *)&writefds, privdata) != 0)
  238. break;
  239. /* timeout */
  240. if (retval == 0)
  241. continue;
  242. list_for_each_entry(entry, &event_fd_list, list) {
  243. if (((entry->flags & (FD_READ | EVENT_NEW)) == FD_READ) && FD_ISSET(entry->fd, &readfds))
  244. if (entry->read_cb(entry->fd, entry->read_priv) != 0)
  245. entry->flags |= EVENT_DELETE;
  246. if (((entry->flags & (FD_WRITE | EVENT_NEW)) == FD_WRITE) && FD_ISSET(entry->fd, &writefds))
  247. if (entry->write_cb(entry->fd, entry->write_priv) != 0)
  248. entry->flags |= EVENT_DELETE;
  249. }
  250. }
  251. return 0;
  252. }