bool Microservice::tx(connection *const c) { int fd = c->fd; _l1: auto it = c->out.head; auto index = c->out.head_data_index; if (!it->next) { // fast-path const auto size = it->size; int r = writev(fd, it->data + c->out.head_data_index, size - index); if (-1 == r) { if (EINTR == errno) { return true; } else if (EAGAIN == errno) { need_write_avail(c); return true; } else { return shutdown(c, __LINE__); } } if (r == it->remaining_bytes) { // fast-path of fast-path // all remaining data of this payload were written to the socket buffer successfully // we also don't need to check if we have flushed the whole payload again if (c->flags & (1u << unsigned(connection::Flags::close_on_flush))) { return shutdown(c, __LINE__); } put_outgoing_payload(it); did_flush_payload(c); c->out.reset(); if (did_flush(c)) { goto _l1; } stop_need_write_avail(c); try_make_inactive(c); return true; } // we know that the whole payload wasn't transferred so our // for() loop here is tighter; no need to check for payload transmission it->remaining_bytes -= r; // we won't need to check if we have written the whole thing into the socket buffer for (;;) { auto &span = it->data[index]; if (r >= span.iov_len) { ++index; r -= span.iov_len; } else { span.iov_base = static_cast(span.iov_base) + r; span.iov_len -= r; c->out.head_data_index = index; break; } } need_write_avail(c); return true; } struct iovec iov[128], *out; bool have_cork = false; for (;;) { // fill iov[] { const auto n = it->size - index; const auto end = iov + sizeof_array(iov); auto p = it->next; memcpy(iov, it->data + index, n * sizeof(iovec)); out = iov + n; while (p) { const auto n = std::min(std::distance(out, end), p->size); memcpy(out, p->data, n * sizeof(iovec)); out += n; if (out == end) { break; } p = p->next; } if (p && !have_cork) { have_cork = true; Switch::SetTCPCork(fd, 1); } } const auto size = std::distance(iov, out); int r = writev(fd, iov, size); if (-1 == r) { if (errno == EINTR) { if (have_cork) { Switch::SetTCPCork(fd, 0); } return true; } else if (errno == EAGAIN) { if (have_cork) { Switch::SetTCPCork(fd, 0); } need_write_avail(c); return true; } else { return shutdown(c, __LINE__); } } for (;;) { const auto bytes = it->remaining_bytes; if (r >= bytes) { auto next = it->next; put_outgoing_payload(it); did_flush_payload(c); if (next == nullptr) { if (have_cork) { Switch::SetTCPCork(fd, 0); } if (c->flags & (1u << unsigned(connection::Flags::close_on_flush))) { return shutdown(c, __LINE__); } c->out.reset(); if (did_flush(c)) { goto _l1; } stop_need_write_avail(c); try_make_inactive(c); return true; } r -= bytes; it = next; index = 0; continue; } auto &span = it->data[index]; it->remaining_bytes -= r; span.iov_base = static_cast(span.iov_base) + r; span.iov_len -= r; break; } c->out.head_data_index = index; c->out.head = it; } }