diff options
| author | David Lechner <david@pybricks.com> | 2020-01-22 23:54:38 -0600 |
|---|---|---|
| committer | Damien George <damien.p.george@gmail.com> | 2020-03-27 14:40:46 +1100 |
| commit | 9418611c8aa860812e28b6e2b6bfde7be5817b43 (patch) | |
| tree | 5e42c1510ea143c8b279641dbae5e1b178edf5b4 /extmod/vfs_posix_file.c | |
| parent | 5e6cee07aba4fd73c13024f091a287171bea1f17 (diff) | |
unix: Implement PEP 475 to retry syscalls failing with EINTR.
https://www.python.org/dev/peps/pep-0475/
This implements something similar to PEP 475 on the unix port, and for the
VfsPosix class.
There are a few differences from the CPython implementation:
- Since we call mp_handle_pending() between any ENITR's, additional
functions could be called if MICROPY_ENABLE_SCHEDULER is enabled, not
just signal handlers.
- CPython only handles signal on the main thread, so other threads will
raise InterruptedError instead of retrying. On MicroPython,
mp_handle_pending() will currently raise exceptions on any thread.
A new macro MP_HAL_RETRY_SYSCALL is introduced to reduce duplicated code
and ensure that all instances behave the same. This will also allow other
ports that use POSIX-like system calls (and use, eg, VfsPosix) to provide
their own implementation if needed.
Diffstat (limited to 'extmod/vfs_posix_file.c')
| -rw-r--r-- | extmod/vfs_posix_file.c | 58 |
1 files changed, 20 insertions, 38 deletions
diff --git a/extmod/vfs_posix_file.c b/extmod/vfs_posix_file.c index ef40eff6a..4604747a8 100644 --- a/extmod/vfs_posix_file.c +++ b/extmod/vfs_posix_file.c @@ -24,6 +24,7 @@ * THE SOFTWARE. */ +#include "py/mphal.h" #include "py/mpthread.h" #include "py/runtime.h" #include "py/stream.h" @@ -102,12 +103,8 @@ mp_obj_t mp_vfs_posix_file_open(const mp_obj_type_t *type, mp_obj_t file_in, mp_ } const char *fname = mp_obj_str_get_str(fid); - MP_THREAD_GIL_EXIT(); - int fd = open(fname, mode_x | mode_rw, 0644); - MP_THREAD_GIL_ENTER(); - if (fd == -1) { - mp_raise_OSError(errno); - } + int fd; + MP_HAL_RETRY_SYSCALL(fd, open(fname, mode_x | mode_rw, 0644), mp_raise_OSError(err)); o->fd = fd; return MP_OBJ_FROM_PTR(o); } @@ -139,14 +136,12 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(vfs_posix_file___exit___obj, 4, 4, vf STATIC mp_uint_t vfs_posix_file_read(mp_obj_t o_in, void *buf, mp_uint_t size, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); - MP_THREAD_GIL_EXIT(); - mp_int_t r = read(o->fd, buf, size); - MP_THREAD_GIL_ENTER(); - if (r == -1) { - *errcode = errno; + ssize_t r; + MP_HAL_RETRY_SYSCALL(r, read(o->fd, buf, size), { + *errcode = err; return MP_STREAM_ERROR; - } - return r; + }); + return (mp_uint_t)r; } STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t size, int *errcode) { @@ -158,46 +153,33 @@ STATIC mp_uint_t vfs_posix_file_write(mp_obj_t o_in, const void *buf, mp_uint_t return size; } #endif - MP_THREAD_GIL_EXIT(); - mp_int_t r = write(o->fd, buf, size); - MP_THREAD_GIL_ENTER(); - while (r == -1 && errno == EINTR) { - if (MP_STATE_VM(mp_pending_exception) != MP_OBJ_NULL) { - mp_obj_t obj = MP_STATE_VM(mp_pending_exception); - MP_STATE_VM(mp_pending_exception) = MP_OBJ_NULL; - nlr_raise(obj); - } - MP_THREAD_GIL_EXIT(); - r = write(o->fd, buf, size); - MP_THREAD_GIL_ENTER(); - } - if (r == -1) { - *errcode = errno; + ssize_t r; + MP_HAL_RETRY_SYSCALL(r, write(o->fd, buf, size), { + *errcode = err; return MP_STREAM_ERROR; - } - return r; + }); + return (mp_uint_t)r; } STATIC mp_uint_t vfs_posix_file_ioctl(mp_obj_t o_in, mp_uint_t request, uintptr_t arg, int *errcode) { mp_obj_vfs_posix_file_t *o = MP_OBJ_TO_PTR(o_in); check_fd_is_open(o); switch (request) { - case MP_STREAM_FLUSH: - MP_THREAD_GIL_EXIT(); - int ret = fsync(o->fd); - MP_THREAD_GIL_ENTER(); - if (ret == -1) { - if (errno == EINVAL + case MP_STREAM_FLUSH: { + int ret; + MP_HAL_RETRY_SYSCALL(ret, fsync(o->fd), { + if (err == EINVAL && (o->fd == STDIN_FILENO || o->fd == STDOUT_FILENO || o->fd == STDERR_FILENO)) { // fsync(stdin/stdout/stderr) may fail with EINVAL, but don't propagate that // error out. Because data is not buffered by us, and stdin/out/err.flush() // should just be a no-op. return 0; } - *errcode = errno; + *errcode = err; return MP_STREAM_ERROR; - } + }); return 0; + } case MP_STREAM_SEEK: { struct mp_stream_seek_t *s = (struct mp_stream_seek_t *)arg; MP_THREAD_GIL_EXIT(); |
