Skip to content

Commit b9c3003

Browse files
Merge pull request #5466 from kjbracey-arm/write_all
Make POSIX-like writes write everything when blocking
2 parents 70522bb + 9678c80 commit b9c3003

File tree

5 files changed

+77
-32
lines changed

5 files changed

+77
-32
lines changed

drivers/UARTSerial.cpp

Lines changed: 35 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -138,36 +138,47 @@ ssize_t UARTSerial::write(const void* buffer, size_t length)
138138
size_t data_written = 0;
139139
const char *buf_ptr = static_cast<const char *>(buffer);
140140

141+
if (length == 0) {
142+
return 0;
143+
}
144+
141145
api_lock();
142146

143-
while (_txbuf.full()) {
144-
if (!_blocking) {
145-
api_unlock();
146-
return -EAGAIN;
147+
// Unlike read, we should write the whole thing if blocking. POSIX only
148+
// allows partial as a side-effect of signal handling; it normally tries to
149+
// write everything if blocking. Without signals we can always write all.
150+
while (data_written < length) {
151+
152+
if (_txbuf.full()) {
153+
if (!_blocking) {
154+
break;
155+
}
156+
do {
157+
api_unlock();
158+
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
159+
api_lock();
160+
} while (_txbuf.full());
147161
}
148-
api_unlock();
149-
wait_ms(1); // XXX todo - proper wait, WFE for non-rtos ?
150-
api_lock();
151-
}
152162

153-
while (data_written < length && !_txbuf.full()) {
154-
_txbuf.push(*buf_ptr++);
155-
data_written++;
156-
}
163+
while (data_written < length && !_txbuf.full()) {
164+
_txbuf.push(*buf_ptr++);
165+
data_written++;
166+
}
157167

158-
core_util_critical_section_enter();
159-
if (!_tx_irq_enabled) {
160-
UARTSerial::tx_irq(); // only write to hardware in one place
161-
if (!_txbuf.empty()) {
162-
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
163-
_tx_irq_enabled = true;
168+
core_util_critical_section_enter();
169+
if (!_tx_irq_enabled) {
170+
UARTSerial::tx_irq(); // only write to hardware in one place
171+
if (!_txbuf.empty()) {
172+
SerialBase::attach(callback(this, &UARTSerial::tx_irq), TxIrq);
173+
_tx_irq_enabled = true;
174+
}
164175
}
176+
core_util_critical_section_exit();
165177
}
166-
core_util_critical_section_exit();
167178

168179
api_unlock();
169180

170-
return data_written;
181+
return data_written != 0 ? (ssize_t) data_written : (ssize_t) -EAGAIN;
171182
}
172183

173184
ssize_t UARTSerial::read(void* buffer, size_t length)
@@ -176,6 +187,10 @@ ssize_t UARTSerial::read(void* buffer, size_t length)
176187

177188
char *ptr = static_cast<char *>(buffer);
178189

190+
if (length == 0) {
191+
return 0;
192+
}
193+
179194
api_lock();
180195

181196
while (_rxbuf.empty()) {

drivers/UARTSerial.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ class UARTSerial : private SerialBase, public FileHandle, private NonCopyable<UA
7070
using FileHandle::writable;
7171

7272
/** Write the contents of a buffer to a file
73+
*
74+
* Follows POSIX semantics:
75+
*
76+
* * if blocking, block until all data is written
77+
* * if no data can be written, and non-blocking set, return -EAGAIN
78+
* * if some data can be written, and non-blocking set, write partial
7379
*
7480
* @param buffer The buffer to write from
7581
* @param length The number of bytes to write

features/netsocket/TCPSocket.cpp

Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,25 +105,36 @@ nsapi_error_t TCPSocket::connect(const char *host, uint16_t port)
105105
nsapi_size_or_error_t TCPSocket::send(const void *data, nsapi_size_t size)
106106
{
107107
_lock.lock();
108+
const uint8_t *data_ptr = static_cast<const uint8_t *>(data);
108109
nsapi_size_or_error_t ret;
110+
nsapi_size_t written = 0;
109111

110112
// If this assert is hit then there are two threads
111113
// performing a send at the same time which is undefined
112114
// behavior
113115
MBED_ASSERT(!_write_in_progress);
114116
_write_in_progress = true;
115117

118+
// Unlike recv, we should write the whole thing if blocking. POSIX only
119+
// allows partial as a side-effect of signal handling; it normally tries to
120+
// write everything if blocking. Without signals we can always write all.
116121
while (true) {
117122
if (!_socket) {
118123
ret = NSAPI_ERROR_NO_SOCKET;
119124
break;
120125
}
121126

122127
_pending = 0;
123-
ret = _stack->socket_send(_socket, data, size);
124-
if ((_timeout == 0) || (ret != NSAPI_ERROR_WOULD_BLOCK)) {
128+
ret = _stack->socket_send(_socket, data_ptr + written, size - written);
129+
if (ret >= 0) {
130+
written += ret;
131+
if (written >= size) {
132+
break;
133+
}
134+
}
135+
if (_timeout == 0) {
125136
break;
126-
} else {
137+
} else if (ret == NSAPI_ERROR_WOULD_BLOCK) {
127138
uint32_t flag;
128139

129140
// 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)
134145

135146
if (flag & osFlagsError) {
136147
// Timeout break
137-
ret = NSAPI_ERROR_WOULD_BLOCK;
138148
break;
139149
}
150+
} else if (ret < 0) {
151+
break;
140152
}
141153
}
142154

143155
_write_in_progress = false;
144156
_lock.unlock();
145-
return ret;
157+
if (ret <= 0 && ret != NSAPI_ERROR_WOULD_BLOCK) {
158+
return ret;
159+
} else if (written == 0) {
160+
return NSAPI_ERROR_WOULD_BLOCK;
161+
} else {
162+
return written;
163+
}
146164
}
147165

148166
nsapi_size_or_error_t TCPSocket::recv(void *data, nsapi_size_t size)

features/netsocket/TCPSocket.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -88,9 +88,9 @@ class TCPSocket : public Socket {
8888
* The socket must be connected to a remote host. Returns the number of
8989
* bytes sent from the buffer.
9090
*
91-
* By default, send blocks until data is sent. If socket is set to
92-
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
93-
* immediately.
91+
* By default, send blocks until all data is sent. If socket is set to
92+
* non-blocking or times out, a partial amount can be written.
93+
* NSAPI_ERROR_WOULD_BLOCK is returned if no data was written.
9494
*
9595
* @param data Buffer of data to send to the host
9696
* @param size Size of the buffer in bytes
@@ -104,9 +104,9 @@ class TCPSocket : public Socket {
104104
* The socket must be connected to a remote host. Returns the number of
105105
* bytes received into the buffer.
106106
*
107-
* By default, recv blocks until data is sent. If socket is set to
108-
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK is returned
109-
* immediately.
107+
* By default, recv blocks until some data is received. If socket is set to
108+
* non-blocking or times out, NSAPI_ERROR_WOULD_BLOCK can be returned to
109+
* indicate no data.
110110
*
111111
* @param data Destination buffer for data received from the host
112112
* @param size Size of the buffer in bytes

platform/FileHandle.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ class FileHandle : private NonCopyable<FileHandle> {
5151
* Devices acting as FileHandles should follow POSIX semantics:
5252
*
5353
* * if no data is available, and non-blocking set return -EAGAIN
54-
* * if no data is available, and blocking set, wait until data is available
54+
* * if no data is available, and blocking set, wait until some data is available
5555
* * If any data is available, call returns immediately
5656
*
5757
* @param buffer The buffer to read in to
@@ -61,6 +61,12 @@ class FileHandle : private NonCopyable<FileHandle> {
6161
virtual ssize_t read(void *buffer, size_t size) = 0;
6262

6363
/** Write the contents of a buffer to a file
64+
*
65+
* Devices acting as FileHandles should follow POSIX semantics:
66+
*
67+
* * if blocking, block until all data is written
68+
* * if no data can be written, and non-blocking set, return -EAGAIN
69+
* * if some data can be written, and non-blocking set, write partial
6470
*
6571
* @param buffer The buffer to write from
6672
* @param size The number of bytes to write

0 commit comments

Comments
 (0)