Skip to content

Instantly share code, notes, and snippets.

@markpapadakis
Last active August 29, 2015 14:19
Show Gist options
  • Select an option

  • Save markpapadakis/e18e235909f72ff8e912 to your computer and use it in GitHub Desktop.

Select an option

Save markpapadakis/e18e235909f72ff8e912 to your computer and use it in GitHub Desktop.
bool TrySend(connection *const c)
{
iovec iov[128];
int fd = c->fd;
sendv:
auto it = c->respTail;
int32_t r;
bool haveCork;
// If we have multiple requests(therefore multiple writev() calls are likely required)
// and/or have say data to stream using sendfile(), then we need a cork
// We don't use sendfile(), but that's how it'd work. Also, if we did need sendfile()
// c could have been == nullptr.
if (!it->next) // Fast path: single request(no pipelining)
{
r = writev(fd, it->iov + it->iovCur, it->iovCnt - it->iovCur);
haveCork = false;
}
else
{
// Need to reduce writev() calls to absolute minimum
uint8_t i{0};
auto cur= it->iovCur;
if (!SwitchNetwork::SetTCPCork(fd, 1))
haveCork = true;
do
{
if (cur >= 127)
{
// Patch, starting from it->iovCnt - 127. Reset it afterwards
const auto cnt = it->iovCnt;
for (decltype(it->iovCnt) i = it->iovCur - 127; i != cnt; ++i)
{
auto &v = it->iov[i];
if (v.iov_len >= (1U<<30))
{
v.iov_len -= 1U<<30;
v.iov_base = it->content->At((uint32_t)(uintptr_t)v.iov_base);
}
}
cur = it->iovCur = 0;
}
const auto n = Min<uint8_t>(sizeof_array(iov) - i, it->iovCnt - cur);
(void)memcpy(iov + i, it->iov + it->iovCur, n * sizeof(iovec));
i+=n;
cur = 0;
} while (i != sizeof_array(iov) && (it = it->next));
// Timings:
// 5%: 4
// 10%: 5
// 25%: 5
// 50%: 7
// 75%: 8
// 90%: 13
// 95%: 19
// 99%: 31
r = writev(c->fd, iov, i);
}
if (unlikely(r == -1))
{
const auto err = errno;
if (unlikely(haveCork))
SwitchNetwork::SetTCPCork(fd, 0);
if (err == EAGAIN)
{
if (!c->needIoAvail)
{
c->needIoAvail = true;
(void)poller.SetDataAndEvents(c->fd, c, POLLIN|POLLOUT);
}
}
else if (err != EINTR)
{
Shutdown(c, __LINE__);
return false;
}
return true;
}
for (it = c->respTail; r >= it->iov[it->iovCur].iov_len; )
{
r-=it->iov[it->iovCur].iov_len;
if (++(it->iovCur) == it->iovCnt)
{
auto *const n = it->next;
PutResponse(it);
it = c->respTail = n;
if (!it)
{
if (unlikely(haveCork))
SwitchNetwork::SetTCPCork(fd, 0);
if (!c->keepAlive)
{
Shutdown(c, __LINE__);
return false;
}
else
{
c->respHead = nullptr;
if (c->needIoAvail)
{
(void)poller.SetDataAndEvents(c->fd, c, POLLIN);
c->needIoAvail = false;
}
return true;
}
}
}
}
auto &v = it->iov[it->iovCur];
v.iov_len-=r;
v.iov_base = (char *)v.iov_base + r;
// We didn't get to send all the content out - because we are limited in terms of sizeof_array(iov)
// so we need another short at it
// Ideally, we 'd have a large enough iov[] anyway.
//
// However, this could have been because there is no more space left in the socket send buf
// so it only accepted as much as it could. In this case, another call to writev() will
// return EAGAIN.
//
// We could probably better deal with this (enter needIoAvail mode if not all scheduled
// content was dispatched, instead of retrying).
goto sendv;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment