int tcp_master(dthread_t *thread) { if (!thread || !thread->data) { return KNOT_EINVAL; } iohandler_t *handler = (iohandler_t *)thread->data; unsigned *iostate = &handler->thread_state[dt_get_id(thread)]; int ret = KNOT_EOK; ref_t *ref = NULL; tcp_context_t tcp; memset(&tcp, 0, sizeof(tcp_context_t)); /* Create big enough memory cushion. */ knot_mm_t mm; mm_ctx_mempool(&mm, 16 * MM_DEFAULT_BLKSIZE); /* Create TCP answering context. */ tcp.server = handler->server; tcp.thread_id = handler->thread_id[dt_get_id(thread)]; tcp.overlay.mm = &mm; /* Prepare structures for bound sockets. */ conf_val_t val = conf_get(conf(), C_SRV, C_LISTEN); fdset_init(&tcp.set, conf_val_count(&val) + CONF_XFERS); /* Create iovec abstraction. */ for (unsigned i = 0; i < 2; ++i) { tcp.iov[i].iov_len = KNOT_WIRE_MAX_PKTSIZE; tcp.iov[i].iov_base = malloc(tcp.iov[i].iov_len); if (tcp.iov[i].iov_base == NULL) { ret = KNOT_ENOMEM; goto finish; } } /* Initialize sweep interval. */ timev_t next_sweep = {0}; time_now(&next_sweep); next_sweep.tv_sec += TCP_SWEEP_INTERVAL; for(;;) { /* Check handler state. */ if (unlikely(*iostate & ServerReload)) { *iostate &= ~ServerReload; /* Cancel client connections. */ for (unsigned i = tcp.client_threshold; i < tcp.set.n; ++i) { close(tcp.set.pfd[i].fd); } ref_release(ref); ref = server_set_ifaces(handler->server, &tcp.set, IO_TCP, tcp.thread_id); if (tcp.set.n == 0) { break; /* Terminate on zero interfaces. */ } tcp.client_threshold = tcp.set.n; } /* Check for cancellation. */ if (dt_is_cancelled(thread)) { break; } /* Serve client requests. */ tcp_wait_for_events(&tcp); /* Sweep inactive clients. */ if (tcp.last_poll_time.tv_sec >= next_sweep.tv_sec) { fdset_sweep(&tcp.set, &tcp_sweep, NULL); time_now(&next_sweep); next_sweep.tv_sec += TCP_SWEEP_INTERVAL; } } finish: free(tcp.iov[0].iov_base); free(tcp.iov[1].iov_base); mp_delete(mm.ctx); fdset_clear(&tcp.set); ref_release(ref); return ret; }
int main(int argc, char *argv[]) { plan(12); /* 1. Create fdset. */ fdset_t set; int ret = fdset_init(&set, 32); is_int(0, ret, "fdset: init"); /* 2. Create pipe. */ int fds[2], tmpfds[2]; ret = pipe(fds); ok(ret >= 0, "fdset: pipe() works"); ret = pipe(tmpfds); ok(ret >= 0, "fdset: 2nd pipe() works"); /* 3. Add fd to set. */ ret = fdset_add(&set, fds[0], POLLIN, NULL); is_int(0, ret, "fdset: add to set works"); fdset_add(&set, tmpfds[0], POLLIN, NULL); /* Schedule write. */ struct timeval ts, te; gettimeofday(&ts, 0); pthread_t t; pthread_create(&t, 0, thr_action, &fds[1]); /* 4. Watch fdset. */ int nfds = poll(set.pfd, set.n, 60 * 1000); gettimeofday(&te, 0); size_t diff = timeval_diff(&ts, &te); ok(nfds > 0, "fdset: poll returned %d events in %zu ms", nfds, diff); /* 5. Prepare event set. */ ok(set.pfd[0].revents & POLLIN, "fdset: pipe is active"); /* 6. Receive data. */ char buf = 0x00; ret = read(set.pfd[0].fd, &buf, WRITE_PATTERN_LEN); ok(ret >= 0 && buf == WRITE_PATTERN, "fdset: contains valid data"); /* 7-9. Remove from event set. */ ret = fdset_remove(&set, 0); is_int(0, ret, "fdset: remove from fdset works"); close(fds[0]); close(fds[1]); ret = fdset_remove(&set, 0); close(tmpfds[1]); close(tmpfds[1]); is_int(0, ret, "fdset: remove from fdset works (2)"); ret = fdset_remove(&set, 0); ok(ret != 0, "fdset: removing nonexistent item"); /* 10. Crash test. */ fdset_init(0, 0); fdset_add(0, 1, 1, 0); fdset_add(0, 0, 1, 0); fdset_remove(0, 1); fdset_remove(0, 0); ok(1, "fdset: crash test successful"); /* 11. Destroy fdset. */ ret = fdset_clear(&set); is_int(0, ret, "fdset: destroyed"); /* Cleanup. */ pthread_join(t, 0); return 0; }