// Atomically send data, then close the socket. // @p Data to be sent. // @len Length of data starting from `p`. // // This function sends a chunk of data atomically. The socket // will be closed after data are sent. The reactor thread should // not call this, or it may be blocked forever. // // @return `true` if this socket is valid, `false` otherwise. bool send_close(const char* p, std::size_t len) { lock_guard g(m_lock); if( ! _send(p, len, g) ) return false; m_shutdown = true; if( empty() ) { _flush(); g.unlock(); invalidate_and_close(); return true; } g.unlock(); m_cond_write.notify_all(); return true; }
// Atomically send multiple data, then close the socket. // @iov Array of <iovec>. // @iovcnt Number of elements in `iov`. // // This function sends a chunk of data atomically. The socket // will be closed after data are sent. The reactor thread should // not call this, or it may be blocked forever. // // @return `true` if this socket is valid, `false` otherwise. bool sendv_close(const iovec* iov, int iovcnt) { if( iovcnt >= MAX_IOVCNT ) throw std::logic_error("<tcp_socket::sendv> too many iov."); lock_guard g(m_lock); if( ! _sendv(iov, iovcnt, g) ) return false; m_shutdown = true; if( empty() ) { _flush(); g.unlock(); invalidate_and_close(); return true; } g.unlock(); m_cond_write.notify_all(); return true; }
bool tcp_socket::_sendv(const iovec* iov, const int iovcnt, lock_guard& g) { std::size_t total = 0; for( int i = 0; i < iovcnt; ++i ) { total += iov[i].len; } while( ! can_send(total) ) m_cond_write.wait(g); if( m_shutdown ) return false; ::iovec v[MAX_IOVCNT]; for( int i = 0; i < iovcnt; ++i ) { v[i].iov_base = (void*)(iov[i].p); v[i].iov_len = iov[i].len; } int ind = 0; if( m_pending.empty() ) { while( ind < iovcnt ) { ssize_t n = ::writev(m_fd, &(v[ind]), iovcnt - ind); if( n == -1 ) { if( errno == EAGAIN || errno == EWOULDBLOCK ) break; if( errno == EINTR ) continue; auto ecnd = std::system_category().default_error_condition(errno); if( ecnd.value() != EPIPE ) logger::error() << "<tcp_socket::_sendv>: (" << ecnd.value() << ") " << ecnd.message(); g.unlock(); invalidate_and_close(); return false; } while( n > 0 ) { if( static_cast<std::size_t>(n) < v[ind].iov_len ) { v[ind].iov_base = ((char*)v[ind].iov_base) + n; v[ind].iov_len = v[ind].iov_len - n; break; } n -= v[ind].iov_len; ++ind; } } if( ind == iovcnt ) return true; } // recalculate total length total = 0; for( int i = ind; i < iovcnt; ++i ) { total += v[i].iov_len; } // put data in the pending request queue. if( capacity() < total ) { // here, m_pending.empty() and m_tmpbuf.empty() holds true. logger::debug() << "<tcp_socket::_sendv> buffering " << total << " bytes data."; m_tmpbuf.resize(total); char* p = m_tmpbuf.data(); for( int i = ind; i < iovcnt; ++i ) { std::memcpy(p, v[i].iov_base, v[i].iov_len); p += v[i].iov_len; } return true; } while( ind < iovcnt ) { char* t_p; std::size_t t_len; if(m_pending.empty() || std::get<1>(m_pending.back()) == SENDBUF_SIZE) { t_p = m_free_buffers.back(); t_len = 0; m_free_buffers.pop_back(); m_pending.emplace_back(t_p, t_len, 0); } else { std::tie(t_p, t_len, std::ignore) = m_pending.back(); } std::size_t room = SENDBUF_SIZE - t_len; while( room > 0 ) { std::size_t to_write = std::min(room, v[ind].iov_len); std::memcpy(t_p + t_len, v[ind].iov_base, to_write); room -= to_write; t_len += to_write; std::get<1>(m_pending.back()) = t_len; if( to_write == v[ind].iov_len ) { ++ind; if( ind == iovcnt ) break; continue; } v[ind].iov_base = ((char*)v[ind].iov_base) + to_write; v[ind].iov_len -= to_write; } } return true; }
bool tcp_socket::_send(const char* p, std::size_t len, lock_guard& g) { while( ! can_send(len) ) m_cond_write.wait(g); if( m_shutdown ) return false; if( m_pending.empty() ) { while( len > 0 ) { ssize_t n = ::send(m_fd, p, len, 0); if( n == -1 ) { if( errno == EAGAIN || errno == EWOULDBLOCK ) break; if( errno == EINTR ) continue; auto ecnd = std::system_category().default_error_condition(errno); if( ecnd.value() != EPIPE ) logger::error() << "<tcp_socket::_send>: (" << ecnd.value() << ") " << ecnd.message(); g.unlock(); invalidate_and_close(); return false; } p += n; len -= n; } if( len == 0 ) return true; } // put data in the pending request queue. if( capacity() < len ) { // here, m_pending.empty() and m_tmpbuf.empty() holds true. logger::debug() << "<tcp_socket::_send> buffering " << len << " bytes data."; m_tmpbuf.resize(len); std::memcpy(m_tmpbuf.data(), p, len); return true; } if( ! m_pending.empty() ) { auto& t = m_pending.back(); char* t_p; std::size_t t_len; std::tie(t_p, t_len, std::ignore) = t; std::size_t room = SENDBUF_SIZE - t_len; if( room > 0 ) { std::size_t to_write = std::min(room, len); std::memcpy(t_p + t_len, p, to_write); p += to_write; len -= to_write; std::get<1>(t) = t_len + to_write; if( len == 0 ) return true; } } while( len > 0 ) { char* t_p = m_free_buffers.back(); m_free_buffers.pop_back(); std::size_t to_write = std::min(len, SENDBUF_SIZE); std::memcpy(t_p, p, to_write); p += to_write; len -= to_write; m_pending.emplace_back(t_p, to_write, 0); } return true; }