int ConnectionDescriptor::_SendRawOutboundData (const char *data, int length) { /* This internal method is called to schedule bytes that * will be sent out to the remote peer. * It's not directly accessed by the caller, who hits ::SendOutboundData, * which may or may not filter or encrypt the caller's data before * sending it here. */ // Highly naive and incomplete implementation. // There's no throttle for runaways (which should abort only this connection // and not the whole process), and no coalescing of small pages. // (Well, not so bad, small pages are coalesced in ::Write) if (IsCloseScheduled()) //if (bCloseNow || bCloseAfterWriting) return 0; if (!data && (length > 0)) throw std::runtime_error ("bad outbound data"); char *buffer = (char *) malloc (length + 1); if (!buffer) throw std::runtime_error ("no allocation for outbound data"); memcpy (buffer, data, length); buffer [length] = 0; OutboundPages.push_back (OutboundPage (buffer, length)); OutboundDataSize += length; _UpdateEvents(false, true); return length; }
void ConnectionDescriptor::SetNotifyWritable(bool writable) { if (!bWatchOnly) throw std::runtime_error ("notify_writable must be on 'watch only' connections"); bNotifyWritable = writable; _UpdateEvents(false, true); }
void ConnectionDescriptor::SetNotifyReadable(bool readable) { if (!bWatchOnly) throw std::runtime_error ("notify_readable must be on 'watch only' connections"); bNotifyReadable = readable; _UpdateEvents(true, false); }
bool ConnectionDescriptor::Resume() { if (bWatchOnly) throw std::runtime_error ("cannot pause/resume 'watch only' connections, set notify readable/writable instead"); bool old = bPaused; bPaused = false; _UpdateEvents(); return old == true; }
void ConnectionDescriptor::_WriteOutboundData() { /* This is a helper function called by ::Write. * It's possible for a socket to select writable and then no longer * be writable by the time we get around to writing. The kernel might * have used up its available output buffers between the select call * and when we get here. So this condition is not an error. * * 20Jul07, added the same kind of protection against an invalid socket * that is at the top of ::Read. Not entirely how this could happen in * real life (connection-reset from the remote peer, perhaps?), but I'm * doing it to address some reports of crashing under heavy loads. */ int sd = GetSocket(); //assert (sd != INVALID_SOCKET); if (sd == INVALID_SOCKET) { assert (!bWriteAttemptedAfterClose); bWriteAttemptedAfterClose = true; return; } LastActivity = MyEventMachine->GetCurrentLoopTime(); size_t nbytes = 0; #ifdef HAVE_WRITEV int iovcnt = OutboundPages.size(); // Max of 16 outbound pages at a time if (iovcnt > 16) iovcnt = 16; #ifdef CC_SUNWspro struct iovec iov[16]; #else struct iovec iov[ iovcnt ]; #endif for(int i = 0; i < iovcnt; i++){ OutboundPage *op = &(OutboundPages[i]); #ifdef CC_SUNWspro iov[i].iov_base = (char *)(op->Buffer + op->Offset); #else iov[i].iov_base = (void *)(op->Buffer + op->Offset); #endif iov[i].iov_len = op->Length - op->Offset; nbytes += iov[i].iov_len; } #else char output_buffer [16 * 1024]; while ((OutboundPages.size() > 0) && (nbytes < sizeof(output_buffer))) { OutboundPage *op = &(OutboundPages[0]); if ((nbytes + op->Length - op->Offset) < sizeof (output_buffer)) { memcpy (output_buffer + nbytes, op->Buffer + op->Offset, op->Length - op->Offset); nbytes += (op->Length - op->Offset); op->Free(); OutboundPages.pop_front(); } else { int len = sizeof(output_buffer) - nbytes; memcpy (output_buffer + nbytes, op->Buffer + op->Offset, len); op->Offset += len; nbytes += len; } } #endif // We should never have gotten here if there were no data to write, // so assert that as a sanity check. // Don't bother to make sure nbytes is less than output_buffer because // if it were we probably would have crashed already. assert (nbytes > 0); assert (GetSocket() != INVALID_SOCKET); #ifdef HAVE_WRITEV int bytes_written = writev (GetSocket(), iov, iovcnt); #else int bytes_written = write (GetSocket(), output_buffer, nbytes); #endif bool err = false; if (bytes_written < 0) { err = true; bytes_written = 0; } assert (bytes_written >= 0); OutboundDataSize -= bytes_written; if (ProxiedFrom && MaxOutboundBufSize && (unsigned int)GetOutboundDataSize() < MaxOutboundBufSize && ProxiedFrom->IsPaused()) ProxiedFrom->Resume(); #ifdef HAVE_WRITEV if (!err) { unsigned int sent = bytes_written; deque<OutboundPage>::iterator op = OutboundPages.begin(); for (int i = 0; i < iovcnt; i++) { if (iov[i].iov_len <= sent) { // Sent this page in full, free it. op->Free(); OutboundPages.pop_front(); sent -= iov[i].iov_len; } else { // Sent part (or none) of this page, increment offset to send the remainder op->Offset += sent; break; } // Shouldn't be possible run out of pages before the loop ends assert(op != OutboundPages.end()); *op++; } } #else if ((size_t)bytes_written < nbytes) { int len = nbytes - bytes_written; char *buffer = (char*) malloc (len + 1); if (!buffer) throw std::runtime_error ("bad alloc throwing back data"); memcpy (buffer, output_buffer + bytes_written, len); buffer [len] = 0; OutboundPages.push_front (OutboundPage (buffer, len)); } #endif _UpdateEvents(false, true); if (err) { #ifdef OS_UNIX if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) #endif #ifdef OS_WIN32 if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) #endif Close(); } }
void ConnectionDescriptor::Write() { /* A socket which is in a pending-connect state will select * writable when the disposition of the connect is known. * At that point, check to be sure there are no errors, * and if none, then promote the socket out of the pending * state. * TODO: I haven't figured out how Windows signals errors on * unconnected sockets. Maybe it does the untraditional but * logical thing and makes the socket selectable for error. * If so, it's unsupported here for the time being, and connect * errors will have to be caught by the timeout mechanism. */ if (bConnectPending) { int error; socklen_t len; len = sizeof(error); #ifdef OS_UNIX int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, &error, &len); #endif #ifdef OS_WIN32 int o = getsockopt (GetSocket(), SOL_SOCKET, SO_ERROR, (char*)&error, &len); #endif if ((o == 0) && (error == 0)) { if (EventCallback) (*EventCallback)(GetBinding(), EM_CONNECTION_COMPLETED, "", 0); // 5May09: Moved epoll/kqueue read/write arming into SetConnectPending, so it can be called // from EventMachine_t::AttachFD as well. SetConnectPending (false); } else ScheduleClose (false); //bCloseNow = true; } else { if (bNotifyWritable) { if (EventCallback) (*EventCallback)(GetBinding(), EM_CONNECTION_NOTIFY_WRITABLE, NULL, 0); _UpdateEvents(false, true); return; } assert(!bWatchOnly); /* 5May09: Kqueue bugs on OSX cause one extra writable event to fire even though we're using EV_ONESHOT. We ignore this extra event once, but only the first time. If it happens again, we should fall through to the assert(nbytes>0) failure to catch any EM bugs which might cause ::Write to be called in a busy-loop. */ #ifdef HAVE_KQUEUE if (MyEventMachine->UsingKqueue()) { if (OutboundDataSize == 0 && !bGotExtraKqueueEvent) { bGotExtraKqueueEvent = true; return; } else if (OutboundDataSize > 0) { bGotExtraKqueueEvent = false; } } #endif _WriteOutboundData(); } }
void ConnectionDescriptor::SetWatchOnly(bool watching) { bWatchOnly = watching; _UpdateEvents(); }
void ConnectionDescriptor::SetConnectPending(bool f) { bConnectPending = f; _UpdateEvents(); }
void ConnectionDescriptor::_UpdateEvents() { _UpdateEvents(true, true); }