Merge pull request #5466 from kjbracey-arm/write_all

Make POSIX-like writes write everything when blocking
pull/5574/head^2
Jimmy Brisson 2017-11-27 10:36:28 -06:00 committed by GitHub
commit b9c3003419
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 79 additions and 34 deletions

View File

@ -138,36 +138,47 @@ ssize_t UARTSerial::write(const void* buffer, size_t length)
size_t data_written = 0;
const char *buf_ptr = static_cast<const char *>(buffer);
if (length == 0) {
return 0;
}
api_lock();
while (_txbuf.full()) {
if (!_blocking) {
api_unlock();
return -EAGAIN;
}
api_unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
api_lock();
}
// Unlike read, we should write the whole thing if blocking. POSIX only
// allows partial as a side-effect of signal handling; it normally tries to
// write everything if blocking. Without signals we can always write all.
while (data_written < length) {
while (data_written < length && !_txbuf.full()) {
_txbuf.push(*buf_ptr++);
data_written++;
}
core_util_critical_section_enter();
if (!_tx_irq_enabled) {
UARTSerial::tx_irq(); // only write to hardware in one place
if (!_txbuf.empty()) {
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
_tx_irq_enabled = true;
if (_txbuf.full()) {
if (!_blocking) {
break;
}
do {
api_unlock();
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
api_lock();
} while (_txbuf.full());
}
while (data_written < length && !_txbuf.full()) {
_txbuf.push(*buf_ptr++);
data_written++;
}
core_util_critical_section_enter();
if (!_tx_irq_enabled) {
UARTSerial::tx_irq(); // only write to hardware in one place
if (!_txbuf.empty()) {
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
_tx_irq_enabled = true;
}
}
core_util_critical_section_exit();
}
core_util_critical_section_exit();
api_unlock();
return data_written;
return data_written != 0 ? (ssize_t) data_written : (ssize_t) -EAGAIN;
}
ssize_t UARTSerial::read(void* buffer, size_t length)
@ -176,6 +187,10 @@ ssize_t UARTSerial::read(void* buffer, size_t length)
char *ptr = static_cast<char *>(buffer);
if (length == 0) {
return 0;
}
api_lock();
while (_rxbuf.empty()) {

View File

@ -70,6 +70,12 @@ public:
using FileHandle::writable;
/** Write the contents of a buffer to a file
*
* Follows POSIX semantics:
*
* * if blocking, block until all data is written
* * if no data can be written, and non-blocking set, return -EAGAIN
* * if some data can be written, and non-blocking set, write partial
*
* @param buffer The buffer to write from
* @param length The number of bytes to write

View File

@ -105,7 +105,9 @@ nsapi_error_t TCPSocket::connect(const char *host, uint16_t port)
nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
{
_lock.lock();
const uint8_t *data_ptr = static_cast<const uint8_t *>(data);
nsapi_size_or_error_t ret;
nsapi_size_t written = 0;
// If this assert is hit then there are two threads
// performing a send at the same time which is undefined
@ -113,6 +115,9 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
MBED_ASSERT(!_write_in_progress);
_write_in_progress = true;
// Unlike recv, we should write the whole thing if blocking. POSIX only
// allows partial as a side-effect of signal handling; it normally tries to
// write everything if blocking. Without signals we can always write all.
while (true) {
if (!_socket) {
ret = NSAPI_ERROR_NO_SOCKET;
@ -120,10 +125,16 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
}
_pending = 0;
ret = _stack->socket_send(_socket, data, size);
if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK)) {
ret = _stack->socket_send(_socket, data_ptr + written, size - written);
if (ret >= 0) {
written += ret;
if (written >= size) {
break;
}
}
if (_timeout == 0) {
break;
} else {
} else if (ret == NSAPI_ERROR_WOULD_BLOCK) {
uint32_t flag;
// Release lock before blocking so other threads
@ -134,15 +145,22 @@ nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
if (flag & osFlagsError) {
// Timeout break
ret = NSAPI_ERROR_WOULD_BLOCK;
break;
}
} else if (ret < 0) {
break;
}
}
_write_in_progress = false;
_lock.unlock();
return ret;
if (ret <= 0 && ret != NSAPI_ERROR_WOULD_BLOCK) {
return ret;
} else if (written == 0) {
return NSAPI_ERROR_WOULD_BLOCK;
} else {
return written;
}
}
nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size)

View File

@ -88,9 +88,9 @@ public:
* The socket must be connected to a remote host. Returns the number of
* bytes sent from the buffer.
*
* By default, send blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
* By default, send blocks until all data is sent. If socket is set to
* non-blocking or times out, a partial amount can be written.
* NSAPI_ERROR_WOULD_BLOCK is returned if no data was written.
*
* @param data Buffer of data to send to the host
* @param size Size of the buffer in bytes
@ -104,9 +104,9 @@ public:
* The socket must be connected to a remote host. Returns the number of
* bytes received into the buffer.
*
* By default, recv blocks until data is sent. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
* immediately.
* By default, recv blocks until some data is received. If socket is set to
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK can be returned to
* indicate no data.
*
* @param data Destination buffer for data received from the host
* @param size Size of the buffer in bytes

View File

@ -51,7 +51,7 @@ public:
* Devices acting as FileHandles should follow POSIX semantics:
*
* * if no data is available, and non-blocking set return -EAGAIN
* * if no data is available, and blocking set, wait until data is available
* * if no data is available, and blocking set, wait until some data is available
* * If any data is available, call returns immediately
*
* @param buffer The buffer to read in to
@ -61,6 +61,12 @@ public:
virtual ssize_t read(void *buffer, size_t size) = 0;
/** Write the contents of a buffer to a file
*
* Devices acting as FileHandles should follow POSIX semantics:
*
* * if blocking, block until all data is written
* * if no data can be written, and non-blocking set, return -EAGAIN
* * if some data can be written, and non-blocking set, write partial
*
* @param buffer The buffer to write from
* @param size The number of bytes to write