void PipeDescriptor::Write() { int sd = GetSocket(); assert (sd != INVALID_SOCKET); LastIo = gCurrentLoopTime; char output_buffer [16 * 1024]; size_t nbytes = 0; 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; } } // 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); int bytes_written = write (GetSocket(), output_buffer, nbytes); if (bytes_written > 0) { OutboundDataSize -= bytes_written; 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)); } #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); assert (MyEventMachine); MyEventMachine->Modify (this); #endif } else { #ifdef OS_UNIX if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) #endif #ifdef OS_WIN32 if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) #endif Close(); } }
void DatagramDescriptor::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. * This code is very reminiscent of ConnectionDescriptor::_WriteOutboundData, * but differs in the that the outbound data pages (received from the * user) are _message-structured._ That is, we send each of them out * one message at a time. * TODO, we are currently suppressing the EMSGSIZE error!!! */ int sd = GetSocket(); assert (sd != INVALID_SOCKET); LastIo = gCurrentLoopTime; assert (OutboundPages.size() > 0); // Send out up to 10 packets, then cycle the machine. for (int i = 0; i < 10; i++) { if (OutboundPages.size() <= 0) break; OutboundPage *op = &(OutboundPages[0]); // The nasty cast to (char*) is needed because Windows is brain-dead. int s = sendto (sd, (char*)op->Buffer, op->Length, 0, (struct sockaddr*)&(op->From), sizeof(op->From)); int e = errno; OutboundDataSize -= op->Length; op->Free(); OutboundPages.pop_front(); if (s == SOCKET_ERROR) { #ifdef OS_UNIX if ((e != EINPROGRESS) && (e != EWOULDBLOCK) && (e != EINTR)) { #endif #ifdef OS_WIN32 if ((e != WSAEINPROGRESS) && (e != WSAEWOULDBLOCK)) { #endif Close(); break; } } } #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); assert (MyEventMachine); MyEventMachine->Modify (this); #endif } /********************************** DatagramDescriptor::SelectForWrite **********************************/ bool DatagramDescriptor::SelectForWrite() { /* Changed 15Nov07, per bug report by Mark Zvillius. * The outbound data size will be zero if there are zero-length outbound packets, * so we now select writable in case the outbound page buffer is not empty. * Note that the superclass ShouldDelete method still checks for outbound data size, * which may be wrong. */ //return (GetOutboundDataSize() > 0); (Original) return (OutboundPages.size() > 0); } /************************************ DatagramDescriptor::SendOutboundData ************************************/ int DatagramDescriptor::SendOutboundData (const char *data, int length) { // This is an exact clone of ConnectionDescriptor::SendOutboundData. // That means it needs to move to a common ancestor. 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, ReturnAddress)); OutboundDataSize += length; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); assert (MyEventMachine); MyEventMachine->Modify (this); #endif return length; } /**************************************** DatagramDescriptor::SendOutboundDatagram ****************************************/ int DatagramDescriptor::SendOutboundDatagram (const char *data, int length, const char *address, int port) { // This is an exact clone of ConnectionDescriptor::SendOutboundData. // That means it needs to move to a common ancestor. // TODO: Refactor this so there's no overlap with SendOutboundData. if (IsCloseScheduled()) //if (bCloseNow || bCloseAfterWriting) return 0; if (!address || !*address || !port) return 0; sockaddr_in pin; unsigned long HostAddr; HostAddr = inet_addr (address); if (HostAddr == INADDR_NONE) { // The nasty cast to (char*) is because Windows is brain-dead. hostent *hp = gethostbyname ((char*)address); if (!hp) return 0; HostAddr = ((in_addr*)(hp->h_addr))->s_addr; } memset (&pin, 0, sizeof(pin)); pin.sin_family = AF_INET; pin.sin_addr.s_addr = HostAddr; pin.sin_port = htons (port); 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, pin)); OutboundDataSize += length; #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | EPOLLOUT); assert (MyEventMachine); MyEventMachine->Modify (this); #endif return length; } /**************************************** STATIC: DatagramDescriptor::SendDatagram ****************************************/ int DatagramDescriptor::SendDatagram (const char *binding, const char *data, int length, const char *address, int port) { DatagramDescriptor *dd = dynamic_cast <DatagramDescriptor*> (Bindable_t::GetObject (binding)); if (dd) return dd->SendOutboundDatagram (data, length, address, port); else return -1; }
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; } LastIo = gCurrentLoopTime; char output_buffer [16 * 1024]; size_t nbytes = 0; 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; } } // 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); int bytes_written = send (GetSocket(), output_buffer, nbytes, 0); if (bytes_written > 0) { OutboundDataSize -= bytes_written; 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)); } #ifdef HAVE_EPOLL EpollEvent.events = (EPOLLIN | (SelectForWrite() ? EPOLLOUT : 0)); assert (MyEventMachine); MyEventMachine->Modify (this); #endif #ifdef HAVE_KQUEUE if (SelectForWrite()) MyEventMachine->ArmKqueueWriter (this); #endif } else { #ifdef OS_UNIX if ((errno != EINPROGRESS) && (errno != EWOULDBLOCK) && (errno != EINTR)) #endif #ifdef OS_WIN32 if ((errno != WSAEINPROGRESS) && (errno != WSAEWOULDBLOCK)) #endif Close(); } }
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(); } }