Skip to content

Instantly share code, notes, and snippets.

@markpapadakis
Created January 25, 2020 11:00
Show Gist options
  • Save markpapadakis/2559599cdc4a6aa4160f6560ad5b0577 to your computer and use it in GitHub Desktop.
Save markpapadakis/2559599cdc4a6aa4160f6560ad5b0577 to your computer and use it in GitHub Desktop.
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<char *>(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<size_t>(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<char *>(span.iov_base) + r;
span.iov_len -= r;
break;
}
c->out.head_data_index = index;
c->out.head = it;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment