/* resume from pause, start waiting for data */ void sbuf_continue(SBuf *sbuf) { bool do_recv = DO_RECV; bool res; AssertActive(sbuf); res = sbuf_wait_for_data(sbuf); if (!res) { /* drop if problems */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return; } /* * It's tempting to try to avoid the recv() but that would * only work if no code wants to see full packet. * * This is not true in ServerParameter case. */ /* * if (sbuf->recv_pos - sbuf->pkt_pos >= SBUF_SMALL_PKT) * do_recv = false; */ sbuf_main_loop(sbuf, do_recv); }
/* socket is full, wait until it's writable again */ static bool sbuf_queue_send(SBuf *sbuf) { int err; AssertActive(sbuf); Assert(sbuf->wait_type == W_RECV); /* if false is returned, the socket will be closed later */ /* stop waiting for read events */ err = event_del(&sbuf->ev); sbuf->wait_type = W_NONE; /* make sure its called only once */ if (err < 0) { log_warning("sbuf_queue_send: event_del failed: %s", strerror(errno)); return false; } /* instead wait for EV_WRITE on destination socket */ event_set(&sbuf->ev, sbuf->dst->sock, EV_WRITE, sbuf_send_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_queue_send: event_add failed: %s", strerror(errno)); return false; } sbuf->wait_type = W_SEND; return true; }
/* proto_fn tells to skip some amount of bytes */ void sbuf_prepare_skip(SBuf *sbuf, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || iobuf_send_pending_avail(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_SKIP; sbuf->pkt_remain = amount; }
/* proto_fn tells to skip some amount of bytes */ void sbuf_prepare_fetch(SBuf *sbuf, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || iobuf_send_pending_avail(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_CALL; sbuf->pkt_remain = amount; /* sbuf->dst = NULL; // FIXME ?? */ }
/* proto_fn tells to send some bytes to socket */ void sbuf_prepare_send(SBuf *sbuf, SBuf *dst, unsigned amount) { AssertActive(sbuf); Assert(sbuf->pkt_remain == 0); /* Assert(sbuf->pkt_action == ACT_UNSET || sbuf->pkt_action == ACT_SEND || iobuf_amount_pending(&sbuf->io)); */ Assert(amount > 0); sbuf->pkt_action = ACT_SEND; sbuf->pkt_remain = amount; sbuf->dst = dst; }
/* * There's data in buffer to be sent. Returns bool if processing can continue. * * Does not look at pkt_pos/remain fields, expects them to be merged to send_* */ static bool sbuf_send_pending(SBuf *sbuf) { int res, avail; IOBuf *io = sbuf->io; AssertActive(sbuf); Assert(sbuf->dst || iobuf_amount_pending(io) == 0); try_more: /* how much data is available for sending */ avail = iobuf_amount_pending(io); if (avail == 0) return true; if (sbuf->dst->sock == 0) { log_error("sbuf_send_pending: no dst sock?"); return false; } /* actually send it */ res = iobuf_send_pending(io, sbuf->dst->sock); if (res < 0) { if (errno == EAGAIN) { if (!sbuf_queue_send(sbuf)) /* drop if queue failed */ sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } else { sbuf_call_proto(sbuf, SBUF_EV_SEND_FAILED); } return false; } AssertActive(sbuf); /* * Should do sbuf_queue_send() immediately? * * To be sure, let's run into EAGAIN. */ goto try_more; }
/* don't wait for data on this socket */ bool sbuf_pause(SBuf *sbuf) { AssertActive(sbuf); Assert(sbuf->wait_type == W_RECV); if (event_del(&sbuf->ev) < 0) { log_warning("event_del: %s", strerror(errno)); return false; } sbuf->wait_type = W_NONE; return true; }
/* * Resume from pause and give socket over to external * callback function. * * The callback will be called with arg given to sbuf_init. */ bool sbuf_continue_with_callback(SBuf *sbuf, sbuf_libevent_cb user_cb) { int err; AssertActive(sbuf); event_set(&sbuf->ev, sbuf->sock, EV_READ | EV_PERSIST, user_cb, sbuf); err = event_add(&sbuf->ev, NULL); if (err < 0) { log_warning("sbuf_continue_with_callback: %s", strerror(errno)); return false; } sbuf->wait_type = W_RECV; return true; }
/* reposition at buffer start again */ static void sbuf_try_resync(SBuf *sbuf, bool release) { IOBuf *io = sbuf->io; if (io) { log_noise("resync: done=%d, parse=%d, recv=%d", io->done_pos, io->parse_pos, io->recv_pos); } AssertActive(sbuf); if (!io) return; if (release && iobuf_empty(io)) { slab_free(iobuf_cache, io); sbuf->io = NULL; } else { iobuf_try_resync(io, SBUF_SMALL_PKT); } }
/* actually ask kernel for more data */ static bool sbuf_actual_recv(SBuf *sbuf, unsigned len) { int got; IOBuf *io = sbuf->io; AssertActive(sbuf); Assert(len > 0); Assert(iobuf_amount_recv(io) >= len); got = iobuf_recv_limit(io, sbuf->sock, len); if (got == 0) { /* eof from socket */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } else if (got < 0 && errno != EAGAIN) { /* some error occurred */ sbuf_call_proto(sbuf, SBUF_EV_RECV_FAILED); return false; } return true; }
typename ValueAt<I>::type const &&get() const && { static_assert(I < sizeof...(Elems), "Value of I is too large."); AssertActive(I); return ValueAt<I>::get(static_cast<StoreT const &&>(Store)); }
/* process as much data as possible */ static bool sbuf_process_pending(SBuf *sbuf) { unsigned avail; IOBuf *io = sbuf->io; bool full = iobuf_amount_recv(io) <= 0; bool res; while (1) { AssertActive(sbuf); /* * Enough for now? * * The (avail <= SBUF_SMALL_PKT) check is to avoid partial pkts. * As SBuf should not assume knowledge about packets, * the check is not done in !full case. Packet handler can * then still notify about partial packet by returning false. */ avail = iobuf_amount_parse(io); if (avail == 0 || (full && avail <= SBUF_SMALL_PKT)) break; /* * If start of packet, process packet header. */ if (sbuf->pkt_remain == 0) { res = sbuf_call_proto(sbuf, SBUF_EV_READ); if (!res) return false; Assert(sbuf->pkt_remain > 0); } if (sbuf->pkt_action == ACT_SKIP || sbuf->pkt_action == ACT_CALL) { /* send any pending data before skipping */ if (iobuf_amount_pending(io) > 0) { res = sbuf_send_pending(sbuf); if (!res) return res; } } if (avail > sbuf->pkt_remain) avail = sbuf->pkt_remain; switch (sbuf->pkt_action) { case ACT_SEND: iobuf_tag_send(io, avail); break; case ACT_CALL: res = sbuf_call_proto(sbuf, SBUF_EV_PKT_CALLBACK); if (!res) return false; /* after callback, skip pkt */ case ACT_SKIP: iobuf_tag_skip(io, avail); break; } sbuf->pkt_remain -= avail; } return sbuf_send_pending(sbuf); }