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.
 
 
 
 

358 lines
8.1 KiB

  1. #include "fdm.h"
  2. #include <stdlib.h>
  3. #include <stdbool.h>
  4. #include <inttypes.h>
  5. #include <unistd.h>
  6. #include <errno.h>
  7. #include <assert.h>
  8. #include <fcntl.h>
  9. #include <sys/epoll.h>
  10. #include <tllist.h>
  11. #define LOG_MODULE "fdm"
  12. #define LOG_ENABLE_DBG 0
  13. #include "log.h"
  14. struct handler {
  15. int fd;
  16. int events;
  17. fdm_handler_t callback;
  18. void *callback_data;
  19. bool deleted;
  20. };
  21. struct hook {
  22. fdm_hook_t callback;
  23. void *callback_data;
  24. };
  25. typedef tll(struct hook) hooks_t;
  26. struct fdm {
  27. int epoll_fd;
  28. bool is_polling;
  29. tll(struct handler *) fds;
  30. tll(struct handler *) deferred_delete;
  31. hooks_t hooks_low;
  32. hooks_t hooks_normal;
  33. hooks_t hooks_high;
  34. };
  35. struct fdm *
  36. fdm_init(void)
  37. {
  38. int epoll_fd = epoll_create1(EPOLL_CLOEXEC);
  39. if (epoll_fd == -1) {
  40. LOG_ERRNO("failed to create epoll FD");
  41. return NULL;
  42. }
  43. struct fdm *fdm = malloc(sizeof(*fdm));
  44. if (unlikely(fdm == NULL)) {
  45. LOG_ERRNO("malloc() failed");
  46. return NULL;
  47. }
  48. *fdm = (struct fdm){
  49. .epoll_fd = epoll_fd,
  50. .is_polling = false,
  51. .fds = tll_init(),
  52. .deferred_delete = tll_init(),
  53. .hooks_low = tll_init(),
  54. .hooks_normal = tll_init(),
  55. .hooks_high = tll_init(),
  56. };
  57. return fdm;
  58. }
  59. void
  60. fdm_destroy(struct fdm *fdm)
  61. {
  62. if (fdm == NULL)
  63. return;
  64. if (tll_length(fdm->fds) > 0)
  65. LOG_WARN("FD list not empty");
  66. if (tll_length(fdm->hooks_low) > 0 ||
  67. tll_length(fdm->hooks_normal) > 0 ||
  68. tll_length(fdm->hooks_high) > 0)
  69. {
  70. LOG_WARN("hook list not empty");
  71. }
  72. assert(tll_length(fdm->fds) == 0);
  73. assert(tll_length(fdm->deferred_delete) == 0);
  74. assert(tll_length(fdm->hooks_low) == 0);
  75. assert(tll_length(fdm->hooks_normal) == 0);
  76. assert(tll_length(fdm->hooks_high) == 0);
  77. tll_free(fdm->fds);
  78. tll_free(fdm->deferred_delete);
  79. tll_free(fdm->hooks_low);
  80. tll_free(fdm->hooks_normal);
  81. tll_free(fdm->hooks_high);
  82. close(fdm->epoll_fd);
  83. free(fdm);
  84. }
  85. bool
  86. fdm_add(struct fdm *fdm, int fd, int events, fdm_handler_t handler, void *data)
  87. {
  88. #if defined(_DEBUG)
  89. int flags = fcntl(fd, F_GETFL);
  90. if (!(flags & O_NONBLOCK)) {
  91. LOG_ERR("FD=%d is in blocking mode", fd);
  92. assert(false);
  93. return false;
  94. }
  95. tll_foreach(fdm->fds, it) {
  96. if (it->item->fd == fd) {
  97. LOG_ERR("FD=%d already registered", fd);
  98. assert(false);
  99. return false;
  100. }
  101. }
  102. #endif
  103. struct handler *fd_data = malloc(sizeof(*fd_data));
  104. if (unlikely(fd_data == NULL)) {
  105. LOG_ERRNO("malloc() failed");
  106. return false;
  107. }
  108. *fd_data = (struct handler) {
  109. .fd = fd,
  110. .events = events,
  111. .callback = handler,
  112. .callback_data = data,
  113. .deleted = false,
  114. };
  115. tll_push_back(fdm->fds, fd_data);
  116. struct epoll_event ev = {
  117. .events = events,
  118. .data = {.ptr = fd_data},
  119. };
  120. if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_ADD, fd, &ev) < 0) {
  121. LOG_ERRNO("failed to register FD=%d with epoll", fd);
  122. free(fd_data);
  123. tll_pop_back(fdm->fds);
  124. return false;
  125. }
  126. return true;
  127. }
  128. static bool
  129. fdm_del_internal(struct fdm *fdm, int fd, bool close_fd)
  130. {
  131. if (fd == -1)
  132. return true;
  133. tll_foreach(fdm->fds, it) {
  134. if (it->item->fd != fd)
  135. continue;
  136. if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_DEL, fd, NULL) < 0)
  137. LOG_ERRNO("failed to unregister FD=%d from epoll", fd);
  138. if (close_fd)
  139. close(it->item->fd);
  140. it->item->deleted = true;
  141. if (fdm->is_polling)
  142. tll_push_back(fdm->deferred_delete, it->item);
  143. else
  144. free(it->item);
  145. tll_remove(fdm->fds, it);
  146. return true;
  147. }
  148. LOG_ERR("no such FD: %d", fd);
  149. close(fd);
  150. return false;
  151. }
  152. bool
  153. fdm_del(struct fdm *fdm, int fd)
  154. {
  155. return fdm_del_internal(fdm, fd, true);
  156. }
  157. bool
  158. fdm_del_no_close(struct fdm *fdm, int fd)
  159. {
  160. return fdm_del_internal(fdm, fd, false);
  161. }
  162. static bool
  163. event_modify(struct fdm *fdm, struct handler *fd, int new_events)
  164. {
  165. if (new_events == fd->events)
  166. return true;
  167. struct epoll_event ev = {
  168. .events = new_events,
  169. .data = {.ptr = fd},
  170. };
  171. if (epoll_ctl(fdm->epoll_fd, EPOLL_CTL_MOD, fd->fd, &ev) < 0) {
  172. LOG_ERRNO("failed to modify FD=%d with epoll (events 0x%08x -> 0x%08x)",
  173. fd->fd, fd->events, new_events);
  174. return false;
  175. }
  176. fd->events = new_events;
  177. return true;
  178. }
  179. bool
  180. fdm_event_add(struct fdm *fdm, int fd, int events)
  181. {
  182. tll_foreach(fdm->fds, it) {
  183. if (it->item->fd != fd)
  184. continue;
  185. return event_modify(fdm, it->item, it->item->events | events);
  186. }
  187. LOG_ERR("FD=%d not registered with the FDM", fd);
  188. return false;
  189. }
  190. bool
  191. fdm_event_del(struct fdm *fdm, int fd, int events)
  192. {
  193. tll_foreach(fdm->fds, it) {
  194. if (it->item->fd != fd)
  195. continue;
  196. return event_modify(fdm, it->item, it->item->events & ~events);
  197. }
  198. LOG_ERR("FD=%d not registered with the FDM", fd);
  199. return false;
  200. }
  201. static hooks_t *
  202. hook_priority_to_list(struct fdm *fdm, enum fdm_hook_priority priority)
  203. {
  204. switch (priority) {
  205. case FDM_HOOK_PRIORITY_LOW: return &fdm->hooks_low;
  206. case FDM_HOOK_PRIORITY_NORMAL: return &fdm->hooks_normal;
  207. case FDM_HOOK_PRIORITY_HIGH: return &fdm->hooks_high;
  208. }
  209. assert(false);
  210. return NULL;
  211. }
  212. bool
  213. fdm_hook_add(struct fdm *fdm, fdm_hook_t hook, void *data,
  214. enum fdm_hook_priority priority)
  215. {
  216. hooks_t *hooks = hook_priority_to_list(fdm, priority);
  217. #if defined(_DEBUG)
  218. tll_foreach(*hooks, it) {
  219. if (it->item.callback == hook) {
  220. LOG_ERR("hook=0x%" PRIxPTR " already registered", (uintptr_t)hook);
  221. return false;
  222. }
  223. }
  224. #endif
  225. tll_push_back(*hooks, ((struct hook){hook, data}));
  226. return true;
  227. }
  228. bool
  229. fdm_hook_del(struct fdm *fdm, fdm_hook_t hook, enum fdm_hook_priority priority)
  230. {
  231. hooks_t *hooks = hook_priority_to_list(fdm, priority);
  232. tll_foreach(*hooks, it) {
  233. if (it->item.callback != hook)
  234. continue;
  235. tll_remove(*hooks, it);
  236. return true;
  237. }
  238. LOG_WARN("hook=0x%" PRIxPTR " not registered", (uintptr_t)hook);
  239. return false;
  240. }
  241. bool
  242. fdm_poll(struct fdm *fdm)
  243. {
  244. assert(!fdm->is_polling && "nested calls to fdm_poll() not allowed");
  245. if (fdm->is_polling) {
  246. LOG_ERR("nested calls to fdm_poll() not allowed");
  247. return false;
  248. }
  249. tll_foreach(fdm->hooks_high, it) {
  250. LOG_DBG(
  251. "executing high priority hook 0x%" PRIxPTR" (fdm=%p, data=%p)",
  252. (uintptr_t)it->item.callback, (void *)fdm,
  253. (void *)it->item.callback_data);
  254. it->item.callback(fdm, it->item.callback_data);
  255. }
  256. tll_foreach(fdm->hooks_normal, it) {
  257. LOG_DBG(
  258. "executing normal priority hook 0x%" PRIxPTR " (fdm=%p, data=%p)",
  259. (uintptr_t)it->item.callback, (void *)fdm,
  260. (void *)it->item.callback_data);
  261. it->item.callback(fdm, it->item.callback_data);
  262. }
  263. tll_foreach(fdm->hooks_low, it) {
  264. LOG_DBG(
  265. "executing low priority hook 0x%" PRIxPTR " (fdm=%p, data=%p)",
  266. (uintptr_t)it->item.callback, (void *)fdm,
  267. (void *)it->item.callback_data);
  268. it->item.callback(fdm, it->item.callback_data);
  269. }
  270. struct epoll_event events[tll_length(fdm->fds)];
  271. int r = epoll_wait(fdm->epoll_fd, events, tll_length(fdm->fds), -1);
  272. if (r == -1) {
  273. if (errno == EINTR)
  274. return true;
  275. LOG_ERRNO("failed to epoll");
  276. return false;
  277. }
  278. bool ret = true;
  279. fdm->is_polling = true;
  280. for (int i = 0; i < r; i++) {
  281. struct handler *fd = events[i].data.ptr;
  282. if (fd->deleted)
  283. continue;
  284. if (!fd->callback(fdm, fd->fd, events[i].events, fd->callback_data)) {
  285. ret = false;
  286. break;
  287. }
  288. }
  289. fdm->is_polling = false;
  290. tll_foreach(fdm->deferred_delete, it) {
  291. free(it->item);
  292. tll_remove(fdm->deferred_delete, it);
  293. }
  294. return ret;
  295. }