ssize_t udp_recvl_(struct msock_vfs *mvfs, struct ipaddr *addr, struct iolist *first, struct iolist *last, int64_t deadline) { struct udp_sock *obj = dill_cont(mvfs, struct udp_sock, mvfs); struct msghdr hdr; memset(&hdr, 0, sizeof(hdr)); hdr.msg_name = (void*)addr; hdr.msg_namelen = sizeof(struct ipaddr); /* Make a local iovec array. */ /* TODO: This is dangerous, it may cause stack overflow. There should probably be a on-heap per-socket buffer for that. */ size_t niov; int rc = iol_check(first, last, &niov, NULL); if(dill_slow(rc < 0)) return -1; struct iovec iov[niov]; iol_toiov(first, iov); hdr.msg_iov = (struct iovec*)iov; hdr.msg_iovlen = niov; while(1) { ssize_t sz = recvmsg(obj->fd, &hdr, 0); if(sz >= 0) return sz; if(errno != EAGAIN && errno != EWOULDBLOCK) return -1; rc = fdin(obj->fd, deadline); if(dill_slow(rc < 0)) return -1; } }
int dsrecv(int s, void *buf, size_t *len, int64_t deadline) { size_t received = 0; while(1) { ssize_t sz = recv(s, ((char*)buf) + received, *len - received, 0); if(dill_slow(sz == 0)) { *len = received; errno = ECONNRESET; return -1; } if(sz < 0) { if(dill_slow(errno != EWOULDBLOCK && errno != EAGAIN)) { *len = received; return -1; } } else { received += sz; if(received >= *len) return 0; } int rc = fdin(s, deadline); if(dill_slow(rc < 0)) { *len = received; return -1; } } }
int dsaccept(int s, struct sockaddr *addr, socklen_t *addrlen, int64_t deadline) { int as; while(1) { /* Try to accept new connection synchronously. */ as = accept(s, addr, addrlen); if(dill_fast(as >= 0)) break; if(dill_slow(errno != EAGAIN && errno != EWOULDBLOCK)) return -1; /* Operation is in progress. Wait till new connection is available. */ int rc = fdin(s, deadline); if(dill_slow(rc < 0)) return -1; } int rc = dsunblock(as); dill_assert(rc == 0); return as; }
int main() { /* Pipe to check whether child have failed. */ int fds[2]; int rc = pipe(fds); errno_assert(rc == 0); /* Start second coroutine before forking. */ int h = go(worker()); errno_assert(h >= 0); /* Fork. */ h = proc(child(fds[1])); errno_assert(h >= 0); close(fds[1]); /* Parent waits for the child. */ rc = fdin(fds[0], -1); errno_assert(rc == 0); return 0; }
int main() { int rc; /* Check invalid fd. */ rc = fdin(33, -1); assert(rc == -1 && errno == EBADF); rc = fdout(33, -1); assert(rc == -1 && errno == EBADF); /* Create a pair of file deshndliptors for testing. */ int fds[2]; rc = socketpair(AF_UNIX, SOCK_STREAM, 0, fds); errno_assert(rc == 0); /* Check for in & out. */ rc = fdout(fds[0], 0); errno_assert(rc == 0); rc = fdin(fds[0], 0); assert(rc == -1 && errno == ETIMEDOUT); /* Check with infinite timeout. */ rc = fdout(fds[0], -1); errno_assert(rc == 0); /* Check with the timeout that doesn't expire. */ rc = fdout(fds[0], now() + 100); errno_assert(rc == 0); /* Check with the timeout that does expire. */ int64_t deadline = now() + 100; rc = fdin(fds[0], deadline); assert(rc == -1 && errno == ETIMEDOUT); int64_t diff = now() - deadline; time_assert(diff, 0); /* Check cancelation. */ int hndl1 = go(cancel(fds[0])); errno_assert(hndl1 >= 0); rc = hclose(hndl1); errno_assert(rc == 0); /* Check for in. */ ssize_t sz = send(fds[1], "A", 1, 0); errno_assert(sz == 1); rc = fdin(fds[0], -1); errno_assert(rc == 0); char c; sz = recv(fds[0], &c, 1, 0); errno_assert(sz == 1); /* Two interleaved deadlines. */ int64_t start = now(); int hndl2 = go(trigger(fds[0], start + 50)); errno_assert(hndl2 >= 0); rc = fdin(fds[1], start + 90); errno_assert(rc == 0); diff = now() - start; time_assert(diff, 50); rc = hclose(hndl2); errno_assert(rc == 0); /* Check whether closing the connection is reported. */ ssize_t nbytes = send(fds[1], "ABC", 3, 0); errno_assert(nbytes == 3); rc = fdclean(fds[1]); errno_assert(rc == 0); rc = close(fds[1]); errno_assert(rc == 0); rc = msleep(now() + 50); errno_assert(rc == 0); rc = fdin(fds[0], -1); errno_assert(rc == 0); rc = fdout(fds[0], -1); errno_assert(rc == 0); char buf[10]; nbytes = recv(fds[0], buf, sizeof(buf), 0); assert(nbytes == 3); rc = fdin(fds[0], -1); errno_assert(rc == 0); nbytes = recv(fds[0], buf, sizeof(buf), 0); assert(nbytes == 0 || (nbytes == -1 && errno == ECONNRESET)); rc = fdclean(fds[0]); errno_assert(rc == 0); rc = close(fds[0]); errno_assert(rc == 0); /* Test whether fd is removed from the pollset after fdin/fdout times out. */ int pp[2]; rc = pipe(pp); assert(rc == 0); rc = fdin(pp[0], now() + 10); assert(rc == -1 && errno == ETIMEDOUT); nbytes = write(pp[1], "ABC", 3); assert(nbytes == 3); rc = msleep(now() + 100); assert(rc == 0); rc = fdclean(pp[0]); errno_assert(rc == 0); rc = fdclean(pp[1]); errno_assert(rc == 0); rc = close(pp[0]); assert(rc == 0); rc = close(pp[1]); assert(rc == 0); return 0; }
coroutine void cancel(int fd) { int rc = fdin(fd, -1); assert(rc == -1 && errno == ECANCELED); rc = fdin(fd, -1); assert(rc == -1 && errno == ECANCELED); }