A clone of btpd with my configuration changes.
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

evport.c 12 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. /*
  2. * Submitted by David Pacheco (dp.spambait@gmail.com)
  3. *
  4. * Redistribution and use in source and binary forms, with or without
  5. * modification, are permitted provided that the following conditions
  6. * are met:
  7. * 1. Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * 2. Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * 3. The name of the author may not be used to endorse or promote products
  13. * derived from this software without specific prior written permission.
  14. *
  15. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  16. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  17. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  18. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  19. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  20. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  21. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  22. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  23. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  24. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  25. */
  26. /*
  27. * Copyright (c) 2006 Sun Microsystems. All rights reserved.
  28. * Use is subject to license terms.
  29. */
  30. /*
  31. * evport.c: event backend using Solaris 10 event ports. See port_create(3C).
  32. * This implementation is loosely modeled after the one used for select(2) (in
  33. * select.c).
  34. *
  35. * The outstanding events are tracked in a data structure called evport_data.
  36. * Each entry in the ed_fds array corresponds to a file descriptor, and contains
  37. * pointers to the read and write events that correspond to that fd. (That is,
  38. * when the file is readable, the "read" event should handle it, etc.)
  39. *
  40. * evport_add and evport_del update this data structure. evport_dispatch uses it
  41. * to determine where to callback when an event occurs (which it gets from
  42. * port_getn).
  43. *
  44. * Helper functions are used: grow() grows the file descriptor array as
  45. * necessary when large fd's come in. reassociate() takes care of maintaining
  46. * the proper file-descriptor/event-port associations.
  47. *
  48. * As in the select(2) implementation, signals are handled by evsignal, and
  49. * evport_recalc does almost nothing.
  50. */
  51. #ifdef HAVE_CONFIG_H
  52. #include "config.h"
  53. #endif
  54. #ifdef HAVE_SYS_TIME_H
  55. #include <sys/time.h>
  56. #else
  57. #include <sys/_time.h>
  58. #endif
  59. #include <assert.h>
  60. #include <sys/queue.h>
  61. #include <sys/tree.h>
  62. #include <errno.h>
  63. #include <poll.h>
  64. #include <port.h>
  65. #include <signal.h>
  66. #include <stdio.h>
  67. #include <stdlib.h>
  68. #include <string.h>
  69. #include <time.h>
  70. #include <unistd.h>
  71. #ifdef CHECK_INVARIANTS
  72. #include <assert.h>
  73. #endif
  74. #include "event.h"
  75. #include "event-internal.h"
  76. #include "log.h"
  77. #include "evsignal.h"
  78. extern volatile sig_atomic_t evsignal_caught;
  79. /*
  80. * Default value for ed_nevents, which is the maximum file descriptor number we
  81. * can handle. If an event comes in for a file descriptor F > nevents, we will
  82. * grow the array of file descriptors, doubling its size.
  83. */
  84. #define DEFAULT_NFDS 16
  85. /*
  86. * EVENTS_PER_GETN is the maximum number of events to retrieve from port_getn on
  87. * any particular call. You can speed things up by increasing this, but it will
  88. * (obviously) require more memory.
  89. */
  90. #define EVENTS_PER_GETN 8
  91. /*
  92. * Per-file-descriptor information about what events we're subscribed to. These
  93. * fields are NULL if no event is subscribed to either of them.
  94. */
  95. struct fd_info {
  96. struct event* fdi_revt; /* the event responsible for the "read" */
  97. struct event* fdi_wevt; /* the event responsible for the "write" */
  98. };
  99. #define FDI_HAS_READ(fdi) ((fdi)->fdi_revt != NULL)
  100. #define FDI_HAS_WRITE(fdi) ((fdi)->fdi_wevt != NULL)
  101. #define FDI_HAS_EVENTS(fdi) (FDI_HAS_READ(fdi) || FDI_HAS_WRITE(fdi))
  102. #define FDI_TO_SYSEVENTS(fdi) (FDI_HAS_READ(fdi) ? POLLIN : 0) | \
  103. (FDI_HAS_WRITE(fdi) ? POLLOUT : 0)
  104. struct evport_data {
  105. int ed_port; /* event port for system events */
  106. int ed_nevents; /* number of allocated fdi's */
  107. struct fd_info *ed_fds; /* allocated fdi table */
  108. /* fdi's that we need to reassoc */
  109. struct fd_info *ed_pending[EVENTS_PER_GETN];
  110. };
  111. static void* evport_init (void);
  112. static int evport_add (void *, struct event *);
  113. static int evport_del (void *, struct event *);
  114. static int evport_recalc (struct event_base *, void *, int);
  115. static int evport_dispatch (struct event_base *, void *, struct timeval *);
  116. const struct eventop evportops = {
  117. "event ports",
  118. evport_init,
  119. evport_add,
  120. evport_del,
  121. evport_recalc,
  122. evport_dispatch
  123. };
  124. /*
  125. * Initialize the event port implementation.
  126. */
  127. static void*
  128. evport_init(void)
  129. {
  130. struct evport_data *evpd;
  131. /*
  132. * Disable event ports when this environment variable is set
  133. */
  134. if (getenv("EVENT_NOEVPORT"))
  135. return (NULL);
  136. if (!(evpd = calloc(1, sizeof(struct evport_data))))
  137. return (NULL);
  138. if ((evpd->ed_port = port_create()) == -1) {
  139. free(evpd);
  140. return (NULL);
  141. }
  142. /*
  143. * Initialize file descriptor structure
  144. */
  145. evpd->ed_fds = calloc(DEFAULT_NFDS, sizeof(struct fd_info));
  146. if (evpd->ed_fds == NULL) {
  147. close(evpd->ed_port);
  148. free(evpd);
  149. return (NULL);
  150. }
  151. evpd->ed_nevents = DEFAULT_NFDS;
  152. memset(&evpd->ed_pending, 0, EVENTS_PER_GETN * sizeof(struct fd_info*));
  153. evsignal_init();
  154. return (evpd);
  155. }
  156. #ifdef CHECK_INVARIANTS
  157. /*
  158. * Checks some basic properties about the evport_data structure. Because it
  159. * checks all file descriptors, this function can be expensive when the maximum
  160. * file descriptor ever used is rather large.
  161. */
  162. static void
  163. check_evportop(struct evport_data *evpd)
  164. {
  165. assert(evpd);
  166. assert(evpd->ed_nevents > 0);
  167. assert(evpd->ed_port > 0);
  168. assert(evpd->ed_fds > 0);
  169. /*
  170. * Verify the integrity of the fd_info struct as well as the events to
  171. * which it points (at least, that they're valid references and correct
  172. * for their position in the structure).
  173. */
  174. int i;
  175. for (i = 0; i < evpd->ed_nevents; ++i) {
  176. struct event *ev;
  177. struct fd_info *fdi;
  178. fdi = &evpd->ed_fds[i];
  179. if ((ev = fdi->fdi_revt) != NULL) {
  180. assert(ev->ev_fd == i);
  181. }
  182. if ((ev = fdi->fdi_wevt) != NULL) {
  183. assert(ev->ev_fd == i);
  184. }
  185. }
  186. }
  187. /*
  188. * Verifies very basic integrity of a given port_event.
  189. */
  190. static void
  191. check_event(port_event_t* pevt)
  192. {
  193. /*
  194. * We've only registered for PORT_SOURCE_FD events. The only
  195. * other thing we can legitimately receive is PORT_SOURCE_ALERT,
  196. * but since we're not using port_alert either, we can assume
  197. * PORT_SOURCE_FD.
  198. */
  199. assert(pevt->portev_source == PORT_SOURCE_FD);
  200. assert(pevt->portev_user == NULL);
  201. }
  202. #else
  203. #define check_evportop(epop)
  204. #define check_event(pevt)
  205. #endif /* CHECK_INVARIANTS */
  206. /*
  207. * Doubles the size of the allocated file descriptor array.
  208. */
  209. static int
  210. grow(struct evport_data *epdp, int factor)
  211. {
  212. struct fd_info *tmp;
  213. int oldsize = epdp->ed_nevents;
  214. int newsize = factor * oldsize;
  215. assert(factor > 1);
  216. check_evportop(epdp);
  217. tmp = realloc(epdp->ed_fds, sizeof(struct fd_info) * newsize);
  218. if (NULL == tmp)
  219. return -1;
  220. epdp->ed_fds = tmp;
  221. memset((char*) (epdp->ed_fds + oldsize), 0,
  222. (newsize - oldsize)*sizeof(struct fd_info));
  223. epdp->ed_nevents = newsize;
  224. check_evportop(epdp);
  225. return 0;
  226. }
  227. /*
  228. * (Re)associates the given file descriptor with the event port. The OS events
  229. * are specified (implicitly) from the fd_info struct.
  230. */
  231. static int
  232. reassociate(struct evport_data *epdp, struct fd_info *fdip, int fd)
  233. {
  234. int sysevents = FDI_TO_SYSEVENTS(fdip);
  235. if (sysevents != 0) {
  236. if ((-1 == port_associate(epdp->ed_port, PORT_SOURCE_FD,
  237. fd, sysevents, NULL))) {
  238. perror("port_associate");
  239. return (-1);
  240. }
  241. } else {
  242. if (-1 == port_dissociate(epdp->ed_port, PORT_SOURCE_FD, fd)) {
  243. perror("port_dissociate");
  244. return (-1);
  245. }
  246. }
  247. check_evportop(epdp);
  248. return (0);
  249. }
  250. /*
  251. * Main event loop - polls port_getn for some number of events, and processes
  252. * them.
  253. */
  254. static int
  255. evport_dispatch(struct event_base *base, void *arg, struct timeval *tv)
  256. {
  257. int i, res;
  258. struct evport_data *epdp = arg;
  259. port_event_t pevtlist[EVENTS_PER_GETN];
  260. /*
  261. * port_getn will block until it has at least nevents events. It will
  262. * also return how many it's given us (which may be more than we asked
  263. * for, as long as it's less than our maximum (EVENTS_PER_GETN)) in
  264. * nevents.
  265. */
  266. int nevents = 1;
  267. /*
  268. * We have to convert a struct timeval to a struct timespec
  269. * (only difference is nanoseconds vs. microseconds)
  270. */
  271. struct timespec ts = {tv->tv_sec, tv->tv_usec * 1000};
  272. /*
  273. * Before doing anything else, we need to reassociate the events we hit
  274. * last time which need reassociation. See comment at the end of the
  275. * loop below.
  276. */
  277. for (i = 0; i < EVENTS_PER_GETN; ++i) {
  278. struct fd_info *fdi = epdp->ed_pending[i];
  279. if (fdi != NULL && FDI_HAS_EVENTS(fdi)) {
  280. int fd = FDI_HAS_READ(fdi) ? fdi->fdi_revt->ev_fd :
  281. fdi->fdi_wevt->ev_fd;
  282. reassociate(epdp, fdi, fd);
  283. epdp->ed_pending[i] = NULL;
  284. }
  285. }
  286. if ((res = port_getn(epdp->ed_port, pevtlist, EVENTS_PER_GETN,
  287. &nevents, &ts)) == -1) {
  288. if (errno == EINTR) {
  289. evsignal_process();
  290. return (0);
  291. } else if (errno == ETIME) {
  292. if (nevents == 0)
  293. return (0);
  294. } else {
  295. perror("port_getn");
  296. return (-1);
  297. }
  298. } else if (evsignal_caught) {
  299. evsignal_process();
  300. }
  301. event_debug(("%s: port_getn reports %d events", __func__, nevents));
  302. for (i = 0; i < nevents; ++i) {
  303. struct event *ev;
  304. struct fd_info *fdi;
  305. port_event_t *pevt = &pevtlist[i];
  306. int fd = (int) pevt->portev_object;
  307. check_evportop(epdp);
  308. check_event(pevt);
  309. /*
  310. * Figure out what kind of event it was
  311. * (because we have to pass this to the callback)
  312. */
  313. res = 0;
  314. if (pevt->portev_events & POLLIN)
  315. res |= EV_READ;
  316. if (pevt->portev_events & POLLOUT)
  317. res |= EV_WRITE;
  318. assert(epdp->ed_nevents > fd);
  319. fdi = &(epdp->ed_fds[fd]);
  320. /*
  321. * We now check for each of the possible events (READ or WRITE).
  322. * If the event is not persistent, then we delete it. Then, we
  323. * activate the event (which will cause its callback to be
  324. * executed).
  325. */
  326. if ((res & EV_READ) && ((ev = fdi->fdi_revt) != NULL)) {
  327. if (!(ev->ev_events & EV_PERSIST))
  328. event_del(ev);
  329. event_active(ev, res, 1);
  330. }
  331. if ((res & EV_WRITE) && ((ev = fdi->fdi_wevt) != NULL)) {
  332. if (!(ev->ev_events & EV_PERSIST))
  333. event_del(ev);
  334. event_active(ev, res, 1);
  335. }
  336. /*
  337. * If there are still events (they haven't been deleted), then
  338. * we must reassociate the port, since the event port interface
  339. * dissociates them automatically.
  340. *
  341. * But we can't do it right away, because the event hasn't
  342. * handled this event yet, so of course there's still data
  343. * waiting!
  344. */
  345. if(FDI_HAS_EVENTS(fdi)) {
  346. epdp->ed_pending[i] = fdi;
  347. }
  348. } /* end of all events gotten */
  349. check_evportop(epdp);
  350. return (0);
  351. }
  352. /*
  353. * Copied from the version in select.c
  354. */
  355. static int
  356. evport_recalc(struct event_base *base, void *arg, int max)
  357. {
  358. struct evport_data *evpd = arg;
  359. check_evportop(evpd);
  360. return (0);
  361. }
  362. /*
  363. * Adds the given event (so that you will be notified when it happens via
  364. * the callback function).
  365. */
  366. static int
  367. evport_add(void *arg, struct event *ev)
  368. {
  369. struct evport_data *evpd = arg;
  370. struct fd_info *fdi;
  371. int factor;
  372. check_evportop(evpd);
  373. /*
  374. * Delegate, if it's not ours to handle.
  375. */
  376. if (ev->ev_events & EV_SIGNAL)
  377. return (evsignal_add(ev));
  378. /*
  379. * If necessary, grow the file descriptor info table
  380. */
  381. factor = 1;
  382. while (ev->ev_fd >= factor * evpd->ed_nevents)
  383. factor *= 2;
  384. if (factor > 1) {
  385. if (-1 == grow(evpd, factor)) {
  386. return (-1);
  387. }
  388. }
  389. fdi = &evpd->ed_fds[ev->ev_fd];
  390. if (ev->ev_events & EV_READ)
  391. fdi->fdi_revt = ev;
  392. if (ev->ev_events & EV_WRITE)
  393. fdi->fdi_wevt = ev;
  394. return reassociate(evpd, fdi, ev->ev_fd);
  395. }
  396. /*
  397. * Removes the given event from the list of events to wait for.
  398. */
  399. static int
  400. evport_del(void *arg, struct event *ev)
  401. {
  402. struct evport_data *evpd = arg;
  403. struct fd_info *fdi;
  404. check_evportop(evpd);
  405. /*
  406. * Delegate, if it's not ours to handle
  407. */
  408. if (ev->ev_events & EV_SIGNAL) {
  409. return (evsignal_del(ev));
  410. }
  411. if (evpd->ed_nevents < ev->ev_fd) {
  412. return (-1);
  413. }
  414. fdi = &evpd->ed_fds[ev->ev_fd];
  415. if (ev->ev_events & EV_READ)
  416. fdi->fdi_revt = NULL;
  417. if (ev->ev_events & EV_WRITE)
  418. fdi->fdi_wevt = NULL;
  419. return reassociate(evpd, fdi, ev->ev_fd);
  420. }