Allow shutting down sockets for read, write or both
This replaces half-closing sockets with calls to shut down a socket endpoint for read, write, or both as a separate operation from closing. Shutting an endpoint down for read disables all further reads from that endpoint (past data already buffered) and all further writes into the peer endpoint. Shutting an endpoint down for writing disables all further writing into that endpoint and all reader from the peer endpoint (past data already buffered). Shutting a socket endpoint down no longer raises the PEER_CLOSED signal on the peer endpoint. Separate signals are raised when reading and writing are disabled and the PEER_CLOSED signal is only raised when the peer is actually closed. In mxio, this fully implements the posix shutdown(2) calls for local sockets created with the socketpair(2) call. Remote sockets support shutdown(SHUT_WR) using their existing mechanism. Once remote sockets are migrated to use an mx_socket directly, they can re-use this machinery to fully support partial shutdowns. This is a reland of2f2e0a0f8fwhich was reverted ina8fa45f268, with the following changes: mxio/remotesocket.c updated to use the new shutdown bits for reading and writing The READ_DISABLED signal is now only raised when reading is disabled in that direction on the socket and the socket is drained in that direction. New tests added for blocking recv() and send() and fixes in the mxio bindings to behave correctly for these cases. Change-Id: I455855d29a6a79d2e98ad47b37222965332b9b98
Esse commit está contido em:
@@ -14,10 +14,33 @@ only move data (not handles).
|
||||
Data is written into one end of a socket via *mx_socket_write* and
|
||||
read from the opposing end via *mx_socket_read*.
|
||||
|
||||
Upon creation, both ends of the socket are writable and readable. Via
|
||||
the **MX_SOCKET_HALF_CLOSE** option to *mx_socket_write*, one end of
|
||||
the socket can be closed for reading (and the opposing end for
|
||||
writing).
|
||||
Upon creation, both ends of the socket are writable and readable. Via the
|
||||
**MX_SOCKET_SHUTDOWN_READ** and **MX_SOCKET_SHUTDOWN_WRITE** options to
|
||||
*mx_socket_write*, one end of the socket can be closed for reading and/or
|
||||
writing.
|
||||
|
||||
## SIGNALS
|
||||
|
||||
The following signals may be set for a socket object.
|
||||
|
||||
**MX_SOCKET_READABLE** data is available to read from the socket
|
||||
|
||||
**MX_SOCKET_WRITABLE** data may be written to the socket
|
||||
|
||||
**MX_SOCKET_PEER_CLOSED** the other endpoint of this socket has
|
||||
been closed.
|
||||
|
||||
**MX_SOCKET_READ_DISABLED** reading (beyond already buffered data) is disabled
|
||||
permanently for this endpoint either because of passing
|
||||
**MX_SOCKET_SHUTDOWN_READ** to this endpoint or passing
|
||||
**MX_SOCKET_SHUTDOWN_WRITE** to the peer. Reads on a socket endpoint with this
|
||||
signal raised will succeed so long as there is data in the socket that was
|
||||
written before reading was disabled.
|
||||
|
||||
**MX_SOCKET_WRITE_DISABLED** writing is disabled permanently for this endpoing either
|
||||
because of passing **MX_SOCKET_SHUTDOWN_WRITE** to this endpoint or passing
|
||||
**MX_SOCKET_SHUTDOWN_READ** to the peer.
|
||||
|
||||
|
||||
## SYSCALLS
|
||||
|
||||
|
||||
@@ -49,9 +49,10 @@ or if *options* is nonzero.
|
||||
|
||||
**MX_ERR_SHOULD_WAIT** The socket contained no data to read.
|
||||
|
||||
**MX_ERR_PEER_CLOSED** The other side of the socket is closed, or this
|
||||
side of the socket has been previously closed via a write with the
|
||||
**MX_SOCKET_HALF_CLOSE** flag.
|
||||
**MX_ERR_PEER_CLOSED** The other side of the socket is closed and no data is
|
||||
readable.
|
||||
|
||||
**MX_ERR_BAD_STATE** Reading has been disabled for this socket endpoint.
|
||||
|
||||
**MX_ERR_NO_MEMORY** (Temporary) Failure due to lack of memory.
|
||||
|
||||
|
||||
@@ -20,10 +20,18 @@ mx_status_t mx_socket_write(mx_handle_t handle, uint32_t options,
|
||||
specified by *handle*. The pointer to *bytes* may be NULL if *size*
|
||||
is zero.
|
||||
|
||||
There is one value (besides 0) that may be passed to *options*. If
|
||||
**MX_SOCKET_HALF_CLOSE** is passed to options, and *size* is 0, then the
|
||||
socket endpoint at *handle* is closed. Further writes to the other
|
||||
endpoint of the socket will fail with **MX_ERR_BAD_STATE**.
|
||||
If *size* is zero, a bitwise combination of **MX_SOCKET_SHUTDOWN_READ** and
|
||||
**MX_SOCKET_SHUTDOWN_WRITE** can be passed to *options* to disable reading or
|
||||
writing from a socket endpoint.
|
||||
|
||||
If **MX_SOCKET_SHUTDOWN_READ** is passed to options, and *size* is 0, then reading is disabled for
|
||||
the socket endpoint at *handle*. All data buffered in the socket at the time of the call may be
|
||||
read, but further reads from this endpoint or writes to the other endpoint of the socket will fail
|
||||
with **MX_ERR_BAD_STATE**.
|
||||
|
||||
If **MX_SOCKET_SHUTDOWN_WRITE** is passed to options, and *size* is 0, then writing is disabled for
|
||||
the socket endpoint at *handle*. Further writes to this endpoint or reads from the other endpoint of
|
||||
the socket will fail with **MX_ERR_BAD_STATE**.
|
||||
|
||||
If a NULL *actual* is passed in, it will be ignored.
|
||||
|
||||
@@ -55,8 +63,7 @@ not 0, or *options* was not 0 or **MX_SOCKET_HALF_CLOSE**.
|
||||
the socket was created with **MX_SOCKET_DATAGRAM** and *buffer* is
|
||||
larger than the remaining space in the socket.
|
||||
|
||||
**MX_ERR_BAD_STATE** This side of the socket has been closed by a prior write
|
||||
to the other side with **MX_SOCKET_HALF_CLOSE**.
|
||||
**MX_ERR_BAD_STATE** Writing has been disabled for this socket endpoint.
|
||||
|
||||
**MX_ERR_PEER_CLOSED** The other side of the socket is closed.
|
||||
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
// Socket methods.
|
||||
mx_status_t Write(user_ptr<const void> src, size_t len, size_t* written);
|
||||
|
||||
status_t HalfClose();
|
||||
// Shut this endpoint of the socket down for reading, writing, or both.
|
||||
status_t Shutdown(uint32_t how);
|
||||
|
||||
mx_status_t Read(user_ptr<void> dst, size_t len, size_t* nread);
|
||||
|
||||
@@ -52,7 +53,7 @@ private:
|
||||
void Init(mxtl::RefPtr<SocketDispatcher> other);
|
||||
mx_status_t WriteSelf(user_ptr<const void> src, size_t len, size_t* nwritten);
|
||||
status_t UserSignalSelf(uint32_t clear_mask, uint32_t set_mask);
|
||||
status_t HalfCloseOther();
|
||||
status_t ShutdownOther(uint32_t how);
|
||||
|
||||
bool is_full() const TA_REQ(lock_) { return data_.is_full(); }
|
||||
bool is_empty() const TA_REQ(lock_) { return data_.is_empty(); }
|
||||
@@ -67,6 +68,5 @@ private:
|
||||
Mutex lock_;
|
||||
MBufChain data_ TA_GUARDED(lock_);
|
||||
mxtl::RefPtr<SocketDispatcher> other_ TA_GUARDED(lock_);
|
||||
// half_closed_[0] is this end and [1] is the other end.
|
||||
bool half_closed_[2] TA_GUARDED(lock_);
|
||||
bool read_disabled_ TA_GUARDED(lock_);
|
||||
};
|
||||
|
||||
@@ -59,7 +59,7 @@ SocketDispatcher::SocketDispatcher(uint32_t flags)
|
||||
: flags_(flags),
|
||||
peer_koid_(0u),
|
||||
state_tracker_(MX_SOCKET_WRITABLE),
|
||||
half_closed_{false, false} {
|
||||
read_disabled_(false) {
|
||||
}
|
||||
|
||||
SocketDispatcher::~SocketDispatcher() {
|
||||
@@ -122,29 +122,73 @@ status_t SocketDispatcher::UserSignalSelf(uint32_t clear_mask, uint32_t set_mask
|
||||
return MX_OK;
|
||||
}
|
||||
|
||||
status_t SocketDispatcher::HalfClose() {
|
||||
status_t SocketDispatcher::Shutdown(uint32_t how) {
|
||||
canary_.Assert();
|
||||
|
||||
LTRACE_ENTRY;
|
||||
|
||||
const bool shutdown_read = how & MX_SOCKET_SHUTDOWN_READ;
|
||||
const bool shutdown_write = how & MX_SOCKET_SHUTDOWN_WRITE;
|
||||
|
||||
mxtl::RefPtr<SocketDispatcher> other;
|
||||
{
|
||||
AutoLock lock(&lock_);
|
||||
if (half_closed_[0])
|
||||
mx_signals_t signals = state_tracker_.GetSignalsState();
|
||||
// If we're already shut down in the requested way, return immediately.
|
||||
const uint32_t want_signals =
|
||||
(shutdown_read ? MX_SOCKET_READ_DISABLED : 0) |
|
||||
(shutdown_write ? MX_SOCKET_WRITE_DISABLED : 0);
|
||||
const uint32_t have_signals = signals & (MX_SOCKET_READ_DISABLED | MX_SOCKET_WRITE_DISABLED);
|
||||
if (want_signals == have_signals) {
|
||||
return MX_OK;
|
||||
if (!other_)
|
||||
return MX_ERR_PEER_CLOSED;
|
||||
}
|
||||
other = other_;
|
||||
half_closed_[0] = true;
|
||||
state_tracker_.UpdateState(MX_SOCKET_WRITABLE, 0u);
|
||||
mx_signals_t clear_mask = 0u;
|
||||
mx_signals_t set_mask = 0u;
|
||||
if (shutdown_read) {
|
||||
read_disabled_ = true;
|
||||
if (is_empty())
|
||||
set_mask |= MX_SOCKET_READ_DISABLED;
|
||||
}
|
||||
if (shutdown_write) {
|
||||
clear_mask |= MX_SOCKET_WRITABLE;
|
||||
set_mask |= MX_SOCKET_WRITE_DISABLED;
|
||||
}
|
||||
state_tracker_.UpdateState(clear_mask, set_mask);
|
||||
}
|
||||
// Our peer already be closed - if so, we've already updated our own bits so we are done. If the
|
||||
// peer is done, we need to notify them of the state change.
|
||||
if (other) {
|
||||
return other->ShutdownOther(how);
|
||||
} else {
|
||||
return MX_OK;
|
||||
}
|
||||
return other->HalfCloseOther();
|
||||
}
|
||||
|
||||
status_t SocketDispatcher::HalfCloseOther() {
|
||||
status_t SocketDispatcher::ShutdownOther(uint32_t how) {
|
||||
canary_.Assert();
|
||||
|
||||
const bool shutdown_read = how & MX_SOCKET_SHUTDOWN_READ;
|
||||
const bool shutdown_write = how & MX_SOCKET_SHUTDOWN_WRITE;
|
||||
|
||||
AutoLock lock(&lock_);
|
||||
half_closed_[1] = true;
|
||||
state_tracker_.UpdateState(0u, MX_SOCKET_PEER_CLOSED);
|
||||
mx_signals_t clear_mask = 0u;
|
||||
mx_signals_t set_mask = 0u;
|
||||
if (shutdown_read) {
|
||||
// If the other end shut down reading, we can't write any more.
|
||||
clear_mask |= MX_SOCKET_WRITABLE;
|
||||
set_mask |= MX_SOCKET_WRITE_DISABLED;
|
||||
}
|
||||
if (shutdown_write) {
|
||||
// If the other end shut down writing, we can't read any more than already exists in the
|
||||
// buffer. If we're empty, set MX_SOCKET_READ_DISABLED now. If we aren't empty, Read() will
|
||||
// set this bit after reading the remaining data from the socket.
|
||||
read_disabled_ = true;
|
||||
if (is_empty())
|
||||
set_mask |= MX_SOCKET_READ_DISABLED;
|
||||
}
|
||||
|
||||
state_tracker_.UpdateState(clear_mask, set_mask);
|
||||
return MX_OK;
|
||||
}
|
||||
|
||||
@@ -152,12 +196,15 @@ mx_status_t SocketDispatcher::Write(user_ptr<const void> src, size_t len,
|
||||
size_t* nwritten) {
|
||||
canary_.Assert();
|
||||
|
||||
LTRACE_ENTRY;
|
||||
|
||||
mxtl::RefPtr<SocketDispatcher> other;
|
||||
{
|
||||
AutoLock lock(&lock_);
|
||||
if (!other_)
|
||||
return MX_ERR_PEER_CLOSED;
|
||||
if (half_closed_[0])
|
||||
mx_signals_t signals = state_tracker_.GetSignalsState();
|
||||
if (signals & MX_SOCKET_WRITE_DISABLED)
|
||||
return MX_ERR_BAD_STATE;
|
||||
other = other_;
|
||||
}
|
||||
@@ -209,6 +256,8 @@ mx_status_t SocketDispatcher::Read(user_ptr<void> dst, size_t len,
|
||||
size_t* nread) {
|
||||
canary_.Assert();
|
||||
|
||||
LTRACE_ENTRY;
|
||||
|
||||
AutoLock lock(&lock_);
|
||||
|
||||
// Just query for bytes outstanding.
|
||||
@@ -220,19 +269,28 @@ mx_status_t SocketDispatcher::Read(user_ptr<void> dst, size_t len,
|
||||
if (len != (size_t)((uint32_t)len))
|
||||
return MX_ERR_INVALID_ARGS;
|
||||
|
||||
bool closed = half_closed_[1] || !other_;
|
||||
|
||||
if (is_empty())
|
||||
return closed ? MX_ERR_PEER_CLOSED : MX_ERR_SHOULD_WAIT;
|
||||
if (is_empty()) {
|
||||
if (!other_)
|
||||
return MX_ERR_PEER_CLOSED;
|
||||
// If reading is disabled on our end and we're empty, we'll never become readable again.
|
||||
// Return a different error to let the caller know.
|
||||
if (read_disabled_)
|
||||
return MX_ERR_BAD_STATE;
|
||||
return MX_ERR_SHOULD_WAIT;
|
||||
}
|
||||
|
||||
bool was_full = is_full();
|
||||
|
||||
auto st = data_.ReadMBufs(dst, len, flags_ == MX_SOCKET_DATAGRAM);
|
||||
|
||||
if (is_empty())
|
||||
state_tracker_.UpdateState(MX_SOCKET_READABLE, 0u);
|
||||
if (is_empty()) {
|
||||
uint32_t set_mask = 0u;
|
||||
if (read_disabled_)
|
||||
set_mask |= MX_SOCKET_READ_DISABLED;
|
||||
state_tracker_.UpdateState(MX_SOCKET_READABLE, set_mask);
|
||||
}
|
||||
|
||||
if (!closed && was_full && (st > 0))
|
||||
if (other_ && was_full && (st > 0))
|
||||
other_->state_tracker_.UpdateState(0u, MX_SOCKET_WRITABLE);
|
||||
|
||||
*nread = static_cast<size_t>(st);
|
||||
|
||||
@@ -79,8 +79,7 @@ mx_status_t sys_socket_write(mx_handle_t handle, uint32_t options,
|
||||
if (status != MX_OK)
|
||||
return status;
|
||||
|
||||
switch (options) {
|
||||
case 0: {
|
||||
if (!options) {
|
||||
size_t nwritten;
|
||||
status = socket->Write(_buffer, size, &nwritten);
|
||||
|
||||
@@ -90,13 +89,15 @@ mx_status_t sys_socket_write(mx_handle_t handle, uint32_t options,
|
||||
|
||||
return status;
|
||||
}
|
||||
case MX_SOCKET_HALF_CLOSE:
|
||||
if (size == 0)
|
||||
return socket->HalfClose();
|
||||
// fall thru if size != 0.
|
||||
default:
|
||||
return MX_ERR_INVALID_ARGS;
|
||||
if (size == 0) {
|
||||
switch (options) {
|
||||
case MX_SOCKET_SHUTDOWN_WRITE:
|
||||
case MX_SOCKET_SHUTDOWN_READ:
|
||||
case MX_SOCKET_SHUTDOWN_READ | MX_SOCKET_SHUTDOWN_WRITE:
|
||||
return socket->Shutdown(options & MX_SOCKET_SHUTDOWN_MASK);
|
||||
}
|
||||
}
|
||||
return MX_ERR_INVALID_ARGS;
|
||||
}
|
||||
|
||||
mx_status_t sys_socket_read(mx_handle_t handle, uint32_t options,
|
||||
|
||||
@@ -119,6 +119,8 @@ typedef uint32_t mx_signals_t;
|
||||
#define MX_SOCKET_READABLE __MX_OBJECT_READABLE
|
||||
#define MX_SOCKET_WRITABLE __MX_OBJECT_WRITABLE
|
||||
#define MX_SOCKET_PEER_CLOSED __MX_OBJECT_PEER_CLOSED
|
||||
#define MX_SOCKET_READ_DISABLED __MX_OBJECT_SIGNAL_4
|
||||
#define MX_SOCKET_WRITE_DISABLED __MX_OBJECT_SIGNAL_5
|
||||
|
||||
// Port
|
||||
#define MX_PORT_READABLE __MX_OBJECT_READABLE
|
||||
@@ -252,7 +254,11 @@ typedef int64_t mx_rel_off_t;
|
||||
#define MX_CHANNEL_MAX_MSG_HANDLES 64u
|
||||
|
||||
// Socket options and limits.
|
||||
#define MX_SOCKET_HALF_CLOSE 1u
|
||||
// These options can be passed to mx_socket_write()
|
||||
#define MX_SOCKET_SHUTDOWN_WRITE 1u
|
||||
#define MX_SOCKET_SHUTDOWN_READ 2u
|
||||
#define MX_SOCKET_SHUTDOWN_MASK (MX_SOCKET_SHUTDOWN_WRITE | MX_SOCKET_SHUTDOWN_READ)
|
||||
// These can be passed to mx_socket_create()
|
||||
#define MX_SOCKET_STREAM 0u
|
||||
#define MX_SOCKET_DATAGRAM 1u
|
||||
|
||||
|
||||
+17
-13
@@ -27,20 +27,22 @@ ssize_t mx_pipe_read_internal(mx_handle_t h, void* data, size_t len, int nonbloc
|
||||
ssize_t r = mx_socket_read(h, 0, data, len, &len);
|
||||
if (r == MX_OK) {
|
||||
return (ssize_t) len;
|
||||
} else if (r == MX_ERR_PEER_CLOSED) {
|
||||
} else if (r == MX_ERR_PEER_CLOSED || r == MX_ERR_BAD_STATE) {
|
||||
return 0;
|
||||
}
|
||||
if (r == MX_ERR_SHOULD_WAIT && !nonblock) {
|
||||
mx_signals_t pending;
|
||||
r = mx_object_wait_one(h, MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED,
|
||||
MX_TIME_INFINITE, &pending);
|
||||
r = mx_object_wait_one(h,
|
||||
MX_SOCKET_READABLE | MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED,
|
||||
MX_TIME_INFINITE,
|
||||
&pending);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
if (pending & MX_SOCKET_READABLE) {
|
||||
continue;
|
||||
}
|
||||
if (pending & MX_SOCKET_PEER_CLOSED) {
|
||||
if (pending & (MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
return 0;
|
||||
}
|
||||
// impossible
|
||||
@@ -59,15 +61,17 @@ ssize_t mx_pipe_write_internal(mx_handle_t h, const void* data, size_t len, int
|
||||
}
|
||||
if (r == MX_ERR_SHOULD_WAIT && !nonblock) {
|
||||
mx_signals_t pending;
|
||||
r = mx_object_wait_one(h, MX_SOCKET_WRITABLE | MX_SOCKET_PEER_CLOSED,
|
||||
MX_TIME_INFINITE, &pending);
|
||||
r = mx_object_wait_one(h,
|
||||
MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED | MX_SOCKET_PEER_CLOSED,
|
||||
MX_TIME_INFINITE,
|
||||
&pending);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
if (pending & MX_SOCKET_WRITABLE) {
|
||||
continue;
|
||||
}
|
||||
if (pending & MX_SOCKET_PEER_CLOSED) {
|
||||
if (pending & (MX_SOCKET_WRITE_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
return MX_ERR_PEER_CLOSED;
|
||||
}
|
||||
// impossible
|
||||
@@ -131,26 +135,26 @@ void mx_pipe_wait_begin(mxio_t* io, uint32_t events, mx_handle_t* handle, mx_sig
|
||||
*handle = p->h;
|
||||
mx_signals_t signals = 0;
|
||||
if (events & POLLIN) {
|
||||
signals |= MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED;
|
||||
signals |= MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED | MX_SOCKET_READ_DISABLED;
|
||||
}
|
||||
if (events & POLLOUT) {
|
||||
signals |= MX_SOCKET_WRITABLE;
|
||||
signals |= MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED;
|
||||
}
|
||||
if (events & POLLRDHUP) {
|
||||
signals |= MX_SOCKET_PEER_CLOSED;
|
||||
signals |= MX_SOCKET_PEER_CLOSED | MX_SOCKET_READ_DISABLED;
|
||||
}
|
||||
*_signals = signals;
|
||||
}
|
||||
|
||||
void mx_pipe_wait_end(mxio_t* io, mx_signals_t signals, uint32_t* _events) {
|
||||
uint32_t events = 0;
|
||||
if (signals & (MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED)) {
|
||||
if (signals & (MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED | MX_SOCKET_READ_DISABLED)) {
|
||||
events |= POLLIN;
|
||||
}
|
||||
if (signals & MX_SOCKET_WRITABLE) {
|
||||
if (signals & (MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED)) {
|
||||
events |= POLLOUT;
|
||||
}
|
||||
if (signals & MX_SOCKET_PEER_CLOSED) {
|
||||
if (signals & (MX_SOCKET_PEER_CLOSED | MX_SOCKET_READ_DISABLED)) {
|
||||
events |= POLLRDHUP;
|
||||
}
|
||||
*_events = events;
|
||||
|
||||
@@ -29,12 +29,12 @@ static ssize_t mxsio_read_stream(mxio_t* io, void* data, size_t len) {
|
||||
if ((r = mx_socket_read(rio->h2, 0, data, len, &len)) == MX_OK) {
|
||||
return (ssize_t) len;
|
||||
}
|
||||
if (r == MX_ERR_PEER_CLOSED) {
|
||||
if (r == MX_ERR_PEER_CLOSED || r == MX_ERR_BAD_STATE) {
|
||||
return 0;
|
||||
} else if (r == MX_ERR_SHOULD_WAIT && !nonblock) {
|
||||
mx_signals_t pending;
|
||||
r = mx_object_wait_one(rio->h2,
|
||||
MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED,
|
||||
MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED | MX_SOCKET_READ_DISABLED,
|
||||
MX_TIME_INFINITE, &pending);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
@@ -42,7 +42,7 @@ static ssize_t mxsio_read_stream(mxio_t* io, void* data, size_t len) {
|
||||
if (pending & MX_SOCKET_READABLE) {
|
||||
continue;
|
||||
}
|
||||
if (pending & MX_SOCKET_PEER_CLOSED) {
|
||||
if (pending & (MX_SOCKET_PEER_CLOSED | MX_SOCKET_READ_DISABLED)) {
|
||||
return 0;
|
||||
}
|
||||
// impossible
|
||||
@@ -85,16 +85,16 @@ static ssize_t mxsio_write_stream(mxio_t* io, const void* data, size_t len) {
|
||||
return (ssize_t) len;
|
||||
}
|
||||
if (r == MX_ERR_SHOULD_WAIT && !nonblock) {
|
||||
// No wait for PEER_CLOSED signal. PEER_CLOSED could be signaled
|
||||
// even if the socket is only half-closed for read.
|
||||
// TODO: how to detect if the write direction is closed?
|
||||
mx_signals_t pending;
|
||||
r = mx_object_wait_one(rio->h2,
|
||||
MX_SOCKET_WRITABLE,
|
||||
MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED | MX_SOCKET_PEER_CLOSED,
|
||||
MX_TIME_INFINITE, &pending);
|
||||
if (r < 0) {
|
||||
return r;
|
||||
}
|
||||
if (pending & (MX_SOCKET_WRITE_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
return MX_ERR_BAD_STATE;
|
||||
}
|
||||
if (pending & MX_SOCKET_WRITABLE) {
|
||||
continue;
|
||||
}
|
||||
@@ -239,10 +239,10 @@ static void mxsio_wait_begin_stream(mxio_t* io, uint32_t events, mx_handle_t* ha
|
||||
if (io->flags & MXIO_FLAG_SOCKET_CONNECTED) {
|
||||
// if socket is connected
|
||||
if (events & POLLIN) {
|
||||
signals |= MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED;
|
||||
signals |= MX_SOCKET_READABLE | MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED;
|
||||
}
|
||||
if (events & POLLOUT) {
|
||||
signals |= MX_SOCKET_WRITABLE;
|
||||
signals |= MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED;
|
||||
}
|
||||
} else {
|
||||
// if socket is not connected
|
||||
@@ -250,7 +250,7 @@ static void mxsio_wait_begin_stream(mxio_t* io, uint32_t events, mx_handle_t* ha
|
||||
// signal when a listening socket gets an incoming connection
|
||||
// or a connecting socket gets connected and receives data
|
||||
signals |= MXSIO_SIGNAL_INCOMING |
|
||||
MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED;
|
||||
MX_SOCKET_READABLE | MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED;
|
||||
}
|
||||
if (events & POLLOUT) {
|
||||
// signal when connect() operation is finished
|
||||
@@ -258,7 +258,7 @@ static void mxsio_wait_begin_stream(mxio_t* io, uint32_t events, mx_handle_t* ha
|
||||
}
|
||||
}
|
||||
if (events & POLLRDHUP) {
|
||||
signals |= MX_SOCKET_PEER_CLOSED;
|
||||
signals |= MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED;
|
||||
}
|
||||
*_signals = signals;
|
||||
}
|
||||
@@ -273,10 +273,10 @@ static void mxsio_wait_end_stream(mxio_t* io, mx_signals_t signals, uint32_t* _e
|
||||
}
|
||||
uint32_t events = 0;
|
||||
if (io->flags & MXIO_FLAG_SOCKET_CONNECTED) {
|
||||
if (signals & (MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED)) {
|
||||
if (signals & (MX_SOCKET_READABLE | MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
events |= POLLIN;
|
||||
}
|
||||
if (signals & MX_SOCKET_WRITABLE) {
|
||||
if (signals & (MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED)) {
|
||||
events |= POLLOUT;
|
||||
}
|
||||
} else {
|
||||
@@ -290,7 +290,7 @@ static void mxsio_wait_end_stream(mxio_t* io, mx_signals_t signals, uint32_t* _e
|
||||
if (signals & MXSIO_SIGNAL_ERROR) {
|
||||
events |= POLLERR;
|
||||
}
|
||||
if (signals & MX_SOCKET_PEER_CLOSED) {
|
||||
if (signals & (MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
events |= POLLRDHUP;
|
||||
}
|
||||
*_events = events;
|
||||
@@ -459,30 +459,29 @@ static void mxsio_wait_begin_dgram(mxio_t* io, uint32_t events, mx_handle_t* han
|
||||
*handle = rio->h2;
|
||||
mx_signals_t signals = MXSIO_SIGNAL_ERROR;
|
||||
if (events & POLLIN) {
|
||||
signals |= MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED;
|
||||
signals |= MX_SOCKET_READABLE | MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED;
|
||||
}
|
||||
if (events & POLLOUT) {
|
||||
signals |= MX_SOCKET_WRITABLE;
|
||||
signals |= MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED;
|
||||
}
|
||||
if (events & POLLRDHUP) {
|
||||
signals |= MX_SOCKET_PEER_CLOSED;
|
||||
signals |= MX_CHANNEL_PEER_CLOSED;
|
||||
signals |= MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED;
|
||||
}
|
||||
*_signals = signals;
|
||||
}
|
||||
|
||||
static void mxsio_wait_end_dgram(mxio_t* io, mx_signals_t signals, uint32_t* _events) {
|
||||
uint32_t events = 0;
|
||||
if (signals & (MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED)) {
|
||||
if (signals & (MX_SOCKET_READABLE | MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
events |= POLLIN;
|
||||
}
|
||||
if (signals & MX_SOCKET_WRITABLE) {
|
||||
if (signals & (MX_SOCKET_WRITABLE | MX_SOCKET_WRITE_DISABLED)) {
|
||||
events |= POLLOUT;
|
||||
}
|
||||
if (signals & MXSIO_SIGNAL_ERROR) {
|
||||
events |= POLLERR;
|
||||
}
|
||||
if (signals & MX_SOCKET_PEER_CLOSED) {
|
||||
if (signals & (MX_SOCKET_READ_DISABLED | MX_SOCKET_PEER_CLOSED)) {
|
||||
events |= POLLRDHUP;
|
||||
}
|
||||
*_events = events;
|
||||
@@ -565,12 +564,17 @@ mx_status_t mxio_socket_shutdown(mxio_t* io, int how) {
|
||||
return MX_ERR_BAD_STATE;
|
||||
}
|
||||
mxrio_t* rio = (mxrio_t*)io;
|
||||
if (how == SHUT_RD || how == SHUT_RDWR) {
|
||||
// TODO: turn on a flag to prevent all read attempts
|
||||
uint32_t options = 0;
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
options = MX_SOCKET_SHUTDOWN_READ;
|
||||
break;
|
||||
case SHUT_WR:
|
||||
options = MX_SOCKET_SHUTDOWN_WRITE;
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
options = MX_SOCKET_SHUTDOWN_READ | MX_SOCKET_SHUTDOWN_WRITE;
|
||||
break;
|
||||
}
|
||||
if (how == SHUT_WR || how == SHUT_RDWR) {
|
||||
// TODO: turn on a flag to prevent all write attempts
|
||||
mx_object_signal_peer(rio->h2, 0u, MXSIO_SIGNAL_HALFCLOSED);
|
||||
}
|
||||
return MX_OK;
|
||||
return mx_socket_write(rio->h2, options, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
@@ -134,12 +134,18 @@ int sockatmark(int fd) {
|
||||
|
||||
mx_status_t mxio_socketpair_shutdown(mxio_t* io, int how) {
|
||||
mx_pipe_t* p = (mx_pipe_t*)io;
|
||||
if (how == SHUT_RD || how == SHUT_RDWR) {
|
||||
// TODO: flag to disable all reads from this end (and writes from other)
|
||||
|
||||
uint32_t options = 0;
|
||||
switch (how) {
|
||||
case SHUT_RD:
|
||||
options = MX_SOCKET_SHUTDOWN_READ;
|
||||
break;
|
||||
case SHUT_WR:
|
||||
options = MX_SOCKET_SHUTDOWN_WRITE;
|
||||
break;
|
||||
case SHUT_RDWR:
|
||||
options = MX_SOCKET_SHUTDOWN_READ | MX_SOCKET_SHUTDOWN_WRITE;
|
||||
break;
|
||||
}
|
||||
if (how == SHUT_WR || how == SHUT_RDWR) {
|
||||
// TODO: turn on a flag to prevent all write attempts
|
||||
mx_socket_write(p->h, MX_SOCKET_HALF_CLOSE, NULL, 0, NULL);
|
||||
}
|
||||
return MX_OK;
|
||||
return mx_socket_write(p->h, options, NULL, 0, NULL);
|
||||
}
|
||||
|
||||
+256
-112
@@ -29,34 +29,34 @@ static bool socket_basic(void) {
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
|
||||
status = mx_socket_read(h[0], 0u, read_data, sizeof(read_data), &count);
|
||||
ASSERT_EQ(status, MX_ERR_SHOULD_WAIT, "");
|
||||
EXPECT_EQ(status, MX_ERR_SHOULD_WAIT, "");
|
||||
|
||||
static const uint32_t write_data[] = { 0xdeadbeef, 0xc0ffee };
|
||||
status = mx_socket_write(h[0], 0u, &write_data[0], sizeof(write_data[0]), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(write_data[0]), "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(write_data[0]), "");
|
||||
status = mx_socket_write(h[0], 0u, &write_data[1], sizeof(write_data[1]), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(write_data[1]), "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(write_data[1]), "");
|
||||
|
||||
status = mx_socket_read(h[1], 0u, read_data, sizeof(read_data), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(read_data), "");
|
||||
ASSERT_EQ(read_data[0], write_data[0], "");
|
||||
ASSERT_EQ(read_data[1], write_data[1], "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(read_data), "");
|
||||
EXPECT_EQ(read_data[0], write_data[0], "");
|
||||
EXPECT_EQ(read_data[1], write_data[1], "");
|
||||
|
||||
status = mx_socket_write(h[0], 0u, write_data, sizeof(write_data), NULL);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
memset(read_data, 0, sizeof(read_data));
|
||||
status = mx_socket_read(h[1], 0u, read_data, sizeof(read_data), NULL);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(read_data[0], write_data[0], "");
|
||||
ASSERT_EQ(read_data[1], write_data[1], "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(read_data[0], write_data[0], "");
|
||||
EXPECT_EQ(read_data[1], write_data[1], "");
|
||||
|
||||
mx_handle_close(h[1]);
|
||||
|
||||
status = mx_socket_write(h[0], 0u, &write_data[1], sizeof(write_data[1]), &count);
|
||||
ASSERT_EQ(status, MX_ERR_PEER_CLOSED, "");
|
||||
EXPECT_EQ(status, MX_ERR_PEER_CLOSED, "");
|
||||
|
||||
mx_handle_close(h[0]);
|
||||
END_TEST;
|
||||
@@ -75,8 +75,8 @@ static bool socket_signals(void) {
|
||||
mx_signals_t signals0 = get_satisfied_signals(h0);
|
||||
mx_signals_t signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
ASSERT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
const size_t kAllSize = 128 * 1024;
|
||||
char* big_buf = (char*) malloc(kAllSize);
|
||||
@@ -85,41 +85,41 @@ static bool socket_signals(void) {
|
||||
memset(big_buf, 0x66, kAllSize);
|
||||
|
||||
status = mx_socket_write(h0, 0u, big_buf, kAllSize / 16, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, kAllSize / 16, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, kAllSize / 16, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
ASSERT_EQ(signals1, MX_SOCKET_READABLE | MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_READABLE | MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, big_buf, kAllSize, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, kAllSize / 16, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, kAllSize / 16, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
ASSERT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_object_signal_peer(h0, MX_SOCKET_WRITABLE, 0u);
|
||||
ASSERT_EQ(status, MX_ERR_INVALID_ARGS, "");
|
||||
EXPECT_EQ(status, MX_ERR_INVALID_ARGS, "");
|
||||
|
||||
status = mx_object_signal_peer(h0, 0u, MX_USER_SIGNAL_1);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
ASSERT_EQ(signals1, MX_SOCKET_WRITABLE | MX_USER_SIGNAL_1 | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_USER_SIGNAL_1 | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
mx_handle_close(h1);
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
ASSERT_EQ(signals0, MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
mx_handle_close(h0);
|
||||
|
||||
@@ -127,7 +127,7 @@ static bool socket_signals(void) {
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
static bool socket_half_close(void) {
|
||||
static bool socket_shutdown_write(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
mx_status_t status;
|
||||
@@ -141,51 +141,123 @@ static bool socket_half_close(void) {
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
ASSERT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "12345", 5u, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
status = mx_socket_write(h1, MX_SOCKET_HALF_CLOSE, NULL, 0u, NULL);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
status = mx_socket_write(h1, MX_SOCKET_SHUTDOWN_WRITE, NULL, 0u, NULL);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0,
|
||||
MX_SOCKET_WRITABLE | MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE,
|
||||
EXPECT_EQ(signals0,
|
||||
MX_SOCKET_WRITABLE | MX_SOCKET_READABLE | MX_SIGNAL_LAST_HANDLE,
|
||||
"");
|
||||
ASSERT_EQ(signals1, MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h0, 0u, "abcde", 5u, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
ASSERT_EQ(signals1, MX_SOCKET_READABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_READABLE | MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "fghij", 5u, &count);
|
||||
ASSERT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
char rbuf[10] = {0};
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, sizeof(rbuf), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
ASSERT_EQ(memcmp(rbuf, "12345", 5), 0, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "12345", 5), 0, "");
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, 1u, &count);
|
||||
ASSERT_EQ(status, MX_ERR_PEER_CLOSED, "");
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_READ_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, rbuf, sizeof(rbuf), &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "abcde", 5), 0, "");
|
||||
|
||||
mx_handle_close(h0);
|
||||
|
||||
// Calling shutdown after the peer is closed is completely valid.
|
||||
status = mx_socket_write(h1, MX_SOCKET_SHUTDOWN_READ, NULL, 0u, NULL);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
EXPECT_EQ(signals1, MX_SOCKET_READ_DISABLED | MX_SOCKET_WRITE_DISABLED | MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
mx_handle_close(h1);
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
static bool socket_shutdown_read(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
mx_status_t status;
|
||||
size_t count;
|
||||
mx_signals_t signals0, signals1;
|
||||
|
||||
mx_handle_t h0, h1;
|
||||
status = mx_socket_create(0, &h0, &h1);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
ASSERT_EQ(memcmp(rbuf, "abcde", 5), 0, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "12345", 5u, &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
status = mx_socket_write(h0, MX_SOCKET_SHUTDOWN_READ, NULL, 0u, NULL);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_READABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h0, 0u, "abcde", 5u, &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
EXPECT_EQ(signals1, MX_SOCKET_READABLE | MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "fghij", 5u, &count);
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
char rbuf[10] = {0};
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, sizeof(rbuf), &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "12345", 5), 0, "");
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, 1u, &count);
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_READ_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, rbuf, sizeof(rbuf), &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "abcde", 5), 0, "");
|
||||
|
||||
mx_handle_close(h0);
|
||||
mx_handle_close(h1);
|
||||
@@ -206,39 +278,39 @@ static bool socket_bytes_outstanding(void) {
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
|
||||
status = mx_socket_read(h[0], 0u, read_data, sizeof(read_data), &count);
|
||||
ASSERT_EQ(status, MX_ERR_SHOULD_WAIT, "");
|
||||
EXPECT_EQ(status, MX_ERR_SHOULD_WAIT, "");
|
||||
|
||||
static const uint32_t write_data[] = { 0xdeadbeef, 0xc0ffee };
|
||||
status = mx_socket_write(h[0], 0u, &write_data[0], sizeof(write_data[0]), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(write_data[0]), "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(write_data[0]), "");
|
||||
status = mx_socket_write(h[0], 0u, &write_data[1], sizeof(write_data[1]), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(write_data[1]), "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(write_data[1]), "");
|
||||
|
||||
// Check the number of bytes outstanding.
|
||||
size_t outstanding = 0u;
|
||||
status = mx_socket_read(h[1], 0u, NULL, 0, &outstanding);
|
||||
ASSERT_EQ(outstanding, sizeof(write_data), "");
|
||||
EXPECT_EQ(outstanding, sizeof(write_data), "");
|
||||
|
||||
// Check that the prior mx_socket_read call didn't disturb the pending data.
|
||||
status = mx_socket_read(h[1], 0u, read_data, sizeof(read_data), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(read_data), "");
|
||||
ASSERT_EQ(read_data[0], write_data[0], "");
|
||||
ASSERT_EQ(read_data[1], write_data[1], "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(read_data), "");
|
||||
EXPECT_EQ(read_data[0], write_data[0], "");
|
||||
EXPECT_EQ(read_data[1], write_data[1], "");
|
||||
|
||||
mx_handle_close(h[1]);
|
||||
|
||||
status = mx_socket_write(h[0], 0u, &write_data[1], sizeof(write_data[1]), &count);
|
||||
ASSERT_EQ(status, MX_ERR_PEER_CLOSED, "");
|
||||
EXPECT_EQ(status, MX_ERR_PEER_CLOSED, "");
|
||||
|
||||
mx_handle_close(h[0]);
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
static bool socket_bytes_outstanding_half_close(void) {
|
||||
static bool socket_bytes_outstanding_shutdown_write(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
mx_status_t status;
|
||||
@@ -252,56 +324,126 @@ static bool socket_bytes_outstanding_half_close(void) {
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
ASSERT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "12345", 5u, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
status = mx_socket_write(h1, MX_SOCKET_HALF_CLOSE, NULL, 0u, NULL);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
status = mx_socket_write(h1, MX_SOCKET_SHUTDOWN_WRITE, NULL, 0u, NULL);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
ASSERT_EQ(signals0,
|
||||
MX_SOCKET_WRITABLE | MX_SOCKET_READABLE | MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE,
|
||||
EXPECT_EQ(signals0,
|
||||
MX_SOCKET_WRITABLE | MX_SOCKET_READABLE | MX_SIGNAL_LAST_HANDLE,
|
||||
"");
|
||||
ASSERT_EQ(signals1, MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h0, 0u, "abcde", 5u, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
ASSERT_EQ(signals1, MX_SOCKET_READABLE | MX_SIGNAL_LAST_HANDLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_READABLE | MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "fghij", 5u, &count);
|
||||
ASSERT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
char rbuf[10] = {0};
|
||||
|
||||
status = mx_socket_read(h0, 0u, NULL, 0, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
count = 0;
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, sizeof(rbuf), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
ASSERT_EQ(memcmp(rbuf, "12345", 5), 0, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "12345", 5), 0, "");
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, 1u, &count);
|
||||
ASSERT_EQ(status, MX_ERR_PEER_CLOSED, "");
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
ASSERT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_PEER_CLOSED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_READ_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, rbuf, sizeof(rbuf), &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "abcde", 5), 0, "");
|
||||
|
||||
mx_handle_close(h0);
|
||||
mx_handle_close(h1);
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
|
||||
static bool socket_bytes_outstanding_shutdown_read(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
mx_status_t status;
|
||||
size_t count;
|
||||
mx_signals_t signals0, signals1;
|
||||
|
||||
mx_handle_t h0, h1;
|
||||
status = mx_socket_create(0, &h0, &h1);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
ASSERT_EQ(memcmp(rbuf, "abcde", 5), 0, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "12345", 5u, &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
status = mx_socket_write(h0, MX_SOCKET_SHUTDOWN_READ, NULL, 0u, NULL);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_READABLE | MX_SIGNAL_LAST_HANDLE, "");
|
||||
EXPECT_EQ(signals1, MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h0, 0u, "abcde", 5u, &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
signals1 = get_satisfied_signals(h1);
|
||||
EXPECT_EQ(signals1, MX_SOCKET_READABLE | MX_SOCKET_WRITE_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_write(h1, 0u, "fghij", 5u, &count);
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
char rbuf[10] = {0};
|
||||
|
||||
status = mx_socket_read(h0, 0u, NULL, 0, &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
count = 0;
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, sizeof(rbuf), &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "12345", 5), 0, "");
|
||||
|
||||
status = mx_socket_read(h0, 0u, rbuf, 1u, &count);
|
||||
EXPECT_EQ(status, MX_ERR_BAD_STATE, "");
|
||||
|
||||
signals0 = get_satisfied_signals(h0);
|
||||
EXPECT_EQ(signals0, MX_SOCKET_WRITABLE | MX_SOCKET_READ_DISABLED | MX_SIGNAL_LAST_HANDLE, "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, rbuf, sizeof(rbuf), &count);
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "abcde", 5), 0, "");
|
||||
|
||||
mx_handle_close(h0);
|
||||
mx_handle_close(h1);
|
||||
@@ -345,12 +487,12 @@ static bool socket_datagram(void) {
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
|
||||
status = mx_socket_write(h0, 0u, "packet1", 8u, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 8u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 8u, "");
|
||||
|
||||
status = mx_socket_write(h0, 0u, "pkt2", 5u, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
|
||||
rbuf[0] = 'a';
|
||||
rbuf[1000] = 'b';
|
||||
@@ -359,44 +501,44 @@ static bool socket_datagram(void) {
|
||||
rbuf[4000] = 'e';
|
||||
rbuf[4095] = 'f';
|
||||
status = mx_socket_write(h0, 0u, rbuf, sizeof(rbuf), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(rbuf), "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(rbuf), "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, NULL, 0, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(rbuf) + 8u + 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(rbuf) + 8u + 5u, "");
|
||||
count = 0;
|
||||
|
||||
bzero(rbuf, sizeof(rbuf));
|
||||
status = mx_socket_read(h1, 0u, rbuf, 3, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 3u, "");
|
||||
ASSERT_EQ(memcmp(rbuf, "pac", 4), 0, ""); // short read "packet1"
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 3u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "pac", 4), 0, ""); // short read "packet1"
|
||||
count = 0;
|
||||
|
||||
status = mx_socket_read(h1, 0u, NULL, 0, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(rbuf) + 5u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(rbuf) + 5u, "");
|
||||
count = 0;
|
||||
|
||||
status = mx_socket_read(h1, 0u, rbuf, sizeof(rbuf), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 5u, "");
|
||||
ASSERT_EQ(memcmp(rbuf, "pkt2", 5), 0, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 5u, "");
|
||||
EXPECT_EQ(memcmp(rbuf, "pkt2", 5), 0, "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, rbuf, sizeof(rbuf), &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, sizeof(rbuf), "");
|
||||
ASSERT_EQ(rbuf[0], 'a', "");
|
||||
ASSERT_EQ(rbuf[1000], 'b', "");
|
||||
ASSERT_EQ(rbuf[2000], 'c', "");
|
||||
ASSERT_EQ(rbuf[3000], 'd', "");
|
||||
ASSERT_EQ(rbuf[4000], 'e', "");
|
||||
ASSERT_EQ(rbuf[4095], 'f', "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, sizeof(rbuf), "");
|
||||
EXPECT_EQ(rbuf[0], 'a', "");
|
||||
EXPECT_EQ(rbuf[1000], 'b', "");
|
||||
EXPECT_EQ(rbuf[2000], 'c', "");
|
||||
EXPECT_EQ(rbuf[3000], 'd', "");
|
||||
EXPECT_EQ(rbuf[4000], 'e', "");
|
||||
EXPECT_EQ(rbuf[4095], 'f', "");
|
||||
|
||||
status = mx_socket_read(h1, 0u, NULL, 0, &count);
|
||||
ASSERT_EQ(status, MX_OK, "");
|
||||
ASSERT_EQ(count, 0u, "");
|
||||
EXPECT_EQ(status, MX_OK, "");
|
||||
EXPECT_EQ(count, 0u, "");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
@@ -430,9 +572,11 @@ static bool socket_datagram_no_short_write(void) {
|
||||
BEGIN_TEST_CASE(socket_tests)
|
||||
RUN_TEST(socket_basic)
|
||||
RUN_TEST(socket_signals)
|
||||
RUN_TEST(socket_half_close)
|
||||
RUN_TEST(socket_shutdown_write)
|
||||
RUN_TEST(socket_shutdown_read)
|
||||
RUN_TEST(socket_bytes_outstanding)
|
||||
RUN_TEST(socket_bytes_outstanding_half_close)
|
||||
RUN_TEST(socket_bytes_outstanding_shutdown_write)
|
||||
RUN_TEST(socket_bytes_outstanding_shutdown_read)
|
||||
RUN_TEST(socket_short_write)
|
||||
RUN_TEST(socket_datagram)
|
||||
RUN_TEST(socket_datagram_no_short_write)
|
||||
|
||||
@@ -5,8 +5,11 @@
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <poll.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <threads.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unittest/unittest.h>
|
||||
@@ -79,6 +82,13 @@ bool socketpair_shutdown_setup(int fds[2]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(__Fuchsia__)
|
||||
#define SEND_FLAGS 0
|
||||
#else
|
||||
#define SEND_FLAGS MSG_NOSIGNAL
|
||||
#endif
|
||||
|
||||
|
||||
bool socketpair_shutdown_rd_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
@@ -95,13 +105,15 @@ bool socketpair_shutdown_rd_test(void) {
|
||||
if (status != 0)
|
||||
printf("\nerrno %d\n", errno);
|
||||
|
||||
/*
|
||||
TODO: doesn't pass on linux, maybe there's another way to test for SHUTDOWN_RD ?
|
||||
// Shouldn't be able to read from fds[0] any more.
|
||||
errno = 0;
|
||||
EXPECT_EQ(read(fds[0], buf, sizeof(buf)), -1, "fds[0] should not be readable after SHUT_RD");
|
||||
EXPECT_EQ(errno, EINVAL, "read should return EINVAL after shutdown(SHUT_RD)");
|
||||
*/
|
||||
// Can read the byte already written into the pipe.
|
||||
EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 1, "fds[0] should not be readable after SHUT_RD");
|
||||
|
||||
// But not send any further bytes
|
||||
EXPECT_EQ(send(fds[1], buf, sizeof(buf), SEND_FLAGS), -1, "");
|
||||
EXPECT_EQ(errno, EPIPE, "send should return EPIPE after shutdown(SHUT_RD) on other side");
|
||||
|
||||
// Or read any more
|
||||
EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 0, "");
|
||||
|
||||
EXPECT_EQ(close(fds[0]), 0, "");
|
||||
EXPECT_EQ(close(fds[1]), 0, "");
|
||||
@@ -109,12 +121,6 @@ bool socketpair_shutdown_rd_test(void) {
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
#if defined(__Fuchsia__)
|
||||
#define SEND_FLAGS 0
|
||||
#else
|
||||
#define SEND_FLAGS MSG_NOSIGNAL
|
||||
#endif
|
||||
|
||||
bool socketpair_shutdown_wr_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
@@ -161,17 +167,231 @@ bool socketpair_shutdown_rdwr_test(void) {
|
||||
|
||||
char buf[1] = {};
|
||||
|
||||
/*
|
||||
TODO: doesn't pass on linux, maybe there's another way to test for SHUTDOWN_RD ?
|
||||
// Reading should fail.
|
||||
EXPECT_EQ(read(fds[0], buf, sizeof(buf)), -1, "");
|
||||
EXPECT_EQ(errno, EAGAIN, "errno after read after SHUT_RDWR");
|
||||
*/
|
||||
|
||||
// Writing should fail.
|
||||
EXPECT_EQ(send(fds[0], buf, sizeof(buf), SEND_FLAGS), -1, "");
|
||||
EXPECT_EQ(errno, EPIPE, "errno after write after SHUT_RDWR");
|
||||
|
||||
// Reading should return no data.
|
||||
EXPECT_EQ(read(fds[0], buf, sizeof(buf)), 0, "");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
typedef struct poll_for_read_args {
|
||||
int fd;
|
||||
int poll_result;
|
||||
mx_time_t poll_time;
|
||||
} poll_for_read_args_t;
|
||||
|
||||
int poll_for_read_with_timeout(void* arg) {
|
||||
poll_for_read_args_t* poll_args = (poll_for_read_args_t*) arg;
|
||||
struct pollfd pollfd;
|
||||
pollfd.fd = poll_args->fd;
|
||||
pollfd.events = POLLIN;
|
||||
pollfd.revents = 0;
|
||||
|
||||
int timeout_ms = 100;
|
||||
mx_time_t time_before = mx_time_get(CLOCK_MONOTONIC);
|
||||
poll_args->poll_result = poll(&pollfd, 1, timeout_ms);
|
||||
mx_time_t time_after = mx_time_get(CLOCK_MONOTONIC);
|
||||
poll_args->poll_time = time_after - time_before;
|
||||
|
||||
int num_readable = 0;
|
||||
EXPECT_EQ(ioctl(poll_args->fd, FIONREAD, &num_readable), 0, "ioctl(FIONREAD)");
|
||||
EXPECT_EQ(num_readable, 0, "");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
bool socketpair_shutdown_self_wr_poll_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
int fds[2];
|
||||
socketpair_shutdown_setup(fds);
|
||||
|
||||
poll_for_read_args_t poll_args = {};
|
||||
poll_args.fd = fds[0];
|
||||
thrd_t poll_thread;
|
||||
int thrd_create_result = thrd_create(&poll_thread, poll_for_read_with_timeout, &poll_args);
|
||||
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread");
|
||||
|
||||
shutdown(fds[0], SHUT_RDWR);
|
||||
|
||||
ASSERT_EQ(thrd_join(poll_thread, NULL), thrd_success, "join blocking read thread");
|
||||
|
||||
EXPECT_EQ(poll_args.poll_result, 1, "poll should have one entry");
|
||||
EXPECT_LT(poll_args.poll_time, 100u * 1000 * 1000, "poll should not have timed out");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
bool socketpair_shutdown_peer_wr_poll_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
int fds[2];
|
||||
socketpair_shutdown_setup(fds);
|
||||
|
||||
poll_for_read_args_t poll_args = {};
|
||||
poll_args.fd = fds[0];
|
||||
thrd_t poll_thread;
|
||||
int thrd_create_result = thrd_create(&poll_thread, poll_for_read_with_timeout, &poll_args);
|
||||
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread");
|
||||
|
||||
shutdown(fds[1], SHUT_RDWR);
|
||||
|
||||
ASSERT_EQ(thrd_join(poll_thread, NULL), thrd_success, "join blocking read thread");
|
||||
|
||||
EXPECT_EQ(poll_args.poll_result, 1, "poll should have one entry");
|
||||
EXPECT_LT(poll_args.poll_time, 100u * 1000 * 1000, "poll should not have timed out");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
#define BUF_SIZE 256
|
||||
|
||||
typedef struct recv_args {
|
||||
int fd;
|
||||
int recv_result;
|
||||
int recv_errno;
|
||||
char buf[BUF_SIZE];
|
||||
} recv_args_t;
|
||||
|
||||
int recv_thread(void* arg) {
|
||||
recv_args_t* recv_args = (recv_args_t*) arg;
|
||||
|
||||
recv_args->recv_result = recv(recv_args->fd, recv_args->buf, BUF_SIZE, 0u);
|
||||
if (recv_args->recv_result < 0)
|
||||
recv_args->recv_errno = errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool socketpair_shutdown_self_rd_during_recv_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
int fds[2];
|
||||
int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed");
|
||||
|
||||
recv_args_t recv_args = {};
|
||||
recv_args.fd = fds[0];
|
||||
thrd_t t;
|
||||
int thrd_create_result = thrd_create(&t, recv_thread, &recv_args);
|
||||
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread");
|
||||
|
||||
shutdown(fds[0], SHUT_RD);
|
||||
|
||||
ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking read thread");
|
||||
|
||||
EXPECT_EQ(recv_args.recv_result, 0, "recv should have returned 0");
|
||||
EXPECT_EQ(recv_args.recv_errno, 0, "recv should have left errno alone");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
|
||||
bool socketpair_shutdown_peer_wr_during_recv_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
int fds[2];
|
||||
int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed");
|
||||
|
||||
recv_args_t recv_args = {};
|
||||
recv_args.fd = fds[0];
|
||||
thrd_t t;
|
||||
int thrd_create_result = thrd_create(&t, recv_thread, &recv_args);
|
||||
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking read thread");
|
||||
|
||||
shutdown(fds[1], SHUT_WR);
|
||||
|
||||
ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking read thread");
|
||||
|
||||
EXPECT_EQ(recv_args.recv_result, 0, "recv should have returned 0");
|
||||
EXPECT_EQ(recv_args.recv_errno, 0, "recv should have left errno alone");
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
typedef struct send_args {
|
||||
int fd;
|
||||
int send_result;
|
||||
int send_errno;
|
||||
char buf[BUF_SIZE];
|
||||
} send_args_t;
|
||||
|
||||
int send_thread(void* arg) {
|
||||
send_args_t* send_args = (send_args_t*) arg;
|
||||
|
||||
send_args->send_result = send(send_args->fd, send_args->buf, BUF_SIZE, 0u);
|
||||
if (send_args->send_result < 0)
|
||||
send_args->send_errno = errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool socketpair_shutdown_self_wr_during_send_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
int fds[2];
|
||||
int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed");
|
||||
|
||||
// First, fill up the socket so the next send() will block.
|
||||
char buf[BUF_SIZE] = {};
|
||||
while (true) {
|
||||
status = send(fds[0], buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (status < 0) {
|
||||
ASSERT_EQ(errno, EAGAIN, "send should eventually return EAGAIN when full");
|
||||
break;
|
||||
}
|
||||
}
|
||||
send_args_t send_args = {};
|
||||
send_args.fd = fds[0];
|
||||
thrd_t t;
|
||||
// Then start a thread blocking on a send().
|
||||
int thrd_create_result = thrd_create(&t, send_thread, &send_args);
|
||||
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking send thread");
|
||||
|
||||
shutdown(fds[0], SHUT_WR);
|
||||
|
||||
ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking send thread");
|
||||
|
||||
EXPECT_EQ(send_args.send_result, -1, "send should have returned -1");
|
||||
EXPECT_EQ(send_args.send_errno, EPIPE, "send should have set errno to EPIPE");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
bool socketpair_shutdown_peer_rd_during_send_test(void) {
|
||||
BEGIN_TEST;
|
||||
|
||||
int fds[2];
|
||||
int status = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
|
||||
ASSERT_EQ(status, 0, "socketpair(AF_UNIX, SOCK_STREAM, 0, fds) failed");
|
||||
|
||||
// First, fill up the socket so the next send() will block.
|
||||
char buf[BUF_SIZE] = {};
|
||||
while (true) {
|
||||
status = send(fds[0], buf, sizeof(buf), MSG_DONTWAIT);
|
||||
if (status < 0) {
|
||||
ASSERT_EQ(errno, EAGAIN, "send should eventually return EAGAIN when full");
|
||||
break;
|
||||
}
|
||||
}
|
||||
send_args_t send_args = {};
|
||||
send_args.fd = fds[0];
|
||||
thrd_t t;
|
||||
int thrd_create_result = thrd_create(&t, send_thread, &send_args);
|
||||
ASSERT_EQ(thrd_create_result, thrd_success, "create blocking send thread");
|
||||
|
||||
shutdown(fds[1], SHUT_RD);
|
||||
|
||||
ASSERT_EQ(thrd_join(t, NULL), thrd_success, "join blocking send thread");
|
||||
|
||||
EXPECT_EQ(send_args.send_result, -1, "send should have returned -1");
|
||||
EXPECT_EQ(send_args.send_errno, EPIPE, "send should have set errno to EPIPE");
|
||||
|
||||
END_TEST;
|
||||
}
|
||||
|
||||
@@ -180,4 +400,10 @@ RUN_TEST(socketpair_test);
|
||||
RUN_TEST(socketpair_shutdown_rd_test);
|
||||
RUN_TEST(socketpair_shutdown_wr_test);
|
||||
RUN_TEST(socketpair_shutdown_rdwr_test);
|
||||
RUN_TEST(socketpair_shutdown_self_wr_poll_test);
|
||||
RUN_TEST(socketpair_shutdown_peer_wr_poll_test);
|
||||
RUN_TEST(socketpair_shutdown_self_rd_during_recv_test);
|
||||
RUN_TEST(socketpair_shutdown_peer_wr_during_recv_test);
|
||||
RUN_TEST(socketpair_shutdown_self_wr_during_send_test);
|
||||
RUN_TEST(socketpair_shutdown_peer_rd_during_send_test);
|
||||
END_TEST_CASE(mxio_socketpair_test)
|
||||
|
||||
Referência em uma Nova Issue
Bloquear um usuário