int sfdetach(int s, int64_t deadline) { int err = 0; struct sf *conn = msockdata(s, sf_type); if(dill_slow(!conn)) return -1; /* If connection is broken don't even try to do termination handshake. */ if(conn->res == SF_RESET) {err = ECONNRESET; goto dealloc;} /* Ask oworker to exit. */ struct msg msg = {NULL, 0}; int rc = chsend(conn->ochan, &msg, sizeof(msg), deadline); if(dill_slow(rc < 0 && errno == EPIPE)) {err = ECONNRESET; goto dealloc;} if(dill_slow(rc < 0)) {err = errno; goto dealloc;} /* Given that there's no way for oworker to receive this message, the function only exits when it closes the channel. */ rc = chsend(conn->ochan, &msg, sizeof(msg), deadline); dill_assert(rc < 0); if(dill_slow(errno != EPIPE)) {err = errno; goto dealloc;} if(dill_slow(conn->ores == SF_RESET)) {err = ECONNRESET; goto dealloc;} dill_assert(conn->ores == SF_DONE); /* Now that oworker have exited send the termination sequence. */ rc = bsend(conn->u, &sf_termsequence, sizeof(sf_termsequence), -1); if(dill_slow(rc < 0)) {err = errno; goto dealloc;} rc = bflush(conn->u, deadline); if(dill_slow(rc < 0)) {err = errno; goto dealloc;} /* Read and drop any pending inbound messages. By doing this we'll ensure that reading on the underlying socket will continue from the first byte following the sf termination sequence. */ if(conn->res == SF_ACTIVE) { while(1) { struct msg msg; rc = chrecv(conn->ichan, &msg, sizeof(msg), deadline); if(rc < 0) break; free(msg.buf); } if(dill_slow(errno != EPIPE)) {err = errno; goto dealloc;} if(dill_slow(conn->ires == SF_RESET)) {err = ECONNRESET; goto dealloc;} dill_assert(conn->ires == SF_DONE); } dealloc: /* Deallocate the object. */ rc = hclose(conn->iworker); dill_assert(rc == 0); rc = hclose(conn->oworker); dill_assert(rc == 0); rc = hclose(conn->ichan); dill_assert(rc == 0); rc = hclose(conn->ochan); dill_assert(rc == 0); int u = conn->u; free(conn); if(err == 0) return u; rc = hclose(u); dill_assert(rc == 0); errno = err; return -1; }
static int sf_send(int s, const void *buf, size_t len, int64_t deadline) { struct sf *conn = msockdata(s, sf_type); if(dill_slow(!conn)) return -1; if(dill_slow(conn->res != SF_ACTIVE)) {errno = ECONNRESET; return -1;} /* Create a message object. */ struct msg msg; msg.buf = malloc(len); if(dill_slow(!msg.buf)) {errno = ENOMEM; return -1;} memcpy(msg.buf, buf, len); msg.len = len; /* Send it to the worker. */ int rc = chsend(conn->ochan, &msg, sizeof(msg), deadline); if(dill_fast(rc >= 0)) return 0; /* Closed pipe means that the connection was terminated. */ if(errno == EPIPE) { dill_assert(conn->ores == SF_RESET); conn->res = SF_RESET; errno = ECONNRESET; } /* Clean up. */ int err = errno; free(msg.buf); errno = err; return -1; }
coroutine void sender(int ch, int doyield, int val) { if(doyield) { int rc = yield(); errno_assert(rc == 0); } int rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == 0); }
coroutine void receiver2(int ch) { int val; int rc = chrecv(ch, &val, sizeof(val), -1); errno_assert(rc == -1 && errno == EPIPE); val = 0; rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == 0); }
coroutine void sender3(int ch, int doyield) { if(doyield) { int rc = yield(); errno_assert(rc == 0); } int rc = chsend(ch, NULL, 0, -1); errno_assert(rc == 0); }
coroutine void feeder(int ch, int val) { while(1) { int rc = chsend(ch, &val, sizeof(val), -1); if(rc == -1 && errno == ECANCELED) return; errno_assert(rc == 0); rc = yield(); if(rc == -1 && errno == ECANCELED) return; errno_assert(rc == 0); } }
coroutine void dialogue(int s, int ch) { int op = CONN_ESTABLISHED; int rc = chsend(ch, &op, sizeof(op), -1); assert(rc == 0); int64_t deadline = now() + 60000; rc = msend(s, "What's your name?", 17, deadline); if(rc != 0) goto cleanup; char inbuf[256]; ssize_t sz = mrecv(s, inbuf, sizeof(inbuf), deadline); if(sz < 0) goto cleanup; inbuf[sz] = 0; char outbuf[256]; rc = snprintf(outbuf, sizeof(outbuf), "Hello, %s!", inbuf); rc = msend(s, outbuf, rc, deadline); if(rc != 0) goto cleanup; cleanup: op = errno == 0 ? CONN_SUCCEEDED : CONN_FAILED; rc = chsend(ch, &op, sizeof(op), -1); assert(rc == 0 || errno == ECANCELED); rc = hclose(s); assert(rc == 0); }
static int nagle_bsendv(struct bsock_vfs *bvfs, const struct iovec *iov, size_t iovlen, int64_t deadline) { struct nagle_sock *obj = dsock_cont(bvfs, struct nagle_sock, bvfs); /* Send is done in a worker coroutine. */ struct nagle_vec vec = {iov, iovlen}; int rc = chsend(obj->sendch, &vec, sizeof(vec), deadline); if(dsock_slow(rc < 0)) return -1; /* Wait till worker is done. */ int err; rc = chrecv(obj->ackch, &err, sizeof(err), deadline); if(dsock_slow(rc < 0)) return -1; if(dsock_slow(rc < 0)) {errno = err; return -1;} return 0; }
static coroutine void sf_iworker(struct sf *conn) { struct msg msg = {NULL, 0}; while(1) { uint64_t hdr; int rc = brecv(conn->u, &hdr, sizeof(hdr), -1); CHECKRC(ires) if(dill_slow(hdr == sf_termsequence)) {conn->ires = SF_DONE; break;} msg.len = (size_t)dill_getll((uint8_t*)&hdr); msg.buf = malloc(msg.len); dill_assert(msg.buf); rc = brecv(conn->u, msg.buf, msg.len, -1); CHECKRC(ires) rc = chsend(conn->ichan, &msg, sizeof(msg), -1); if(dill_slow(rc < 0 && errno == ECANCELED)) break; dill_assert(rc == 0); msg.buf = NULL; msg.len = 0; } free(msg.buf); int rc = chdone(conn->ichan); dill_assert(rc == 0); }
int main(int argc, char *argv[]) { if(argc != 2) { printf("usage: chrecv <millions-of-messages>\n"); return 1; } long count = atol(argv[1]) * 1000000; int ch = channel(sizeof(char), count); assert(ch >= 0); long i; char val = 0; for(i = 0; i != count; ++i) { int rc = chsend(ch, &val, sizeof(char), -1); assert(rc == 0); } int64_t start = now(); for(i = 0; i != count; ++i) { int rc = chrecv(ch, &val, sizeof(char), -1); assert(rc == 0); } int64_t stop = now(); long duration = (long)(stop - start); long ns = duration * 1000000 / count; printf("received %ldM messages in %f seconds\n", (long)(count / 1000000), ((float)duration) / 1000); printf("duration of receiving a single message: %ld ns\n", ns); printf("message received per second: %fM\n", (float)(1000000000 / ns) / 1000000); return 0; }
int main() { int val; int rc; /* Receiver waits for sender. */ int ch1[2]; rc = chmake(ch1); errno_assert(rc == 0); assert(ch1[0] >= 0); assert(ch1[1] >= 0); int hndl1 = go(sender(ch1[0], 1, 333)); errno_assert(hndl1 >= 0); rc = chrecv(ch1[1], &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 333); rc = hclose(ch1[1]); errno_assert(rc == 0); rc = hclose(ch1[0]); errno_assert(rc == 0); rc = hclose(hndl1); errno_assert(rc == 0); /* Sender waits for receiver. */ int ch2[2]; rc = chmake(ch2); errno_assert(rc == 0); int hndl2 = go(sender(ch2[0], 0, 444)); errno_assert(hndl2 >= 0); rc = chrecv(ch2[1], &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 444); rc = hclose(ch2[1]); errno_assert(rc == 0); rc = hclose(ch2[0]); errno_assert(rc == 0); rc = hclose(hndl2); errno_assert(rc == 0); /* Test two simultaneous senders. */ int ch3[2]; rc = chmake(ch3); errno_assert(rc == 0); int hndl3[2]; hndl3[0] = go(sender(ch3[0], 0, 888)); errno_assert(hndl3[0] >= 0); hndl3[1] = go(sender(ch3[0], 0, 999)); errno_assert(hndl3[1] >= 0); rc = chrecv(ch3[1], &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 888); rc = yield(); errno_assert(rc == 0); rc = chrecv(ch3[1], &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 999); rc = hclose(ch3[1]); errno_assert(rc == 0); rc = hclose(ch3[0]); errno_assert(rc == 0); rc = hclose(hndl3[0]); errno_assert(rc == 0); rc = hclose(hndl3[1]); errno_assert(rc == 0); /* Test two simultaneous receivers. */ int ch4[2]; rc = chmake(ch4); errno_assert(rc == 0); int hndl4[2]; hndl4[0] = go(receiver(ch4[0], 333)); errno_assert(hndl4[0] >= 0); hndl4[1] = go(receiver(ch4[0], 444)); errno_assert(hndl4[1] >= 0); val = 333; rc = chsend(ch4[1], &val, sizeof(val), -1); errno_assert(rc == 0); val = 444; rc = chsend(ch4[1], &val, sizeof(val), -1); errno_assert(rc == 0); rc = hclose(ch4[1]); errno_assert(rc == 0); rc = hclose(ch4[0]); errno_assert(rc == 0); rc = hclose(hndl4[0]); errno_assert(rc == 0); rc = hclose(hndl4[1]); errno_assert(rc == 0); /* Test simple chdone() scenario. */ int ch8[2]; rc = chmake(ch8); errno_assert(rc == 0); rc = chdone(ch8[0]); errno_assert(rc == 0); rc = chrecv(ch8[1], &val, sizeof(val), -1); errno_assert(rc == -1 && errno == EPIPE); rc = chrecv(ch8[1], &val, sizeof(val), -1); errno_assert(rc == -1 && errno == EPIPE); rc = chrecv(ch8[1], &val, sizeof(val), -1); errno_assert(rc == -1 && errno == EPIPE); rc = hclose(ch8[1]); errno_assert(rc == 0); rc = hclose(ch8[0]); errno_assert(rc == 0); /* Test whether chdone() unblocks all receivers. */ int ch12[2]; rc = chmake(ch12); errno_assert(rc == 0); int hndl6[2]; hndl6[0] = go(receiver2(ch12[0])); errno_assert(hndl6[0] >= 0); hndl6[1] = go(receiver2(ch12[0])); errno_assert(hndl6[1] >= 0); rc = chdone(ch12[1]); errno_assert(rc == 0); rc = chrecv(ch12[1], &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 0); rc = chrecv(ch12[1], &val, sizeof(val), -1); errno_assert(rc == 0); assert(val == 0); rc = hclose(ch12[1]); errno_assert(rc == 0); rc = hclose(ch12[0]); errno_assert(rc == 0); rc = hclose(hndl6[0]); errno_assert(rc == 0); rc = hclose(hndl6[1]); errno_assert(rc == 0); /* Test whether chdone() unblocks blocked senders. */ int ch15[2]; rc = chmake(ch15); errno_assert(rc == 0); int hndl8[3]; hndl8[0] = go(sender2(ch15[0])); errno_assert(hndl8[0] >= 0); hndl8[1] = go(sender2(ch15[0])); errno_assert(hndl8[1] >= 0); hndl8[2] = go(sender2(ch15[0])); errno_assert(hndl8[2] >= 0); rc = msleep(now() + 50); errno_assert(rc == 0); rc = chdone(ch15[1]); errno_assert(rc == 0); rc = hclose(ch15[1]); errno_assert(rc == 0); rc = hclose(ch15[0]); errno_assert(rc == 0); rc = hclose(hndl8[0]); errno_assert(rc == 0); rc = hclose(hndl8[1]); errno_assert(rc == 0); rc = hclose(hndl8[2]); errno_assert(rc == 0); /* Test whether hclose() unblocks blocked senders and receivers. */ int ch16[2]; rc = chmake(ch16); errno_assert(rc == 0); int hndl9[2]; hndl9[0] = go(receiver3(ch16[0])); errno_assert(hndl9[0] >= 0); hndl9[1] = go(receiver3(ch16[0])); errno_assert(hndl9[1] >= 0); rc = msleep(now() + 50); errno_assert(rc == 0); rc = hclose(ch16[1]); errno_assert(rc == 0); rc = hclose(ch16[0]); errno_assert(rc == 0); rc = hclose(hndl9[0]); errno_assert(rc == 0); rc = hclose(hndl9[1]); errno_assert(rc == 0); /* Test cancelation. */ int ch17[2]; rc = chmake(ch17); errno_assert(rc == 0); int hndl10 = go(cancel(ch17[0])); errno_assert(hndl10 >= 0); rc = hclose(hndl10); errno_assert(rc == 0); rc = hclose(ch17[1]); errno_assert(rc == 0); rc = hclose(ch17[0]); errno_assert(rc == 0); /* Receiver waits for sender (zero-byte message). */ int ch18[2]; rc = chmake(ch18); errno_assert(rc == 0); int hndl11 = go(sender3(ch18[0], 1)); errno_assert(hndl11 >= 0); rc = chrecv(ch18[1], NULL, 0, -1); errno_assert(rc == 0); rc = hclose(ch18[1]); errno_assert(rc == 0); rc = hclose(ch18[0]); errno_assert(rc == 0); rc = hclose(hndl11); errno_assert(rc == 0); /* Sender waits for receiver (zero-byte message). */ int ch19[2]; rc = chmake(ch19); errno_assert(rc == 0); int hndl12 = go(sender3(ch19[0], 0)); errno_assert(hndl12 >= 0); rc = chrecv(ch19[1], NULL, 0, -1); errno_assert(rc == 0); rc = hclose(ch19[1]); errno_assert(rc == 0); rc = hclose(ch19[0]); errno_assert(rc == 0); rc = hclose(hndl12); errno_assert(rc == 0); /* Channel with user-supplied storage. */ struct chstorage mem; int ch20[2]; rc = chmake_mem(&mem, ch20); errno_assert(rc == 0); rc = chrecv(ch20[0], NULL, 0, now() + 50); errno_assert(rc == -1 && errno == ETIMEDOUT); rc = hclose(ch20[1]); errno_assert(rc == 0); rc = hclose(ch20[0]); errno_assert(rc == 0); return 0; }
static coroutine void nagle_sender(int s, size_t batch, int64_t interval, uint8_t *buf, int sendch, int ackch) { /* Amount of data in the buffer. */ size_t len = 0; /* Last time at least one byte was sent. */ int64_t last = now(); while(1) { /* Get data to send from the user coroutine. */ struct nagle_vec vec; int rc = chrecv(sendch, &vec, sizeof(vec), interval >= 0 && len ? last + interval : -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; /* Timeout expired. Flush the data in the buffer. */ if(dsock_slow(rc < 0 && errno == ETIMEDOUT)) { rc = bsend(s, buf, len, -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; dsock_assert(rc == 0); len = 0; last = now(); continue; } dsock_assert(rc == 0); /* If data fit into the buffer, store them there. */ size_t bytes = iov_size(vec.iov, vec.iovlen); if(len + bytes < batch) { iov_copyallfrom(buf + len, vec.iov, vec.iovlen); len += bytes; int err = 0; rc = chsend(ackch, &err, sizeof(err), -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; dsock_assert(rc == 0); continue; } if(len > 0) { /* Flush the buffer. */ rc = bsend(s, buf, len, -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; /* Pass the error to the user. */ if(dsock_slow(rc < 0)) { int err = errno; rc = chsend(ackch, &err, sizeof(err), -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; dsock_assert(rc == 0); return; } len = 0; last = now(); } /* Once again: If data fit into buffer store them there. */ if(bytes < batch) { iov_copyallfrom(buf, vec.iov, vec.iovlen); len = bytes; int err = 0; rc = chsend(ackch, &err, sizeof(err), -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; dsock_assert(rc == 0); continue; } /* This is a big chunk of data, no need to Nagle it. We'll send it straight away. */ rc = bsendv(s, vec.iov, vec.iovlen, -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; dsock_assert(rc == 0); last = now(); int err = 0; rc = chsend(ackch, &err, sizeof(err), -1); if(dsock_slow(rc < 0 && errno == ECANCELED)) return; dsock_assert(rc == 0); } }
coroutine static void delay(int n, int ch) { int rc = msleep(now() + n); errno_assert(rc == 0); rc = chsend(ch, &n, sizeof(n), -1); errno_assert(rc == 0); }
coroutine void sender2(int ch) { int val = 0; int rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == -1 && errno == EPIPE); }
coroutine void structsender(int ch, struct foo val) { int rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == 0); }
coroutine void charsender(int ch, char val) { int rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == 0); }
coroutine void sender1(int ch, int val) { int rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == 0); }
coroutine void sender3(int ch, int val, int64_t deadline) { int rc = msleep(deadline); errno_assert(rc == 0); rc = chsend(ch, &val, sizeof(val), -1); errno_assert(rc == 0); }
coroutine void sender4(int ch) { struct large large = {{0}}; int rc = chsend(ch, &large, sizeof(large), -1); errno_assert(rc == 0); }