Skip to content

Commit 3bca93b

Browse files
mvds00dpgeorge
authored andcommitted
ports: Fix sys.stdout.buffer.write() return value.
MicroPython code may rely on the return value of sys.stdout.buffer.write() to reflect the number of bytes actually written. While in most scenarios a write() operation is successful, there are cases where it fails, leading to data loss. This problem arises because, currently, write() merely returns the number of bytes it was supposed to write, without indication of failure. One scenario where write() might fail, is where USB is used and the receiving end doesn't read quickly enough to empty the receive buffer. In that case, write() on the MicroPython side can timeout, resulting in the loss of data without any indication, a behavior observed notably in communication between a Pi Pico as a client and a Linux host using the ACM driver. A complex issue arises with mp_hal_stdout_tx_strn() when it involves multiple outputs, such as USB, dupterm and hardware UART. The challenge is in handling cases where writing to one output is successful, but another fails, either fully or partially. This patch implements the following solution: mp_hal_stdout_tx_strn() attempts to write len bytes to all of the possible destinations for that data, and returns the minimum successful write length. The implementation of this is complicated by several factors: - multiple outputs may be enabled or disabled at compiled time - multiple outputs may be enabled or disabled at runtime - mp_os_dupterm_tx_strn() is one such output, optionally containing multiple additional outputs - each of these outputs may or may not be able to report success - each of these outputs may or may not be able to report partial writes As a result, there's no single strategy that fits all ports, necessitating unique logic for each instance of mp_hal_stdout_tx_strn(). Note that addressing sys.stdout.write() is more complex due to its data modification process ("cooked" output), and it remains unchanged in this patch. Developers who are concerned about accurate return values from write operations should use sys.stdout.buffer.write(). This patch might disrupt some existing code, but it's also expected to resolve issues, considering that the peculiar return value behavior of sys.stdout.buffer.write() is not well-documented and likely not widely known. Therefore, it's improbable that much existing code relies on the previous behavior. Signed-off-by: Maarten van der Schrieck <maarten@thingsconnected.nl>
1 parent 91ee8ac commit 3bca93b

File tree

22 files changed

+136
-40
lines changed

22 files changed

+136
-40
lines changed

ports/cc3200/hal/cc3200_hal.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,15 @@ void mp_hal_delay_ms(mp_uint_t delay) {
140140
}
141141
}
142142

143-
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
144-
mp_os_dupterm_tx_strn(str, len);
143+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
144+
mp_uint_t ret = len;
145+
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
146+
if (dupterm_res >= 0) {
147+
ret = dupterm_res;
148+
}
145149
// and also to telnet
146150
telnet_tx_strn(str, len);
151+
return ret;
147152
}
148153

149154
int mp_hal_stdin_rx_chr(void) {

ports/esp32/mphalport.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,24 +125,34 @@ int mp_hal_stdin_rx_chr(void) {
125125
}
126126
}
127127

128-
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
128+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
129129
// Only release the GIL if many characters are being sent
130+
mp_uint_t ret = len;
131+
bool did_write = false;
130132
bool release_gil = len > 20;
131133
if (release_gil) {
132134
MP_THREAD_GIL_EXIT();
133135
}
134136
#if CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG
135137
usb_serial_jtag_tx_strn(str, len);
138+
did_write = true;
136139
#elif CONFIG_USB_OTG_SUPPORTED
137140
usb_tx_strn(str, len);
141+
did_write = true;
138142
#endif
139143
#if MICROPY_HW_ENABLE_UART_REPL
140144
uart_stdout_tx_strn(str, len);
145+
did_write = true;
141146
#endif
142147
if (release_gil) {
143148
MP_THREAD_GIL_ENTER();
144149
}
145-
mp_os_dupterm_tx_strn(str, len);
150+
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
151+
if (dupterm_res >= 0) {
152+
did_write = true;
153+
ret = MIN((mp_uint_t)dupterm_res, ret);
154+
}
155+
return did_write ? ret : 0;
146156
}
147157

148158
uint32_t mp_hal_ticks_ms(void) {

ports/esp8266/esp_mphal.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,14 @@ void mp_hal_debug_str(const char *str) {
9494
}
9595
#endif
9696

97-
void mp_hal_stdout_tx_strn(const char *str, uint32_t len) {
98-
mp_os_dupterm_tx_strn(str, len);
97+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, uint32_t len) {
98+
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
99+
if (dupterm_res < 0) {
100+
// no outputs, nothing was written
101+
return 0;
102+
} else {
103+
return dupterm_res;
104+
}
99105
}
100106

101107
void mp_hal_debug_tx_strn_cooked(void *env, const char *str, uint32_t len) {

ports/mimxrt/mphalport.c

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,12 @@ int mp_hal_stdin_rx_chr(void) {
112112
}
113113
}
114114

115-
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
115+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
116+
mp_uint_t ret = len;
117+
bool did_write = false;
116118
if (tud_cdc_connected()) {
117-
for (size_t i = 0; i < len;) {
119+
size_t i = 0;
120+
while (i < len) {
118121
uint32_t n = len - i;
119122
if (n > CFG_TUD_CDC_EP_BUFSIZE) {
120123
n = CFG_TUD_CDC_EP_BUFSIZE;
@@ -125,17 +128,25 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
125128
MICROPY_EVENT_POLL_HOOK
126129
}
127130
if (ticks_us64() >= timeout) {
131+
ret = i;
128132
break;
129133
}
130134

131135
uint32_t n2 = tud_cdc_write(str + i, n);
132136
tud_cdc_write_flush();
133137
i += n2;
134138
}
139+
did_write = true;
140+
ret = MIN(i, ret);
135141
}
136142
#if MICROPY_PY_OS_DUPTERM
137-
mp_os_dupterm_tx_strn(str, len);
143+
int dupterm_res = mp_os_dupterm_tx_strn(str, len);
144+
if (dupterm_res >= 0) {
145+
did_write = true;
146+
ret = MIN((mp_uint_t)dupterm_res, ret);
147+
}
138148
#endif
149+
return did_write ? ret : 0;
139150
}
140151

141152
uint64_t mp_hal_time_ns(void) {

ports/minimal/uart_core.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,14 @@ int mp_hal_stdin_rx_chr(void) {
2929
}
3030

3131
// Send string of given length
32-
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
32+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
33+
mp_uint_t ret = len;
3334
#if MICROPY_MIN_USE_STDOUT
3435
int r = write(STDOUT_FILENO, str, len);
35-
(void)r;
36+
if (r >= 0) {
37+
// in case of an error in the syscall, report no bytes written
38+
ret = 0;
39+
}
3640
#elif MICROPY_MIN_USE_STM32_MCU
3741
while (len--) {
3842
// wait for TXE
@@ -41,4 +45,5 @@ void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
4145
USART1->DR = *str++;
4246
}
4347
#endif
48+
return ret;
4449
}

ports/nrf/drivers/bluetooth/ble_uart.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,10 +110,11 @@ int mp_hal_stdin_rx_chr(void) {
110110
return (int)byte;
111111
}
112112

113-
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
113+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
114114
// Not connected: drop output
115-
if (!ble_uart_enabled()) return;
115+
if (!ble_uart_enabled()) return 0;
116116

117+
mp_uint_t ret = len;
117118
uint8_t *buf = (uint8_t *)str;
118119
size_t send_len;
119120

@@ -134,6 +135,7 @@ void mp_hal_stdout_tx_strn(const char *str, size_t len) {
134135
len -= send_len;
135136
buf += send_len;
136137
}
138+
return ret;
137139
}
138140

139141
void ble_uart_tx_char(char c) {

ports/nrf/drivers/usb/usb_cdc.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -231,12 +231,12 @@ int mp_hal_stdin_rx_chr(void) {
231231
return 0;
232232
}
233233

234-
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
235-
234+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
236235
for (const char *top = str + len; str < top; str++) {
237236
ringbuf_put((ringbuf_t*)&tx_ringbuf, *str);
238237
usb_cdc_loop();
239238
}
239+
return len;
240240
}
241241

242242
void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) {

ports/nrf/mphalport.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,10 +196,12 @@ int mp_hal_stdin_rx_chr(void) {
196196
return 0;
197197
}
198198

199-
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
199+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
200200
if (MP_STATE_VM(dupterm_objs[0]) != MP_OBJ_NULL) {
201201
uart_tx_strn(MP_STATE_VM(dupterm_objs[0]), str, len);
202+
return len;
202203
}
204+
return 0;
203205
}
204206

205207
void mp_hal_stdout_tx_strn_cooked(const char *str, mp_uint_t len) {

ports/pic16bit/pic16bit_mphal.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,12 @@ void mp_hal_stdout_tx_str(const char *str) {
7575
mp_hal_stdout_tx_strn(str, strlen(str));
7676
}
7777

78-
void mp_hal_stdout_tx_strn(const char *str, size_t len) {
78+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
79+
mp_uint_t ret = len;
7980
for (; len > 0; --len) {
8081
uart_tx_char(*str++);
8182
}
83+
return ret;
8284
}
8385

8486
void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) {

ports/powerpc/uart_lpc_serial.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,13 @@ int mp_hal_stdin_rx_chr(void) {
108108
}
109109

110110

111-
void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
111+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) {
112112
int i;
113113
for (i = 0; i < len; i++) {
114114
while (lpc_uart_tx_full()) {
115115
;
116116
}
117117
lpc_uart_reg_write(REG_RBR, str[i]);
118118
}
119+
return len;
119120
}

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy