static int lz4_msendv(struct msock_vfs *mvfs, const struct iovec *iov, size_t iovlen, int64_t deadline) { struct lz4_sock *obj = dsock_cont(mvfs, struct lz4_sock, mvfs); /* Adjust the buffer size as needed. */ size_t len = iov_size(iov, iovlen); size_t maxlen = LZ4F_compressFrameBound(len, NULL); if(obj->outlen < maxlen) { uint8_t *newbuf = realloc(obj->outbuf, maxlen); if(dsock_slow(!newbuf)) {errno = ENOMEM; return -1;} obj->outbuf = newbuf; obj->outlen = maxlen; } /* Compress the data. */ /* TODO: Avoid the extra allocations and copies. */ uint8_t *buf = malloc(len); if(dsock_slow(!buf)) {errno = ENOMEM; return -1;} iov_copyallfrom(buf, iov, iovlen); LZ4F_preferences_t prefs = {0}; prefs.frameInfo.contentSize = len; size_t dstlen = LZ4F_compressFrame(obj->outbuf, obj->outlen, buf, len, &prefs); dsock_assert(!LZ4F_isError(dstlen)); dsock_assert(dstlen <= obj->outlen); free(buf); /* Send the compressed frame. */ return msend(obj->s, obj->outbuf, dstlen, deadline); }
static int bthrottler_brecv(int s, void *buf, size_t len, int64_t deadline) { struct bthrottlersock *obj = hdata(s, bsock_type); dsock_assert(obj->vfptrs.type == bthrottler_type); /* If recv-throttling is off forward the call. */ if(obj->recv_full == 0) return brecv(obj->s, buf, len, deadline); /* Get rid of the corner case. */ if(dsock_slow(len == 0)) return 0; while(1) { /* If there's capacity receive as much data as possible. */ if(obj->recv_remaining) { size_t torecv = len < obj->recv_remaining ? len : obj->recv_remaining; int rc = brecv(obj->s, buf, torecv, deadline); if(dsock_slow(rc < 0)) return -1; obj->recv_remaining -= torecv; buf = (char*)buf + torecv; len -= torecv; if(len == 0) return 0; } /* Wait till capacity can be renewed. */ int rc = msleep(obj->recv_last + obj->recv_interval); if(dsock_slow(rc < 0)) return -1; /* Renew the capacity. */ obj->recv_remaining = obj->recv_full; obj->recv_last = now(); } }
int lz4_start(int s) { int err; /* Check whether underlying socket is message-based. */ if(dsock_slow(!hquery(s, msock_type))) {err = errno; goto error1;} /* Create the object. */ struct lz4_sock *obj = malloc(sizeof(struct lz4_sock)); if(dsock_slow(!obj)) {errno = ENOMEM; goto error1;} obj->hvfs.query = lz4_hquery; obj->hvfs.close = lz4_hclose; obj->mvfs.msendv = lz4_msendv; obj->mvfs.mrecvv = lz4_mrecvv; obj->s = s; obj->outbuf = NULL; obj->outlen = 0; obj->inbuf = NULL; obj->inlen = 0; size_t ec = LZ4F_createDecompressionContext(&obj->dctx, LZ4F_VERSION); if(dsock_slow(LZ4F_isError(ec))) {err = EFAULT; goto error2;} /* Create the handle. */ int h = hcreate(&obj->hvfs); if(dsock_slow(h < 0)) {err = errno; goto error3;} return h; error3: ec = LZ4F_freeDecompressionContext(obj->dctx); dsock_assert(!LZ4F_isError(ec)); error2: free(obj); error1: errno = err; return -1; }
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; }
int mthrottler_stop(int s) { struct mthrottler_sock *obj = hquery(s, mthrottler_type); if(dsock_slow(!obj)) return -1; int u = obj->s; free(obj); return u; }
int bthrottler_stop(int s) { struct bthrottlersock *obj = hdata(s, bsock_type); if(dsock_slow(obj && obj->vfptrs.type != bthrottler_type)) { errno = ENOTSUP; return -1;} int u = obj->s; free(obj); return u; }
int mlog_stop(int s) { struct mlogsock *obj = hdata(s, msock_type); if(dsock_slow(obj && obj->vfptrs.type != mlog_type)) { errno = ENOTSUP; return -1;} int u = obj->s; free(obj); return u; }
static ssize_t mthrottler_mrecvv(struct msock_vfs *mvfs, const struct iovec *iov, size_t iovlen, int64_t deadline) { struct mthrottler_sock *obj = dsock_cont(mvfs, struct mthrottler_sock, mvfs); /* If recv-throttling is off forward the call. */ if(obj->recv_full == 0) return mrecvv(obj->s, iov, iovlen, deadline); /* If there's no quota wait till it is renewed. */ if(!obj->recv_remaining) { int rc = msleep(obj->recv_last + obj->recv_interval); if(dsock_slow(rc < 0)) return -1; obj->recv_remaining = obj->recv_full; obj->recv_last = now(); } /* Receive the message. */ int rc = mrecvv(obj->s, iov, iovlen, deadline); if(dsock_slow(rc < 0)) return -1; --obj->recv_remaining; return 0; }
static ssize_t mthrottler_mrecv(int s, void *buf, size_t len, int64_t deadline) { struct mthrottlersock *obj = hdata(s, msock_type); dsock_assert(obj->vfptrs.type == mthrottler_type); /* If recv-throttling is off forward the call. */ if(obj->recv_full == 0) return mrecv(obj->s, buf, len, deadline); /* If there's no quota wait till it is renewed. */ if(!obj->recv_remaining) { int rc = msleep(obj->recv_last + obj->recv_interval); if(dsock_slow(rc < 0)) return -1; obj->recv_remaining = obj->recv_full; obj->recv_last = now(); } /* Receive the message. */ int rc = mrecv(obj->s, buf, len, deadline); if(dsock_slow(rc < 0)) return -1; --obj->recv_remaining; return 0; }
int lz4_stop(int s) { struct lz4_sock *obj = hquery(s, lz4_type); if(dsock_slow(!obj)) return -1; size_t ec = LZ4F_freeDecompressionContext(obj->dctx); dsock_assert(!LZ4F_isError(ec)); free(obj->inbuf); free(obj->outbuf); int u = obj->s; free(obj); return u; }
int mthrottler_start(int s, uint64_t send_throughput, int64_t send_interval, uint64_t recv_throughput, int64_t recv_interval) { if(dsock_slow(send_throughput != 0 && send_interval <= 0 )) { errno = EINVAL; return -1;} if(dsock_slow(recv_throughput != 0 && recv_interval <= 0 )) { errno = EINVAL; return -1;} /* Check whether underlying socket is message-based. */ if(dsock_slow(!hquery(s, msock_type))) return -1; /* Create the object. */ struct mthrottler_sock *obj = malloc(sizeof(struct mthrottler_sock)); if(dsock_slow(!obj)) {errno = ENOMEM; return -1;} obj->hvfs.query = mthrottler_hquery; obj->hvfs.close = mthrottler_hclose; obj->mvfs.msendv = mthrottler_msendv; obj->mvfs.mrecvv = mthrottler_mrecvv; obj->s = s; obj->send_full = 0; if(send_throughput > 0) { obj->send_full = send_throughput * send_interval / 1000; obj->send_remaining = obj->send_full; obj->send_interval = send_interval; obj->send_last = now(); } obj->recv_full = 0; if(recv_throughput > 0) { obj->recv_full = recv_throughput * recv_interval / 1000; obj->recv_remaining = obj->recv_full; obj->recv_interval = recv_interval; obj->recv_last = now(); } /* Create the handle. */ int h = hcreate(&obj->hvfs); if(dsock_slow(h < 0)) { int err = errno; free(obj); errno = err; return -1; } return h; }
int bthrottler_start(int s, uint64_t send_throughput, int64_t send_interval, uint64_t recv_throughput, int64_t recv_interval) { if(dsock_slow(send_throughput != 0 && send_interval <= 0 )) { errno = EINVAL; return -1;} if(dsock_slow(recv_throughput != 0 && recv_interval <= 0 )) { errno = EINVAL; return -1;} /* Check whether underlying socket is a bytestream. */ if(dsock_slow(!hdata(s, bsock_type))) return -1; /* Create the object. */ struct bthrottlersock *obj = malloc(sizeof(struct bthrottlersock)); if(dsock_slow(!obj)) {errno = ENOMEM; return -1;} obj->vfptrs.hvfptrs.close = bthrottler_close; obj->vfptrs.type = bthrottler_type; obj->vfptrs.bsend = bthrottler_bsend; obj->vfptrs.brecv = bthrottler_brecv; obj->s = s; obj->send_full = 0; if(send_throughput > 0) { obj->send_full = send_throughput * send_interval / 1000; obj->send_remaining = obj->send_full; obj->send_interval = send_interval; obj->send_last = now(); } obj->recv_full = 0; if(recv_throughput > 0) { obj->recv_full = recv_throughput * recv_interval / 1000; obj->recv_remaining = obj->recv_full; obj->recv_interval = recv_interval; obj->recv_last = now(); } /* Create the handle. */ int h = handle(bsock_type, obj, &obj->vfptrs.hvfptrs); if(dsock_slow(h < 0)) { int err = errno; free(obj); errno = err; return -1; } return h; }
int mlog_start(int s) { /* Check whether underlying socket is a bytestream. */ if(dsock_slow(!hdata(s, msock_type))) return -1; /* Create the object. */ struct mlogsock *obj = malloc(sizeof(struct mlogsock)); if(dsock_slow(!obj)) {errno = ENOMEM; return -1;} obj->vfptrs.hvfptrs.close = mlog_close; obj->vfptrs.type = mlog_type; obj->vfptrs.msend = mlog_msend; obj->vfptrs.mrecv = mlog_mrecv; obj->s = s; /* Create the handle. */ int h = handle(msock_type, obj, &obj->vfptrs.hvfptrs); if(dsock_slow(h < 0)) { int err = errno; free(obj); errno = err; return -1; } return h; }
static ssize_t mlog_mrecv(int s, void *buf, size_t len, int64_t deadline) { struct mlogsock *obj = hdata(s, msock_type); dsock_assert(obj->vfptrs.type == mlog_type); ssize_t sz = mrecv(obj->s, buf, len, deadline); if(dsock_slow(sz < 0)) return -1; fprintf(stderr, "recv %8zuB: 0x", len); size_t i; for(i = 0; i != sz && i != len; ++i) fprintf(stderr, "%02x", (int)((uint8_t*)buf)[i]); fprintf(stderr, "\n"); return sz; }
int nagle_stop(int s, int64_t deadline) { struct nagle_sock *obj = hquery(s, nagle_type); if(dsock_slow(!obj)) return -1; /* TODO: Flush the data from the buffer! */ int rc = hclose(obj->sender); dsock_assert(rc == 0); rc = hclose(obj->ackch); dsock_assert(rc == 0); rc = hclose(obj->sendch); dsock_assert(rc == 0); free(obj->buf); int u = obj->s; free(obj); return u; }
static ssize_t lz4_mrecvv(struct msock_vfs *mvfs, const struct iovec *iov, size_t iovlen, int64_t deadline) { struct lz4_sock *obj = dsock_cont(mvfs, struct lz4_sock, mvfs); /* Adjust the buffer size as needed. */ size_t len = iov_size(iov, iovlen); size_t maxlen = LZ4F_compressFrameBound(len, NULL); if(obj->inlen < maxlen) { uint8_t *newbuf = realloc(obj->inbuf, maxlen); if(dsock_slow(!newbuf)) {errno = ENOMEM; return -1;} obj->inbuf = newbuf; obj->inlen = maxlen; } /* Get the compressed message. */ ssize_t sz = mrecv(obj->s, obj->inbuf, obj->inlen, deadline); if(dsock_slow(sz < 0)) return -1; /* Extract size of the uncompressed message from LZ4 frame header. */ LZ4F_frameInfo_t info; size_t infolen = sz; size_t ec = LZ4F_getFrameInfo(obj->dctx, &info, obj->inbuf, &infolen); if(dsock_slow(LZ4F_isError(ec))) {errno = EPROTO; return -1;} /* Size is a required field. */ if(dsock_slow(info.contentSize == 0)) {errno = EPROTO; return -1;} /* Decompressed message would exceed the buffer size. */ if(dsock_slow(info.contentSize > len)) {errno = EMSGSIZE; return -1;} /* Decompress. */ /* TODO: Avoid the extra allocations and copies. */ uint8_t *buf = malloc(len); if(dsock_slow(!buf)) {errno = ENOMEM; return -1;} size_t dstlen = len; size_t srclen = sz - infolen; ec = LZ4F_decompress(obj->dctx, buf, &dstlen, obj->inbuf + infolen, &srclen, NULL); if(dsock_slow(LZ4F_isError(ec))) {errno = EPROTO; return -1;} if(dsock_slow(ec != 0)) {errno = EPROTO; return -1;} dsock_assert(srclen == sz - infolen); iov_copyallto(iov, iovlen, buf); free(buf); return dstlen; }
int nagle_start(int s, size_t batch, int64_t interval) { int rc; int err; /* Check whether underlying socket is a bytestream. */ if(dsock_slow(!hquery(s, bsock_type))) {err = errno; goto error1;} /* Create the object. */ struct nagle_sock *obj = malloc(sizeof(struct nagle_sock)); if(dsock_slow(!obj)) {err = ENOMEM; goto error1;} obj->hvfs.query = nagle_hquery; obj->hvfs.close = nagle_hclose; obj->bvfs.bsendv = nagle_bsendv; obj->bvfs.brecvv = nagle_brecvv; obj->s = s; obj->buf = malloc(batch); if(dsock_slow(!obj->buf)) {errno = ENOMEM; goto error2;} obj->sendch = channel(sizeof(struct nagle_vec), 0); if(dsock_slow(obj->sendch < 0)) {err = errno; goto error3;} obj->ackch = channel(sizeof(int), 0); if(dsock_slow(obj->ackch < 0)) {err = errno; goto error4;} obj->sender = go(nagle_sender(s, batch, interval, obj->buf, obj->sendch, obj->ackch)); if(dsock_slow(obj->sender < 0)) {err = errno; goto error5;} /* Create the handle. */ int h = hcreate(&obj->hvfs); if(dsock_slow(h < 0)) {err = errno; goto error6;} return h; error6: rc = hclose(obj->sender); dsock_assert(rc == 0); error5: rc = hclose(obj->ackch); dsock_assert(rc == 0); error4: rc = hclose(obj->sendch); dsock_assert(rc == 0); error3: free(obj->buf); error2: free(obj); error1: errno = err; return -1; }
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); } }
int brecv(int s, void *buf, size_t len, int64_t deadline) { struct hvfptr *h = hdata(s, bsock_type); if(dsock_slow(!h)) return -1; struct bsockvfptrs *b = (struct bsockvfptrs*)h; return b->brecv(s, buf, len, deadline); }