/** * @short Advises the listener to stop. * * The listener is advised to stop listening. After this call no listening is still open, and listen could be * called again, or the onion server freed. * * If there is any pending connection, it can finish if onion not freed before. */ void onion_listen_stop(onion* server){ ONION_DEBUG("Stop listening"); int fd=server->listenfd; server->listenfd=-1; if (server->poller && fd>0){ onion_poller_remove(server->poller, fd); } if (fd>0){ shutdown(fd,SHUT_RDWR); // If no shutdown, listen blocks. close(fd); } }
/** * @short Performs the listening with the given mode * @memberof onion_t * * This is the main loop for the onion server. * * It initiates the listening on all the selected ports and addresses. * * @returns !=0 if there is any error. It returns actualy errno from the network operations. See socket for more information. */ int onion_listen(onion *o){ #ifdef HAVE_PTHREADS if (!(o->flags&O_DETACHED) && (o->flags&O_DETACH_LISTEN)){ // Must detach and return o->flags|=O_DETACHED; pthread_create(&o->listen_thread,NULL, (void*)onion_listen, o); return 0; } #endif if (!o->listen_points){ onion_add_listen_point(o,NULL,NULL,onion_http_new()); ONION_DEBUG("Created default HTTP listen port"); } /// Start listening size_t successful_listened_points=0; onion_listen_point **lp=o->listen_points; while (*lp){ int listen_result=onion_listen_point_listen(*lp); if (!listen_result) { successful_listened_points++; } lp++; } if (!successful_listened_points){ ONION_ERROR("There are no available listen points"); return 1; } if (o->flags&O_ONE){ onion_listen_point **listen_points=o->listen_points; if (listen_points[1]!=NULL){ ONION_WARNING("Trying to use non-poll and non-thread mode with several listen points. Only the first will be listened"); } onion_listen_point *op=listen_points[0]; do{ onion_request *req=onion_request_new(op); if (!req) continue; ONION_DEBUG("Accepted request %p", req); onion_request_set_no_keep_alive(req); int ret; do{ ret=req->connection.listen_point->read_ready(req); }while(ret>=0); ONION_DEBUG("End of request %p", req); onion_request_free(req); //req->connection.listen_point->close(req); }while(((o->flags&O_ONE_LOOP) == O_ONE_LOOP) && op->listenfd>0); } else{ onion_listen_point **listen_points=o->listen_points; while (*listen_points){ onion_listen_point *p=*listen_points; ONION_DEBUG("Adding listen point fd %d to poller", p->listenfd); onion_poller_slot *slot=onion_poller_slot_new(p->listenfd, (void*)onion_listen_point_accept, p); onion_poller_slot_set_type(slot, O_POLL_ALL); onion_poller_add(o->poller, slot); listen_points++; } #ifdef HAVE_PTHREADS ONION_DEBUG("Start polling / listening %p, %p, %p", o->listen_points, *o->listen_points, *(o->listen_points+1)); if (o->flags&O_THREADED){ o->threads=malloc(sizeof(pthread_t)*(o->nthreads-1)); int i; for (i=0;i<o->nthreads-1;i++){ pthread_create(&o->threads[i],NULL,(void*)onion_poller_poll, o->poller); } // Here is where it waits.. but eventually it will exit at onion_listen_stop onion_poller_poll(o->poller); ONION_DEBUG("Closing onion_listen"); for (i=0;i<o->nthreads-1;i++){ pthread_join(o->threads[i],NULL); } } else #endif onion_poller_poll(o->poller); listen_points=o->listen_points; while (*listen_points){ onion_listen_point *p=*listen_points; if (p->listenfd>0){ ONION_DEBUG("Removing %d from poller", p->listenfd); onion_poller_remove(o->poller, p->listenfd); } listen_points++; } } return 0; }
/** * @short Do the event polling. * @memberof onion_poller_t * * It loops over polling. To exit polling call onion_poller_stop(). * * If no fd to poll, returns. */ void onion_poller_poll(onion_poller *p){ struct epoll_event event[MAX_EVENTS]; ONION_DEBUG("Start polling"); p->stop=0; #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->npollers++; ONION_DEBUG0("Npollers %d. %d listenings %p", p->npollers, p->n, p->head); pthread_mutex_unlock(&p->mutex); #endif int maxtime; time_t ctime; int timeout; while (!p->stop && p->head){ ctime=time(NULL); pthread_mutex_lock(&p->mutex); maxtime=onion_poller_get_next_timeout(p); pthread_mutex_unlock(&p->mutex); timeout=maxtime-ctime; if (timeout>3600) timeout=3600000; else timeout*=1000; ONION_DEBUG0("Wait for %d ms", timeout); int nfds = epoll_wait(p->fd, event, MAX_EVENTS, timeout); int ctime_end=time(NULL); ONION_DEBUG0("Current time is %d, limit is %d, timeout is %d. Waited for %d seconds", ctime, maxtime, timeout, ctime_end-ctime); ctime=ctime_end; pthread_mutex_lock(&p->mutex); { // Somebody timedout? onion_poller_slot *next=p->head; while (next){ onion_poller_slot *cur=next; next=next->next; if (cur->timeout_limit <= ctime){ ONION_DEBUG0("Timeout on %d, was %d (ctime %d)", cur->fd, cur->timeout_limit, ctime); int i; for (i=0;i<nfds;i++){ onion_poller_slot *el=(onion_poller_slot*)event[i].data.ptr; if (cur==el){ // If removed just one with event, make it ignore the event later. ONION_DEBUG0("Ignoring event as it timeouted: %d", cur->fd); event[i].data.ptr=NULL; } } onion_poller_remove(p, cur->fd); } } } pthread_mutex_unlock(&p->mutex); if (nfds<0){ // This is normally closed p->fd //ONION_DEBUG("Some error happened"); // Also spurious wakeups... gdb is to blame sometimes or any other. if(p->fd<0 || !p->head){ ONION_DEBUG("Finishing the epoll as finished: %s", strerror(errno)); #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->npollers--; pthread_mutex_unlock(&p->mutex); #endif return; } } int i; for (i=0;i<nfds;i++){ onion_poller_slot *el=(onion_poller_slot*)event[i].data.ptr; if (!el) continue; // Call the callback //ONION_DEBUG("Calling callback for fd %d (%X %X)", el->fd, event[i].events); int n=-1; if (event[i].events&EPOLLRDHUP){ n=-1; } else{ // I also take care of the timeout, no timeout when on the handler, it should handle it itself. el->timeout_limit=INT_MAX; #ifdef __DEBUG0__ char **bs=backtrace_symbols((void * const *)&el->f, 1); ONION_DEBUG0("Calling handler: %s (%d)",bs[0], el->fd); onion_low_free(bs); /* This cannot be onion_low_free since from backtrace_symbols. */ #endif /* Sometimes, el->f happens to be null. We want to remove this polling in that weird case. */ if (el->f) n= el->f(el->data); else n= -1; ctime=time(NULL); if (el->timeout>0) el->timeout_limit=ctime+el->timeout; } if (n<0){ onion_poller_remove(p, el->fd); } else{ ONION_DEBUG0("Re setting poller %d", el->fd); event[i].events=el->type; if (p->fd>=0){ int e=epoll_ctl(p->fd, EPOLL_CTL_MOD, el->fd, &event[i]); if (e<0){ ONION_ERROR("Error resetting poller, %s", strerror(errno)); } } } } } ONION_DEBUG("Finished polling fds"); #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->npollers--; ONION_DEBUG0("Npollers %d", p->npollers); pthread_mutex_unlock(&p->mutex); #endif }
/** * @short Do the event polling. * @memberof onion_poller_t * @ingroup poller * * It loops over polling. To exit polling call onion_poller_stop(). * * If no fd to poll, returns. */ void onion_poller_poll(onion_poller *p){ struct epoll_event event[onion_poller_max_events]; ONION_DEBUG("Start polling"); #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->npollers++; p->stop=0; ONION_DEBUG0("Npollers %d. %d listenings %p", p->npollers, p->n, p->head); pthread_mutex_unlock(&p->mutex); #else p->stop=0; #endif #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); char stop = !p->stop && p->head; pthread_mutex_unlock(&p->mutex); #else char stop = !p->stop && p->head; #endif while (stop){ int nfds = epoll_wait(p->fd, event, onion_poller_max_events, -1); if (nfds<0){ // This is normally closed p->fd //ONION_DEBUG("Some error happened"); // Also spurious wakeups... gdb is to blame sometimes or any other. if(p->fd<0 || !p->head){ ONION_DEBUG("Finishing the epoll as finished: %s", strerror(errno)); #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->npollers--; pthread_mutex_unlock(&p->mutex); #endif return; } } int i; for (i=0;i<nfds;i++){ onion_poller_slot *el=(onion_poller_slot*)event[i].data.ptr; if (!el) continue; // Call the callback //ONION_DEBUG("Calling callback for fd %d (%X %X)", el->fd, event[i].events); int n=-1; if (event[i].events&(EPOLLRDHUP | EPOLLHUP)){ n=-1; } else{ // I also take care of the timeout, no timeout when on the handler, it should handle it itself. el->timeout_limit=INT_MAX; #ifdef __DEBUG0__ char **bs=backtrace_symbols((void * const *)&el->f, 1); ONION_DEBUG0("Calling handler: %s (%d)",bs[0], el->fd); onion_low_free(bs); /* This cannot be onion_low_free since from backtrace_symbols. */ #endif n = el->f(el->data); if (el->timeout>0){ el->timeout_limit=onion_time()+el->timeout; onion_poller_timer_check(p, el->timeout_limit); } } if (n<0){ onion_poller_remove(p, el->fd); } else{ ONION_DEBUG0("Re setting poller %d", el->fd); event[i].events=el->type; if (p->fd>=0){ int e=epoll_ctl(p->fd, EPOLL_CTL_MOD, el->fd, &event[i]); if (e<0){ ONION_ERROR("Error resetting poller, %s", strerror(errno)); } } } } #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); stop = !p->stop && p->head; pthread_mutex_unlock(&p->mutex); #else stop = !p->stop && p->head; #endif } ONION_DEBUG("Finished polling fds"); #ifdef HAVE_PTHREADS pthread_mutex_lock(&p->mutex); p->npollers--; ONION_DEBUG0("Npollers %d", p->npollers); pthread_mutex_unlock(&p->mutex); #endif }