void str_cli(FILE *fp, int sockfd) { int kq, i, n, nev, stdineof = 0, isfile; char buf[MAXLINE]; struct kevent kev[2]; struct timespec ts; struct stat st; isfile = ((fstat(fileno(fp), &st) == 0) && (st.st_mode & S_IFMT) == S_IFREG); EV_SET(&kev[0], fileno(fp), EVFILT_READ, EV_ADD, 0, 0, NULL); EV_SET(&kev[1], sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL); kq = Kqueue(); ts.tv_sec = ts.tv_nsec = 0; Kevent(kq, kev, 2, NULL, 0, &ts); for ( ; ; ) { nev = Kevent(kq, NULL, 0, kev, 2, NULL); for (i = 0; i < nev; i++) { if (kev[i].ident == sockfd) { /* socket is readable */ if ( (n = Read(sockfd, buf, MAXLINE)) == 0) { if (stdineof == 1) return; /* normal termination */ else err_quit("str_cli: server terminated prematurely"); } Write(fileno(stdout), buf, n); } if (kev[i].ident == fileno(fp)) { /* input is readable */ n = Read(fileno(fp), buf, MAXLINE); if (n > 0) Writen(sockfd, buf, n); if (n == 0 || (isfile && n == kev[i].data)) { stdineof = 1; Shutdown(sockfd, SHUT_WR); /* send FIN */ kev[i].flags = EV_DELETE; Kevent(kq, &kev[i], 1, NULL, 0, &ts); /* remove kevent */ continue; } } } } }
// Parameters are taken from EV_SET (see kevent(2)) static int enqueue_kqueue_change(poller *p,uintptr_t ident,short filter,u_short flags, u_int fflags,intptr_t data,void *udata){ // This unlikely event could occur if, for instance, every fd is being // used, and on the last sysdep_poll() every one had event changes, // and then a verdict was injected while the poller was blocked.... or // (far more likely) in the case of programming error, heheh if(p->kchanges >= p->csize){ typeof(p->csize) tmpcsize = p->csize * 2; typeof(*p->cv) *tmpcv; // If we've not yet been initialized, or reset, don't start // growing buffers...that's a serious problem. Should this be // changed, insert a p->csize check around the Kevent() failure // case involving vector dump and reuse of implied first slot! if(p->csize == 0){ bitch("Used with uninitialized poller\n"); return -1; } if((tmpcv = Realloc("kchange vector",p->cv,sizeof(*tmpcv) * tmpcsize)) == NULL){ // If the realloc failed, dump the current vector... if(Kevent(p->kq,p->cv,p->kchanges,NULL,0,NULL)){ inc_stateexceptions(); return -1; } // Reinitialize the buffer. We're assured that there's // room for at least one of us now by p->csize check p->kchanges = 0; }else{ p->cv = tmpcv; p->csize = tmpcsize; } } EV_SET(&p->cv[p->kchanges++],ident,filter,flags,fflags,data,udata); return 0; }
static inline void handle_event(torque_ctx *ctx,const kevententry *e){ #ifdef TORQUE_LINUX if(e->events & EVREAD){ #else if(e->filter == EVFILT_READ){ #endif handle_evsource_read(ctx->eventtables.fdarray,KEVENTENTRY_ID(e)); } #ifdef TORQUE_LINUX if(e->events & EVWRITE){ #else else if(e->filter == EVFILT_WRITE){ #endif handle_evsource_write(ctx->eventtables.fdarray,KEVENTENTRY_ID(e)); } #ifdef TORQUE_FREEBSD else if(e->filter == EVFILT_SIGNAL){ handle_evsource_read(ctx->eventtables.sigarray,KEVENTENTRY_ID(e)); }else if(e->filter == EVFILT_TIMER){ timer_curry(KEVENTENTRY_IDPTR(e)); } #endif } void rxcommonsignal(int sig,void *cbstate){ if(sig == EVTHREAD_TERM || sig == EVTHREAD_INT){ const torque_ctx *ctx = cbstate; void *ret = PTHREAD_CANCELED; evhandler *e = get_thread_evh(); struct rusage ru; int r; // There's no POSIX thread cancellation going on here, nor are // we terminating due to signal; we're catching the signal and // exiting from this thread only. The trigger signal might be // delivered to any one of our threads; if we're here, though, // we cannot be holding the efd. Progress is thus assured. pthread_kill(e->nexttid,sig); // We rely on EDEADLK to cut off our circular join()list if((r = pthread_join(e->nexttid,&ret)) && r != EDEADLK){ ret = NULL; } // FIXME this is kind of lame. I'd like to emulate // RUSAGE_THREAD when it's unavailable, and either way the test // ought be based on whether RUSAGE_THREAD is *expected*, not // *defined* (Linux 2.6.26+) for future-proofing. #ifdef RUSAGE_THREAD getrusage(RUSAGE_THREAD,&ru); #else getrusage(RUSAGE_SELF,&ru); #endif e->stats.utimeus = ru.ru_utime.tv_sec * 1000000 + ru.ru_utime.tv_usec; e->stats.stimeus = ru.ru_stime.tv_sec * 1000000 + ru.ru_stime.tv_usec; e->stats.vctxsw = ru.ru_nvcsw; e->stats.ictxsw = ru.ru_nivcsw; destroy_evhandler(ctx,e); pthread_exit(ret); // FIXME need clean up stack } } #if defined(TORQUE_LINUX) && !defined(TORQUE_LINUX_SIGNALFD) static sig_atomic_t sem_rxcommonsignal; static inline void check_for_termination(void){ if(sem_rxcommonsignal){ int s; s = sem_rxcommonsignal; sem_rxcommonsignal = 0; rxcommonsignal(s,get_thread_ctx()); } } static void rxcommonsignal_handler(int sig,void *cbstate __attribute__ ((unused))){ sem_rxcommonsignal = sig; } #else #define check_for_termination(...) #endif void event_thread(torque_ctx *ctx,evhandler *e){ tsd_evhandler = e; tsd_ctx = ctx; while(1){ int events; check_for_termination(); events = Kevent(e->evq->efd,NULL,0,PTR_TO_EVENTV(&e->evec),e->evec.vsizes); ++e->stats.rounds; if(events < 0){ if(errno != EINTR){ ++e->stats.pollerr; } continue; } while(events--){ #ifdef TORQUE_LINUX handle_event(ctx,&PTR_TO_EVENTV(&e->evec)->events[events]); #else handle_event(ctx,&PTR_TO_EVENTV(&e->evec)[events]); #endif ++e->stats.events; } } } static int #ifdef TORQUE_LINUX create_evector(struct kevent *kv,int n){ if((kv->events = malloc(n * sizeof(*kv->events))) == NULL){ return -1; } if((kv->ctldata = malloc(n * sizeof(*kv->ctldata))) == NULL){ free(kv->events); return -1; } return 0; #else create_evector(struct kevent **kv,int n){ if((*kv = malloc(n * sizeof(**kv))) == NULL){ return -1; } return 0; #endif } static void #ifdef TORQUE_LINUX destroy_evector(struct kevent *kv){ free(kv->events); free(kv->ctldata); #else destroy_evector(struct kevent **kv){ free(*kv); #endif } static int init_evectors(evectors *ev){ // We probably want about a half (small) page's worth...? FIXME ev->vsizes = 512; if(create_evector(&ev->eventv,ev->vsizes)){ return -1; } return 0; }