in folly/io/async/AsyncSocket.cpp [3008:3134]
void AsyncSocket::handleWrite() noexcept {
VLOG(5) << "AsyncSocket::handleWrite() this=" << this << ", fd=" << fd_
<< ", state=" << state_;
DestructorGuard dg(this);
if (state_ == StateEnum::CONNECTING) {
handleConnect();
return;
}
// Normal write
assert(state_ == StateEnum::ESTABLISHED);
assert((shutdownFlags_ & SHUT_WRITE) == 0);
assert(writeReqHead_ != nullptr);
// Loop until we run out of write requests,
// or until this socket is moved to another EventBase.
// (See the comment in handleRead() explaining how this can happen.)
EventBase* originalEventBase = eventBase_;
while (writeReqHead_ != nullptr && eventBase_ == originalEventBase) {
auto writeResult = writeReqHead_->performWrite();
if (writeResult.writeReturn < 0) {
if (writeResult.exception) {
return failWrite(__func__, *writeResult.exception);
}
auto errnoCopy = errno;
AsyncSocketException ex(
AsyncSocketException::INTERNAL_ERROR,
withAddr("writev() failed"),
errnoCopy);
return failWrite(__func__, ex);
} else if (writeReqHead_->isComplete()) {
// We finished this request
WriteRequest* req = writeReqHead_;
writeReqHead_ = req->getNext();
if (writeReqHead_ == nullptr) {
writeReqTail_ = nullptr;
// This is the last write request.
// Unregister for write events and cancel the send timer
// before we invoke the callback. We have to update the state
// properly before calling the callback, since it may want to detach
// us from the EventBase.
if (eventFlags_ & EventHandler::WRITE) {
if (!updateEventRegistration(0, EventHandler::WRITE)) {
assert(state_ == StateEnum::ERROR);
return;
}
// Stop the send timeout
writeTimeout_.cancelTimeout();
}
assert(!writeTimeout_.isScheduled());
// If SHUT_WRITE_PENDING is set, we should shutdown the socket after
// we finish sending the last write request.
//
// We have to do this before invoking writeSuccess(), since
// writeSuccess() may detach us from our EventBase.
if (shutdownFlags_ & SHUT_WRITE_PENDING) {
assert(connectCallback_ == nullptr);
shutdownFlags_ |= SHUT_WRITE;
if (shutdownFlags_ & SHUT_READ) {
// Reads have already been shutdown. Fully close the socket and
// move to STATE_CLOSED.
//
// Note: This code currently moves us to STATE_CLOSED even if
// close() hasn't ever been called. This can occur if we have
// received EOF from the peer and shutdownWrite() has been called
// locally. Should we bother staying in STATE_ESTABLISHED in this
// case, until close() is actually called? I can't think of a
// reason why we would need to do so. No other operations besides
// calling close() or destroying the socket can be performed at
// this point.
assert(readCallback_ == nullptr);
state_ = StateEnum::CLOSED;
if (fd_ != NetworkSocket()) {
ioHandler_.changeHandlerFD(NetworkSocket());
doClose();
}
} else {
// Reads are still enabled, so we are only doing a half-shutdown
netops_->shutdown(fd_, SHUT_WR);
}
}
}
// Invoke the callback
WriteCallback* callback = req->getCallback();
req->destroy();
if (callback) {
callback->writeSuccess();
}
// We'll continue around the loop, trying to write another request
} else {
// Partial write.
writeReqHead_->consume();
if (bufferCallback_) {
bufferCallback_->onEgressBuffered();
}
// Stop after a partial write; it's highly likely that a subsequent
// write attempt will just return EAGAIN.
//
// Ensure that we are registered for write events.
if ((eventFlags_ & EventHandler::WRITE) == 0) {
if (!updateEventRegistration(EventHandler::WRITE, 0)) {
assert(state_ == StateEnum::ERROR);
return;
}
}
// Reschedule the send timeout, since we have made some write progress.
if (sendTimeout_ > 0) {
if (!writeTimeout_.scheduleTimeout(sendTimeout_)) {
AsyncSocketException ex(
AsyncSocketException::INTERNAL_ERROR,
withAddr("failed to reschedule write timeout"));
return failWrite(__func__, ex);
}
}
return;
}
}
if (!writeReqHead_ && bufferCallback_) {
bufferCallback_->onEgressBufferCleared();
}
}