void mill_choose_out(void *clause, chan ch, void *val, size_t sz, int idx) { if(mill_slow(!ch)) mill_panic("null channel used"); if(mill_slow(ch->done)) mill_panic("send to done-with channel"); if(mill_slow(ch->sz != sz)) mill_panic("send of a type not matching the channel"); /* Find out whether the clause is immediately available. */ int available = !mill_list_empty(&ch->receiver.clauses) || ch->items < ch->bufsz ? 1 : 0; if(available) ++mill_running->u_choose.available; /* If there are available clauses don't bother with non-available ones. */ if(!available && mill_running->u_choose.available) return; /* Fill in the clause entry. */ struct mill_clause *cl = (struct mill_clause*) clause; cl->cr = mill_running; cl->ep = &ch->sender; cl->val = val; cl->available = available; cl->idx = idx; cl->used = 1; mill_slist_push_back(&mill_running->u_choose.clauses, &cl->chitem); if(cl->ep->seqnum == mill_choose_seqnum) { ++cl->ep->refs; return; } cl->ep->seqnum = mill_choose_seqnum; cl->ep->refs = 1; cl->ep->tmp = -1; }
void mill_chclose(chan ch) { if(mill_slow(!ch)) mill_panic("null channel used"); assert(ch->refcount > 0); --ch->refcount; if(ch->refcount) return; if(!mill_list_empty(&ch->sender.clauses) || !mill_list_empty(&ch->receiver.clauses)) mill_panic("attempt to close a channel while it is still being used"); free(ch); }
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; }
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); } }
void mill_tcpflush_(struct mill_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) { /* Operating systems are inconsistent w.r.t. returning EPIPE and ECONNRESET. Let's paper over it like this. */ if(errno == EPIPE) { errno = ECONNRESET; return; } if(errno != EAGAIN && errno != EWOULDBLOCK) return; int rc = fdwait(conn->fd, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return; } continue; } pos += sz; remaining -= sz; } conn->olen = 0; errno = 0; }
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; }
void *mill_allocstack(void) { if(!mill_slist_empty(&mill_cached_stacks)) { --mill_num_cached_stacks; return (void*)(mill_slist_pop(&mill_cached_stacks) + 1); } void *ptr = mill_allocstackmem(); if(!ptr) mill_panic("not enough memory to allocate coroutine stack"); return ptr; }
size_t mill_tcpsend_(struct mill_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) { /* Operating systems are inconsistent w.r.t. returning EPIPE and ECONNRESET. Let's paper over it like this. */ if(errno == EPIPE) { errno = ECONNRESET; return 0; } if(errno != EAGAIN && errno != EWOULDBLOCK) return 0; int rc = fdwait(conn->fd, FDW_OUT, deadline); if(rc == 0) { errno = ETIMEDOUT; return len - remaining; } continue; } pos += sz; remaining -= sz; } errno = 0; return len; }
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"); }
size_t unixsend(unixsock s, const void *buf, size_t len, int64_t deadline) { if(s->type != MILL_UNIXCONN) mill_panic("trying to send to an unconnected socket"); struct mill_unixconn *conn = (struct mill_unixconn*)s; /* If it fits into the output buffer copy it there and be done. */ if(conn->olen + len <= MILL_UNIX_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. */ unixflush(s, deadline); if(errno != 0) return 0; /* Try to fit it into the buffer once again. */ if(conn->olen + len <= MILL_UNIX_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; }
size_t tcprecvuntil(tcpsock s, void *buf, size_t len, const char *delims, size_t delimcount, int64_t deadline) { if(s->type != MILL_TCPCONN) mill_panic("trying to receive from an unconnected socket"); char *pos = (char*)buf; size_t i; for(i = 0; i != len; ++i, ++pos) { size_t res = tcprecv(s, pos, 1, deadline); if(res == 1) { size_t j; for(j = 0; j != delimcount; ++j) if(*pos == delims[j]) return i + 1; } if (errno != 0) return i + res; } errno = ENOBUFS; return len; }
ipaddr tcpaddr(tcpsock s) { if(s->type != MILL_TCPCONN) mill_panic("trying to get address from a socket that isn't connected"); struct mill_tcpconn *l = (struct mill_tcpconn *)s; return l->addr; }
size_t tcprecv(tcpsock s, void *buf, size_t len, int64_t deadline) { if(s->type != MILL_TCPCONN) mill_panic("trying to receive from an unconnected socket"); struct mill_tcpconn *conn = (struct mill_tcpconn*)s; /* If there's enough data in the buffer it's easy. */ if(conn->ilen >= len) { memcpy(buf, &conn->ibuf[conn->ifirst], len); conn->ifirst += len; conn->ilen -= len; errno = 0; return len; } /* Let's move all the data from the buffer first. */ char *pos = (char*)buf; size_t remaining = len; memcpy(pos, &conn->ibuf[conn->ifirst], conn->ilen); pos += conn->ilen; remaining -= conn->ilen; conn->ifirst = 0; conn->ilen = 0; mill_assert(remaining); while(1) { if(remaining > MILL_TCP_BUFLEN) { /* If we still have a lot to read try to read it in one go directly into the destination buffer. */ ssize_t sz = recv(conn->fd, pos, remaining, 0); if(!sz) { errno = ECONNRESET; return len - remaining; } if(sz == -1) { if(errno != EAGAIN && errno != EWOULDBLOCK) return len - remaining; sz = 0; } if((size_t)sz == remaining) { errno = 0; return len; } pos += sz; remaining -= sz; } else { /* If we have just a little to read try to read the full connection buffer to minimise the number of system calls. */ ssize_t sz = recv(conn->fd, conn->ibuf, MILL_TCP_BUFLEN, 0); if(!sz) { errno = ECONNRESET; return len - remaining; } if(sz == -1) { if(errno != EAGAIN && errno != EWOULDBLOCK) return len - remaining; sz = 0; } if((size_t)sz < remaining) { memcpy(pos, conn->ibuf, sz); pos += sz; remaining -= sz; conn->ifirst = 0; conn->ilen = 0; } else { memcpy(pos, conn->ibuf, remaining); conn->ifirst = remaining; conn->ilen = sz - remaining; errno = 0; return len; } } /* Wait till there's more data to read. */ int res = fdwait(conn->fd, FDW_IN, deadline); if(!res) { errno = ETIMEDOUT; return len - remaining; } } }
int tcpport(tcpsock s) { if(s->type != MILL_TCPLISTENER) mill_panic("trying to get port from a socket that isn't listening"); struct mill_tcplistener *l = (struct mill_tcplistener*)s; return l->port; }
chan mill_chdup(chan ch) { if(mill_slow(!ch)) mill_panic("null channel used"); ++ch->refcount; return ch; }
void mill_choose_otherwise(void) { if(mill_slow(mill_running->u_choose.othws != 0)) mill_panic("multiple 'otherwise' clauses in a choose statement"); mill_running->u_choose.othws = 1; }