diff --git a/nsock/src/engine_epoll.c b/nsock/src/engine_epoll.c index 9c0070e..274bd7a 100644 --- a/nsock/src/engine_epoll.c +++ b/nsock/src/engine_epoll.c @@ -109,8 +109,6 @@ static void iterate_through_event_lists(struct npool *nsp, int evcount); /* defined in nsock_core.c */ void process_iod_events(struct npool *nsp, struct niod *nsi, int ev); -void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev); -void process_expired_events(struct npool *nsp); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT int pcap_read_on_nonselect(struct npool *nsp); @@ -356,9 +354,6 @@ void iterate_through_event_lists(struct npool *nsp, int evcount) { gh_list_prepend(&nsp->free_iods, &nsi->nodeq); } } - - /* iterate through timers and expired events */ - process_expired_events(nsp); } #endif /* HAVE_EPOLL */ diff --git a/nsock/src/engine_kqueue.c b/nsock/src/engine_kqueue.c index 32c3e80..b9fe6be 100644 --- a/nsock/src/engine_kqueue.c +++ b/nsock/src/engine_kqueue.c @@ -102,8 +102,6 @@ static void iterate_through_event_lists(struct npool *nsp, int evcount); /* defined in nsock_core.c */ void process_iod_events(struct npool *nsp, struct niod *nsi, int ev); -void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev); -void process_expired_events(struct npool *nsp); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT int pcap_read_on_nonselect(struct npool *nsp); @@ -362,9 +360,6 @@ void iterate_through_event_lists(struct npool *nsp, int evcount) { } } } - - /* iterate through timers and expired events */ - process_expired_events(nsp); } #endif /* HAVE_KQUEUE */ diff --git a/nsock/src/engine_poll.c b/nsock/src/engine_poll.c index b38b1fc..1ddc2a0 100644 --- a/nsock/src/engine_poll.c +++ b/nsock/src/engine_poll.c @@ -134,8 +134,6 @@ static void iterate_through_event_lists(struct npool *nsp); /* defined in nsock_core.c */ void process_iod_events(struct npool *nsp, struct niod *nsi, int ev); -void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev); -void process_expired_events(struct npool *nsp); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT int pcap_read_on_nonselect(struct npool *nsp); @@ -424,9 +422,6 @@ void iterate_through_event_lists(struct npool *nsp) { gh_list_prepend(&nsp->free_iods, current); } } - - /* iterate through timers and expired events */ - process_expired_events(nsp); } #endif /* HAVE_POLL */ diff --git a/nsock/src/engine_select.c b/nsock/src/engine_select.c index e9c8571..b144320 100644 --- a/nsock/src/engine_select.c +++ b/nsock/src/engine_select.c @@ -94,9 +94,7 @@ struct io_engine engine_select = { static void iterate_through_event_lists(struct npool *nsp); /* defined in nsock_core.c */ -void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev); void process_iod_events(struct npool *nsp, struct niod *nsi, int ev); -void process_expired_events(struct npool *nsp); #if HAVE_PCAP #ifndef PCAP_CAN_DO_SELECT @@ -388,7 +386,4 @@ void iterate_through_event_lists(struct npool *nsp) { gh_list_prepend(&nsp->free_iods, current); } } - - /* iterate through timers and expired events */ - process_expired_events(nsp); } diff --git a/nsock/src/nsock_core.c b/nsock/src/nsock_core.c index bfaf23b..2ca7410 100644 --- a/nsock/src/nsock_core.c +++ b/nsock/src/nsock_core.c @@ -878,73 +878,6 @@ int pcap_read_on_nonselect(struct npool *nsp) { } #endif /* HAVE_PCAP */ -/* Here is the all important looping function that tells the event engine to - * start up and begin processing events. It will continue until all events have - * been delivered (including new ones started from event handlers), or the - * msec_timeout is reached, or a major error has occurred. Use -1 if you don't - * want to set a maximum time for it to run. A timeout of 0 will return after 1 - * non-blocking loop. The nsock loop can be restarted again after it returns. - * For example you could do a series of 15 second runs, allowing you to do other - * stuff between them */ -enum nsock_loopstatus nsock_loop(nsock_pool nsp, int msec_timeout) { - struct npool *ms = (struct npool *)nsp; - struct timeval loop_timeout; - int msecs_left; - unsigned long loopnum = 0; - enum nsock_loopstatus quitstatus = NSOCK_LOOP_ERROR; - - gettimeofday(&nsock_tod, NULL); - - if (msec_timeout < -1) { - ms->errnum = EINVAL; - return NSOCK_LOOP_ERROR; - } - TIMEVAL_MSEC_ADD(loop_timeout, nsock_tod, msec_timeout); - msecs_left = msec_timeout; - - if (msec_timeout >= 0) - nsock_log_debug(ms, "nsock_loop() started (timeout=%dms). %d events pending", - msec_timeout, ms->events_pending); - else - nsock_log_debug(ms, "nsock_loop() started (no timeout). %d events pending", - ms->events_pending); - - while (1) { - if (ms->quit) { - /* We've been asked to quit the loop through nsock_loop_quit. */ - ms->quit = 0; - quitstatus = NSOCK_LOOP_QUIT; - break; - } - - if (ms->events_pending == 0) { - /* if no events at all are pending, then none can be created until - * we quit nsock_loop() -- so we do that now. */ - quitstatus = NSOCK_LOOP_NOEVENTS; - break; - } - - if (msec_timeout >= 0) { - msecs_left = MAX(0, TIMEVAL_MSEC_SUBTRACT(loop_timeout, nsock_tod)); - if (msecs_left == 0 && loopnum > 0) { - quitstatus = NSOCK_LOOP_TIMEOUT; - break; - } - } - - if (nsock_engine_loop(ms, msecs_left) == -1) { - quitstatus = NSOCK_LOOP_ERROR; - break; - } - - gettimeofday(&nsock_tod, NULL); /* we do this at end because there is one - * at beginning of function */ - loopnum++; - } - - return quitstatus; -} - void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int ev) { int match_r = 0, match_w = 0; #if HAVE_OPENSSL @@ -1069,6 +1002,161 @@ void process_event(struct npool *nsp, gh_list_t *evlist, struct nevent *nse, int } } +static int nevent_unref(struct npool *nsp, struct nevent *nse) { + switch (nse->type) { + case NSE_TYPE_CONNECT: + case NSE_TYPE_CONNECT_SSL: + gh_list_remove(&nsp->connect_events, &nse->nodeq_io); + break; + + case NSE_TYPE_READ: + gh_list_remove(&nsp->read_events, &nse->nodeq_io); + break; + + case NSE_TYPE_WRITE: + gh_list_remove(&nsp->write_events, &nse->nodeq_io); + break; + +#if HAVE_PCAP + case NSE_TYPE_PCAP_READ: { + char read = 0; + char pcap = 0; + +#if PCAP_BSD_SELECT_HACK + read = pcap = 1; +#else + if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0) + read = 1; + else + pcap = 1; +#endif /* PCAP_BSD_SELECT_HACK */ + + if (read) + gh_list_remove(&nsp->read_events, &nse->nodeq_io); + if (pcap) + gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap); + + break; + } +#endif /* HAVE_PCAP */ + + case NSE_TYPE_TIMER: + /* Nothing to do */ + break; + + default: + fatal("Unknown event type %d", nse->type); + } + + gh_list_append(&nsp->free_events, &nse->nodeq_io); + return 0; +} + + +static void process_failed_events(struct npool *nsp) { + gh_lnode_t *lnode; + struct nevent *nse; + + while ((lnode = gh_list_pop(&nsp->failed_events)) != NULL) { + nse = lnode_nevent(lnode); + process_event(nsp, NULL, nse, EV_NONE); + assert(nse->event_done); + gh_list_append(&nsp->free_events, &nse->nodeq_io); + } +} + +static void process_expired_events(struct npool *nsp) { + for (;;) { + gh_hnode_t *hnode; + struct nevent *nse; + + hnode = gh_heap_min(&nsp->expirables); + if (!hnode) + break; + + nse = container_of(hnode, struct nevent, expire); + if (!event_timedout(nse)) + break; + + gh_heap_pop(&nsp->expirables); + process_event(nsp, NULL, nse, EV_NONE); + assert(nse->event_done); + update_first_events(nse); + nevent_unref(nsp, nse); + } +} + +/* Here is the all important looping function that tells the event engine to + * start up and begin processing events. It will continue until all events have + * been delivered (including new ones started from event handlers), or the + * msec_timeout is reached, or a major error has occurred. Use -1 if you don't + * want to set a maximum time for it to run. A timeout of 0 will return after 1 + * non-blocking loop. The nsock loop can be restarted again after it returns. + * For example you could do a series of 15 second runs, allowing you to do other + * stuff between them */ +enum nsock_loopstatus nsock_loop(nsock_pool nsp, int msec_timeout) { + struct npool *ms = (struct npool *)nsp; + struct timeval loop_timeout; + int msecs_left; + unsigned long loopnum = 0; + enum nsock_loopstatus quitstatus = NSOCK_LOOP_ERROR; + + gettimeofday(&nsock_tod, NULL); + + if (msec_timeout < -1) { + ms->errnum = EINVAL; + return NSOCK_LOOP_ERROR; + } + TIMEVAL_MSEC_ADD(loop_timeout, nsock_tod, msec_timeout); + msecs_left = msec_timeout; + + if (msec_timeout >= 0) + nsock_log_debug(ms, "nsock_loop() started (timeout=%dms). %d events pending", + msec_timeout, ms->events_pending); + else + nsock_log_debug(ms, "nsock_loop() started (no timeout). %d events pending", + ms->events_pending); + + while (1) { + if (ms->quit) { + /* We've been asked to quit the loop through nsock_loop_quit. */ + ms->quit = 0; + quitstatus = NSOCK_LOOP_QUIT; + break; + } + + if (ms->events_pending == 0) { + /* if no events at all are pending, then none can be created until + * we quit nsock_loop() -- so we do that now. */ + quitstatus = NSOCK_LOOP_NOEVENTS; + break; + } + + process_failed_events(ms); + + if (msec_timeout >= 0) { + msecs_left = MAX(0, TIMEVAL_MSEC_SUBTRACT(loop_timeout, nsock_tod)); + if (msecs_left == 0 && loopnum > 0) { + quitstatus = NSOCK_LOOP_TIMEOUT; + break; + } + } + + if (nsock_engine_loop(ms, msecs_left) == -1) { + quitstatus = NSOCK_LOOP_ERROR; + break; + } + + process_expired_events(ms); + + gettimeofday(&nsock_tod, NULL); /* we do this at end because there is one + * at beginning of function */ + loopnum++; + } + + return quitstatus; +} + void process_iod_events(struct npool *nsp, struct niod *nsi, int ev) { int i = 0; /* store addresses of the pointers to the first elements of each kind instead @@ -1147,76 +1235,6 @@ void process_iod_events(struct npool *nsp, struct niod *nsi, int ev) { } } -static int nevent_unref(struct npool *nsp, struct nevent *nse) { - switch (nse->type) { - case NSE_TYPE_CONNECT: - case NSE_TYPE_CONNECT_SSL: - gh_list_remove(&nsp->connect_events, &nse->nodeq_io); - break; - - case NSE_TYPE_READ: - gh_list_remove(&nsp->read_events, &nse->nodeq_io); - break; - - case NSE_TYPE_WRITE: - gh_list_remove(&nsp->write_events, &nse->nodeq_io); - break; - -#if HAVE_PCAP - case NSE_TYPE_PCAP_READ: { - char read = 0; - char pcap = 0; - -#if PCAP_BSD_SELECT_HACK - read = pcap = 1; -#else - if (((mspcap *)nse->iod->pcap)->pcap_desc >= 0) - read = 1; - else - pcap = 1; -#endif /* PCAP_BSD_SELECT_HACK */ - - if (read) - gh_list_remove(&nsp->read_events, &nse->nodeq_io); - if (pcap) - gh_list_remove(&nsp->pcap_read_events, &nse->nodeq_pcap); - - break; - } -#endif /* HAVE_PCAP */ - - case NSE_TYPE_TIMER: - /* Nothing to do */ - break; - - default: - fatal("Unknown event type %d", nse->type); - } - gh_list_append(&nsp->free_events, &nse->nodeq_io); - return 0; -} - -void process_expired_events(struct npool *nsp) { - for (;;) { - gh_hnode_t *hnode; - struct nevent *nse; - - hnode = gh_heap_min(&nsp->expirables); - if (!hnode) - break; - - nse = container_of(hnode, struct nevent, expire); - if (!event_timedout(nse)) - break; - - gh_heap_pop(&nsp->expirables); - process_event(nsp, NULL, nse, EV_NONE); - assert(nse->event_done); - update_first_events(nse); - nevent_unref(nsp, nse); - } -} - /* Calling this function will cause nsock_loop to quit on its next iteration * with a return value of NSOCK_LOOP_QUIT. */ void nsock_loop_quit(nsock_pool nsp) { @@ -1324,12 +1342,14 @@ void nsp_add_event(struct npool *nsp, struct nevent *nse) { fatal("Unknown nsock event type (%d)", nse->type); } - /* It can happen that the event already completed. In which case we can - * already deliver it, even though we're probably not inside nsock_loop(). */ + /* It can happen that the event already completed. Remove if from events lists + * and add it to the failed_list for nsock_loop() to deliver the error at + * proper time (i.e.: when the caller is expecting it). */ if (nse->event_done) { - event_dispatch_and_delete(nsp, nse, 1); update_first_events(nse); nevent_unref(nsp, nse); + gh_list_remove(&nsp->free_events, &nse->nodeq_io); /* you're not free yet! */ + gh_list_append(&nsp->failed_events, &nse->nodeq_io); } } diff --git a/nsock/src/nsock_internal.h b/nsock/src/nsock_internal.h index a41ab92..8cbfe07 100644 --- a/nsock/src/nsock_internal.h +++ b/nsock/src/nsock_internal.h @@ -165,6 +165,9 @@ struct npool { /* IO Engine internal data */ void *engine_data; + /* Failed events */ + gh_list_t failed_events; + /* Active network events */ gh_list_t connect_events; gh_list_t read_events; diff --git a/nsock/src/nsock_pool.c b/nsock/src/nsock_pool.c index bd51c48..652ff3f 100644 --- a/nsock/src/nsock_pool.c +++ b/nsock/src/nsock_pool.c @@ -171,6 +171,7 @@ nsock_pool nsp_new(void *userdata) { nsock_engine_init(nsp); /* initialize IO events lists */ + gh_list_init(&nsp->failed_events); gh_list_init(&nsp->connect_events); gh_list_init(&nsp->read_events); gh_list_init(&nsp->write_events); @@ -212,6 +213,7 @@ void nsp_delete(nsock_pool ms_pool) { int i; gh_lnode_t *current, *next; gh_list_t *event_lists[] = { + &nsp->failed_events, &nsp->connect_events, &nsp->read_events, &nsp->write_events, diff --git a/nsock/tests/connect.c b/nsock/tests/connect.c index 78ccfa3..b022371 100644 --- a/nsock/tests/connect.c +++ b/nsock/tests/connect.c @@ -91,8 +91,10 @@ static int connect_tcp_failure(void *tdata) { nsock_connect_tcp(ctd->nsp, ctd->nsi, connect_handler, 4000, NULL, (struct sockaddr *)&peer, sizeof(peer), PORT_TCP); + AssertEqual(ctd->connect_result, 0); nsock_loop(ctd->nsp, 4000); - return ctd->connect_result == -EINVAL ? 0 : ctd->connect_result; + AssertEqual(ctd->connect_result, -EINVAL); + return 0; }