static void first_ev_next(msevent *nse, gh_lnode_t **first, int nodeq2) { if (!first || !*first) return; if (&nse->nodeq_io == *first || &nse->nodeq_pcap == *first) { gh_lnode_t *next; next = gh_lnode_next(*first); if (next) { msevent *newevent; if (nodeq2) newevent = lnode_msevent2(next); else newevent = lnode_msevent(next); if (newevent->iod == nse->iod) *first = next; else *first = NULL; } else { *first = NULL; } } }
/* Returns whether something was read */ int pcap_read_on_nonselect(mspool *nsp) { gh_lnode_t *current, *next; msevent *nse; int ret = 0; for (current = gh_list_first_elem(&nsp->pcap_read_events); current != NULL; current = next) { nse = lnode_msevent2(current); if (do_actual_pcap_read(nse) == 1) { /* something received */ ret++; break; } next = gh_lnode_next(current); } return ret; }
/* Cancel an event (such as a timer or read request). If notify is nonzero, the * requester will be sent an event CANCELLED status back to the given handler. * But in some cases there is no need to do this (like if the function deleting * it is the one which created it), in which case 0 can be passed to skip the * step. This function returns zero if the event is not found, nonzero * otherwise. */ int nsock_event_cancel(nsock_pool ms_pool, nsock_event_id id, int notify) { mspool *nsp = (mspool *)ms_pool; enum nse_type type; unsigned int i; gh_list_t *event_list = NULL, *event_list2 = NULL; gh_lnode_t *current, *next; msevent *nse = NULL; assert(nsp); type = get_event_id_type(id); nsock_log_info(nsp, "Event #%li (type %s) cancelled", id, nse_type2str(type)); /* First we figure out what list it is in */ switch (type) { case NSE_TYPE_CONNECT: case NSE_TYPE_CONNECT_SSL: event_list = &nsp->connect_events; break; case NSE_TYPE_READ: event_list = &nsp->read_events; break; case NSE_TYPE_WRITE: event_list = &nsp->write_events; break; case NSE_TYPE_TIMER: for (i = 0; i < gh_heap_count(&nsp->expirables); i++) { gh_hnode_t *hnode; hnode = gh_heap_find(&nsp->expirables, i); nse = container_of(hnode, msevent, expire); if (nse->id == id) return msevent_cancel(nsp, nse, NULL, NULL, notify); } return 0; #if HAVE_PCAP case NSE_TYPE_PCAP_READ: event_list = &nsp->read_events; event_list2 = &nsp->pcap_read_events; break; #endif default: fatal("Bogus event type in nsock_event_cancel"); break; } /* Now we try to find the event in the list */ for (current = gh_list_first_elem(event_list); current != NULL; current = next) { next = gh_lnode_next(current); nse = lnode_msevent(current); if (nse->id == id) break; } if (current == NULL && event_list2) { event_list = event_list2; for (current = gh_list_first_elem(event_list); current != NULL; current = next) { next = gh_lnode_next(current); nse = lnode_msevent2(current); if (nse->id == id) break; } } if (current == NULL) return 0; return msevent_cancel(nsp, nse, event_list, current, notify); }
void process_iod_events(mspool *nsp, msiod *nsi, int ev) { int i = 0; /* store addresses of the pointers to the first elements of each kind instead * of storing the values, as a connect can add a read for instance */ gh_lnode_t **start_elems[] = { &nsi->first_connect, &nsi->first_read, &nsi->first_write, #if HAVE_PCAP &nsi->first_pcap_read, #endif NULL }; gh_list_t *evlists[] = { &nsp->connect_events, &nsp->read_events, &nsp->write_events, #if HAVE_PCAP &nsp->pcap_read_events, #endif NULL }; assert(nsp == nsi->nsp); nsock_log_debug_all(nsp, "Processing events on IOD %lu (ev=%d)", nsi->id, ev); /* We keep the events separate because we want to handle them in the * order: connect => read => write => timer for several reasons: * * 1) Makes sure we have gone through all the net i/o events before * a timer expires (would be a shame to timeout after the data was * available but before we delivered the events * * 2) The connect() results often lead to a read or write that can be * processed in the same cycle. In the same way, read() often * leads to write(). */ for (i = 0; evlists[i] != NULL; i++) { gh_lnode_t *current, *next, *last; /* for each list, get the last event and don't look past it as an event * could add another event in the same list and so on... */ last = gh_list_last_elem(evlists[i]); for (current = *start_elems[i]; current != NULL && gh_lnode_prev(current) != last; current = next) { msevent *nse; #if HAVE_PCAP if (evlists[i] == &nsi->nsp->pcap_read_events) nse = lnode_msevent2(current); else #endif nse = lnode_msevent(current); /* events are grouped by IOD. Break if we're done with the events for the * current IOD */ if (nse->iod != nsi) break; process_event(nsp, evlists[i], nse, ev); next = gh_lnode_next(current); if (nse->event_done) { /* event is done, remove it from the event list and update IOD pointers * to the first events of each kind */ update_first_events(nse); gh_list_remove(evlists[i], current); gh_list_append(&nsp->free_events, &nse->nodeq_io); if (nse->timeout.tv_sec) gh_heap_remove(&nsp->expirables, &nse->expire); } } } }
/* If nsp_new returned success, you must free the nsp when you are done with it * to conserve memory (and in some cases, sockets). After this call, nsp may no * longer be used. Any pending events are sent an NSE_STATUS_KILL callback and * all outstanding iods are deleted. */ void nsp_delete(nsock_pool ms_pool) { mspool *nsp = (mspool *)ms_pool; msevent *nse; msiod *nsi; int i; gh_lnode_t *current, *next; gh_list_t *event_lists[] = { &nsp->connect_events, &nsp->read_events, &nsp->write_events, #if HAVE_PCAP &nsp->pcap_read_events, #endif NULL }; assert(nsp); /* First I go through all the events sending NSE_STATUS_KILL */ for (i = 0; event_lists[i] != NULL; i++) { while (gh_list_count(event_lists[i]) > 0) { gh_lnode_t *lnode = gh_list_pop(event_lists[i]); assert(lnode); #if HAVE_PCAP if (event_lists[i] == &nsp->pcap_read_events) nse = lnode_msevent2(lnode); else #endif nse = lnode_msevent(lnode); assert(nse); nse->status = NSE_STATUS_KILL; nsock_trace_handler_callback(nsp, nse); nse->handler(nsp, nse, nse->userdata); if (nse->iod) { nse->iod->events_pending--; assert(nse->iod->events_pending >= 0); } msevent_delete(nsp, nse); } gh_list_free(event_lists[i]); } /* Kill timers too, they're not in event lists */ while (gh_heap_count(&nsp->expirables) > 0) { gh_hnode_t *hnode; hnode = gh_heap_pop(&nsp->expirables); nse = container_of(hnode, msevent, expire); if (nse->type == NSE_TYPE_TIMER) { nse->status = NSE_STATUS_KILL; nsock_trace_handler_callback(nsp, nse); nse->handler(nsp, nse, nse->userdata); msevent_delete(nsp, nse); gh_list_append(&nsp->free_events, &nse->nodeq_io); } } gh_heap_free(&nsp->expirables); /* foreach msiod */ for (current = gh_list_first_elem(&nsp->active_iods); current != NULL; current = next) { next = gh_lnode_next(current); nsi = container_of(current, msiod, nodeq); nsi_delete(nsi, NSOCK_PENDING_ERROR); gh_list_remove(&nsp->active_iods, current); gh_list_prepend(&nsp->free_iods, &nsi->nodeq); } /* Now we free all the memory in the free iod list */ while ((current = gh_list_pop(&nsp->free_iods))) { nsi = container_of(current, msiod, nodeq); free(nsi); } while ((current = gh_list_pop(&nsp->free_events))) { nse = lnode_msevent(current); free(nse); } gh_list_free(&nsp->active_iods); gh_list_free(&nsp->free_iods); gh_list_free(&nsp->free_events); nsock_engine_destroy(nsp); #if HAVE_OPENSSL if (nsp->sslctx != NULL) SSL_CTX_free(nsp->sslctx); #endif free(nsp); }