int mill_suspend(void) { /* Even if process never gets idle, we have to process external events once in a while. The external signal may very well be a deadline or a user-issued command that cancels the CPU intensive operation. */ static int counter = 0; if(counter >= 103) { mill_wait(0); counter = 0; } /* Store the context of the current coroutine, if any. */ if(mill_running) { mill_ctx ctx = mill_getctx_(); if (mill_setjmp_(ctx)) return mill_running->result; } while(1) { /* If there's a coroutine ready to be executed go for it. */ if(!mill_slist_empty(&mill_ready)) { ++counter; struct mill_slist_item *it = mill_slist_pop(&mill_ready); mill_running = mill_cont(it, struct mill_cr, ready); mill_assert(mill_running->is_ready == 1); mill_running->is_ready = 0; mill_longjmp_(mill_getctx_()); } /* Otherwise, we are going to wait for sleeping coroutines and for external events. */ mill_wait(1); mill_assert(!mill_slist_empty(&mill_ready)); counter = 0; }
/* Pop one value from the channel. */ static void mill_dequeue(chan ch, void *val) { /* Get a blocked sender, if any. */ struct mill_clause *cl = mill_cont(mill_list_begin(&ch->sender.clauses), struct mill_clause, epitem); if(!ch->items) { /* If chdone was already called we can return the value immediately. There are no senders waiting to send. */ if(mill_slow(ch->done)) { mill_assert(!cl); memcpy(val, ((char*)(ch + 1)) + (ch->bufsz * ch->sz), ch->sz); return; } /* Otherwise there must be a sender waiting to send. */ mill_assert(cl); memcpy(val, cl->val, ch->sz); mill_choose_unblock(cl); return; } /* If there's a value in the buffer start by retrieving it. */ memcpy(val, ((char*)(ch + 1)) + (ch->first * ch->sz), ch->sz); ch->first = (ch->first + 1) % ch->bufsz; --ch->items; /* And if there was a sender waiting, unblock it. */ if(cl) { assert(ch->items < ch->bufsz); size_t pos = (ch->first + ch->items) % ch->bufsz; memcpy(((char*)(ch + 1)) + (pos * ch->sz) , cl->val, ch->sz); ++ch->items; mill_choose_unblock(cl); } }
tcpsock tcpaccept(tcpsock s, int64_t deadline) { if(s->type != MILL_TCPLISTENER) mill_panic("trying to accept on a socket that isn't listening"); struct mill_tcplistener *l = (struct mill_tcplistener*)s; while(1) { /* Try to get new connection (non-blocking). */ int as = accept(l->fd, NULL, NULL); if (as >= 0) { mill_tcptune(as); struct mill_tcpconn *conn = malloc(sizeof(struct mill_tcpconn)); if(!conn) { close(as); errno = ENOMEM; return NULL; } tcpconn_init(conn, as); errno = 0; return (tcpsock)conn; } mill_assert(as == -1); if(errno != EAGAIN && errno != EWOULDBLOCK) return NULL; /* Wait till new connection is available. */ int rc = mill_fdwait(l->fd, FDW_IN, deadline); if(rc == 0) { errno = ETIMEDOUT; return NULL; } mill_assert(rc == FDW_IN); } }
static void mill_unixtune(int s) { /* Make the socket non-blocking. */ int opt = fcntl(s, F_GETFL, 0); if (opt == -1) opt = 0; int rc = fcntl(s, F_SETFL, opt | O_NONBLOCK); mill_assert(rc != -1); /* If possible, prevent SIGPIPE signal when writing to the connection already closed by the peer. */ #ifdef SO_NOSIGPIPE opt = 1; rc = setsockopt (s, SOL_SOCKET, SO_NOSIGPIPE, &opt, sizeof (opt)); mill_assert (rc == 0 || errno == EINVAL); #endif }
unixsock unixconnect(const char *addr) { struct sockaddr_un su; int rc = mill_unixresolve(addr, &su); if (rc != 0) { return NULL; } /* Open a socket. */ int s = socket(AF_UNIX, SOCK_STREAM, 0); if(s == -1) return NULL; mill_unixtune(s); /* Connect to the remote endpoint. */ rc = connect(s, (struct sockaddr*)&su, sizeof(struct sockaddr_un)); if(rc != 0) { int err = errno; mill_assert(rc == -1); close(s); errno = err; return NULL; } /* Create the object. */ struct mill_unixconn *conn = malloc(sizeof(struct mill_unixconn)); if(!conn) { close(s); errno = ENOMEM; return NULL; } unixconn_init(conn, s); errno = 0; return (unixsock)conn; }
void tcpflush(tcpsock s, int64_t deadline) { if(s->type != MILL_TCPCONN) mill_panic("trying to send to an unconnected socket"); struct mill_tcpconn *conn = (struct mill_tcpconn*)s; if(!conn->olen) { errno = 0; return; } char *pos = conn->obuf; size_t remaining = conn->olen; while(remaining) { ssize_t sz = send(conn->fd, pos, remaining, 0); if(sz == -1) { if(errno != EAGAIN && errno != EWOULDBLOCK) return; int rc = fdwait(conn->fd, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return; } mill_assert(rc == FDW_OUT); continue; } pos += sz; remaining -= sz; } conn->olen = 0; errno = 0; }
/* Convert literal IPv4 or IPv6 address to a binary one. */ static ipaddr mill_ipliteral(const char *addr, int port, int mode) { ipaddr raddr; struct sockaddr *sa = (struct sockaddr*)&raddr; if(mill_slow(!addr || port < 0 || port > 0xffff)) { sa->sa_family = AF_UNSPEC; errno = EINVAL; return raddr; } switch(mode) { case IPADDR_IPV4: return mill_ipv4_literal(addr, port); case IPADDR_IPV6: return mill_ipv6_literal(addr, port); case 0: case IPADDR_PREF_IPV4: raddr = mill_ipv4_literal(addr, port); if(errno == 0) return raddr; return mill_ipv6_literal(addr, port); case IPADDR_PREF_IPV6: raddr = mill_ipv6_literal(addr, port); if(errno == 0) return raddr; return mill_ipv4_literal(addr, port); default: mill_assert(0); } }
void mfflush(mfile f, int64_t deadline) { if(!f->olen) { errno = 0; return; } char *pos = f->obuf; size_t remaining = f->olen; while(remaining) { ssize_t sz = write(f->fd, pos, remaining); if(sz == -1) { if(errno != EAGAIN && errno != EWOULDBLOCK) return; int rc = fdwait(f->fd, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return; } mill_assert(rc == FDW_OUT); continue; } pos += sz; remaining -= sz; } f->olen = 0; errno = 0; }
void mfclose(mfile f) { fdclean(f->fd); int rc = close(f->fd); mill_assert(rc == 0); free(f); return; }
void unixclose(unixsock s) { if(s->type == MILL_UNIXLISTENER) { struct mill_unixlistener *l = (struct mill_unixlistener*)s; int rc = close(l->fd); mill_assert(rc == 0); free(l); return; } if(s->type == MILL_UNIXCONN) { struct mill_unixconn *c = (struct mill_unixconn*)s; int rc = close(c->fd); mill_assert(rc == 0); free(c); return; } mill_assert(0); }
static void mill_udptune(int s) { /* Make the socket non-blocking. */ int opt = fcntl(s, F_GETFL, 0); if (opt == -1) opt = 0; int rc = fcntl(s, F_SETFL, opt | O_NONBLOCK); mill_assert(rc != -1); }
static void mill_filetune(int fd) { /* Make the file descriptor non-blocking. */ int opt = fcntl(fd, F_GETFL, 0); if (opt == -1) opt = 0; int rc = fcntl(fd, F_SETFL, opt | O_NONBLOCK); mill_assert(rc != -1); }
void tcpclose(tcpsock s) { if(s->type == MILL_TCPLISTENER) { struct mill_tcplistener *l = (struct mill_tcplistener*)s; int rc = close(l->fd); mill_assert(rc == 0); free(l); return; } if(s->type == MILL_TCPCONN) { struct mill_tcpconn *c = (struct mill_tcpconn*)s; int rc = close(c->fd); mill_assert(rc == 0); free(c); return; } mill_assert(0); }
/* Get memory page size. The query is done once only. The value is cached. */ static size_t mill_page_size(void) { static long pgsz = 0; if(mill_fast(pgsz)) return (size_t)pgsz; pgsz = sysconf(_SC_PAGE_SIZE); mill_assert(pgsz > 0); return (size_t)pgsz; }
void *mill_piperecv(struct mill_pipe_s *mp, int *done) { void *ptr = mill_valbuf(mill->running, mp->sz); mill_assert(done); int rc = pipe_read(mp, ptr); if (mill_slow(rc == -1)) mill_panic(strerror(errno)); /* Hmm! */ *done = (rc == 0); return ptr; }
static struct mill_tcpconn *tcpconn_create(int fd) { struct mill_tcpconn *conn = malloc(sizeof(struct mill_tcpconn)); mill_assert(conn); conn->sock.type = MILL_TCPCONN; conn->fd = fd; conn->ifirst = 0; conn->ilen = 0; conn->olen = 0; return conn; }
static int mill_unixresolve(const char *addr, struct sockaddr_un *su) { mill_assert(su); if (strlen(addr) >= sizeof(su->sun_path)) { errno = EINVAL; return -1; } su->sun_family = AF_UNIX; strncpy(su->sun_path, addr, sizeof(su->sun_path)); errno = 0; return 0; }
int tcpport(tcpsock s) { if(s->type == MILL_TCPCONN) { struct mill_tcpconn *c = (struct mill_tcpconn*)s; return mill_ipport(c->addr); } else if(s->type == MILL_TCPLISTENER) { struct mill_tcplistener *l = (struct mill_tcplistener*)s; return l->port; } mill_assert(0); }
void udpsend(udpsock s, ipaddr addr, const void *buf, size_t len) { struct sockaddr *saddr = (struct sockaddr*) &addr; ssize_t ss = sendto(s->fd, buf, len, 0, saddr, saddr->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr_in6)); if(mill_fast(ss == len)) { errno = 0; return; } mill_assert(ss < 0); if(errno == EAGAIN || errno == EWOULDBLOCK) errno = 0; }
void mill_pipeclose(struct mill_pipe_s *mp) { while (1) { if (0 == close(mp->fd[1])) { mp->fd[1] = -1; return; } if (errno != EINTR) { mill_assert(errno == EBADF); break; } } mill_panic("attempt to close previously closed pipe"); }
int mill_fdwait_(int fd, int events, int64_t deadline, const char *current) { check_poller_initialised(); /* If required, start waiting for the timeout. */ if(deadline >= 0) mill_timer_add(&mill_running->timer, deadline, mill_poller_callback); /* If required, start waiting for the file descriptor. */ if(fd >= 0) mill_poller_add(fd, events); /* Do actual waiting. */ mill_running->state = fd < 0 ? MILL_MSLEEP : MILL_FDWAIT; mill_running->fd = fd; mill_running->events = events; mill_set_current(&mill_running->debug, current); int rc = mill_suspend(); /* Handle file descriptor events. */ if(rc >= 0) { mill_assert(!mill_timer_enabled(&mill_running->timer)); return rc; } /* Handle the timeout. */ mill_assert(mill_running->fd == -1); return 0; }
int tcpdetach(tcpsock s) { if(s->type == MILL_TCPLISTENER) { struct mill_tcplistener *l = (struct mill_tcplistener*)s; int fd = l->fd; free(l); return fd; } if(s->type == MILL_TCPCONN) { int fd = ((struct mill_tcpconn*)s)->fd; free(s); return fd; } mill_assert(0); }
int unixdetach(unixsock s) { if(s->type == MILL_UNIXLISTENER) { struct mill_unixlistener *l = (struct mill_unixlistener*)s; int fd = l->fd; free(l); return fd; } if(s->type == MILL_UNIXCONN) { int fd = ((struct mill_unixconn*)s)->fd; free(s); return fd; } mill_assert(0); }
static int pipe_write(struct mill_pipe_s *mp, void *ptr) { while (1) { int n = (int) write(mp->fd[1], ptr, mp->sz); if (mill_fast(n == mp->sz)) break; mill_assert(n < 0); if (errno == EINTR) continue; /* EAGAIN -- pipe capacity execeeded ? */ if (errno != EAGAIN) return -1; mill_fdevent(mp->fd[1], FDW_OUT, -1); } return mp->sz; }
/* Convert literal IPv6 address to a binary one. */ static ipaddr mill_ipv6_literal(const char *addr, int port) { ipaddr raddr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)&raddr; struct sockaddr_in6 *sockv6; char str[IPADDR_MAXSTRLEN]; int rc = inet_pton(AF_INET6, addr, &ipv6->sin6_addr); mill_assert(rc >= 0); if(rc == 1) { ipv6->sin6_family = AF_INET6; ipv6->sin6_port = htons((uint16_t)port); /* Grab IPv6 Scope ID from the IPv6 address. */ struct ifaddrs *ifaces = NULL; int rc = getifaddrs (&ifaces); mill_assert (rc == 0); mill_assert (ifaces); struct ifaddrs *ifv6 = NULL; struct ifaddrs *it; for(it = ifaces; it != NULL; it = it->ifa_next) { if(!it->ifa_addr) continue; if(it->ifa_addr->sa_family == AF_INET6){ sockv6 = (struct sockaddr_in6 *)it->ifa_addr; inet_ntop(AF_INET6, &(sockv6->sin6_addr), str, IPADDR_MAXSTRLEN); ipv6->sin6_scope_id = 2; if(strcmp(str, addr) == 0){ ipv6->sin6_scope_id = if_nametoindex(it->ifa_name); } } } errno = 0; return raddr; } ipv6->sin6_family = AF_UNSPEC; errno = EINVAL; return raddr; }
/* Convert literal IPv6 address to a binary one. */ static ipaddr mill_ipv6_literal(const char *addr, int port) { ipaddr raddr; struct sockaddr_in6 *ipv6 = (struct sockaddr_in6*)&raddr; int rc = inet_pton(AF_INET6, addr, &ipv6->sin6_addr); mill_assert(rc >= 0); if(rc == 1) { ipv6->sin6_family = AF_INET6; ipv6->sin6_port = htons((uint16_t)port); errno = 0; return raddr; } ipv6->sin6_family = AF_UNSPEC; errno = EINVAL; return raddr; }
tcpsock tcpconnect(ipaddr addr, int64_t deadline) { /* Open a socket. */ int s = socket(mill_ipfamily(addr), SOCK_STREAM, 0); if(s == -1) return NULL; mill_tcptune(s); /* Connect to the remote endpoint. */ int rc = connect(s, (struct sockaddr*)&addr, mill_iplen(addr)); if(rc != 0) { mill_assert(rc == -1); if(errno != EINPROGRESS) return NULL; rc = fdwait(s, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return NULL; } int err; socklen_t errsz = sizeof(err); rc = getsockopt(s, SOL_SOCKET, SO_ERROR, (void*)&err, &errsz); if(rc != 0) { err = errno; fdclean(s); close(s); errno = err; return NULL; } if(err != 0) { fdclean(s); close(s); errno = err; return NULL; } } /* Create the object. */ struct mill_tcpconn *conn = malloc(sizeof(struct mill_tcpconn)); if(!conn) { fdclean(s); close(s); errno = ENOMEM; return NULL; } tcpconn_init(conn, s); errno = 0; return (tcpsock)conn; }
size_t tcpsend(tcpsock s, const void *buf, size_t len, int64_t deadline) { if(s->type != MILL_TCPCONN) mill_panic("trying to send to an unconnected socket"); struct mill_tcpconn *conn = (struct mill_tcpconn*)s; /* If it fits into the output buffer copy it there and be done. */ if(conn->olen + len <= MILL_TCP_BUFLEN) { memcpy(&conn->obuf[conn->olen], buf, len); conn->olen += len; errno = 0; return len; } /* If it doesn't fit, flush the output buffer first. */ tcpflush(s, deadline); if(errno != 0) return 0; /* Try to fit it into the buffer once again. */ if(conn->olen + len <= MILL_TCP_BUFLEN) { memcpy(&conn->obuf[conn->olen], buf, len); conn->olen += len; errno = 0; return len; } /* The data chunk to send is longer than the output buffer. Let's do the sending in-place. */ char *pos = (char*)buf; size_t remaining = len; while(remaining) { ssize_t sz = send(conn->fd, pos, remaining, 0); if(sz == -1) { if(errno != EAGAIN && errno != EWOULDBLOCK) return 0; int rc = fdwait(conn->fd, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return len - remaining; } mill_assert(rc == FDW_OUT); continue; } pos += sz; remaining -= sz; } return len; }
static size_t mill_get_stack_size(void) { #if defined HAVE_POSIX_MEMALIGN && HAVE_MPROTECT /* If sanitisation was already done, return the precomputed size. */ if(mill_fast(mill_sanitised_stack_size)) return mill_sanitised_stack_size; mill_assert(mill_stack_size > mill_page_size()); /* Amount of memory allocated must be multiply of the page size otherwise the behaviour of posix_memalign() is undefined. */ size_t sz = (mill_stack_size + mill_page_size() - 1) & ~(mill_page_size() - 1); /* Allocate one additional guard page. */ mill_sanitised_stack_size = sz + mill_page_size(); return mill_sanitised_stack_size; #else return mill_stack_size; #endif }
int64_t now(void) { #if defined __APPLE__ if (mill_slow(!mill_mtid.denom)) mach_timebase_info(&mill_mtid); uint64_t ticks = mach_absolute_time(); return (int64_t)(ticks * mill_mtid.numer / mill_mtid.denom / 1000000); #elif defined CLOCK_MONOTONIC struct timespec ts; int rc = clock_gettime(CLOCK_MONOTONIC, &ts); mill_assert (rc == 0); return ((int64_t)ts.tv_sec) * 1000 + (((int64_t)ts.tv_nsec) / 1000000); #else struct timeval tv; int rc = gettimeofday(&tv, NULL); assert(rc == 0); return ((int64_t)tv.tv_sec) * 1000 + (((int64_t)tv.tv_usec) / 1000); #endif }