Commit b409ffa8 authored by nierob's avatar nierob Committed by Victor Stinner

bpo-35189: Retry fnctl calls on EINTR (GH-10413)

Modify the following fnctl function to retry if interrupted by a signal
(EINTR): flock, lockf, fnctl.
parent f653fd4d
...@@ -10,6 +10,7 @@ sub-second periodicity (contrarily to signal()). ...@@ -10,6 +10,7 @@ sub-second periodicity (contrarily to signal()).
import contextlib import contextlib
import faulthandler import faulthandler
import fcntl
import os import os
import select import select
import signal import signal
...@@ -486,5 +487,43 @@ class SelectEINTRTest(EINTRBaseTest): ...@@ -486,5 +487,43 @@ class SelectEINTRTest(EINTRBaseTest):
self.assertGreaterEqual(dt, self.sleep_time) self.assertGreaterEqual(dt, self.sleep_time)
class FNTLEINTRTest(EINTRBaseTest):
def _lock(self, lock_func, lock_name):
self.addCleanup(support.unlink, support.TESTFN)
code = '\n'.join((
"import fcntl, time",
"with open('%s', 'wb') as f:" % support.TESTFN,
" fcntl.%s(f, fcntl.LOCK_EX)" % lock_name,
" time.sleep(%s)" % self.sleep_time))
start_time = time.monotonic()
proc = self.subprocess(code)
with kill_on_error(proc):
with open(support.TESTFN, 'wb') as f:
while True: # synchronize the subprocess
dt = time.monotonic() - start_time
if dt > 60.0:
raise Exception("failed to sync child in %.1f sec" % dt)
try:
lock_func(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
lock_func(f, fcntl.LOCK_UN)
time.sleep(0.01)
except BlockingIOError:
break
# the child locked the file just a moment ago for 'sleep_time' seconds
# that means that the lock below will block for 'sleep_time' minus some
# potential context switch delay
lock_func(f, fcntl.LOCK_EX)
dt = time.monotonic() - start_time
self.assertGreaterEqual(dt, self.sleep_time)
self.stop_alarm()
proc.wait()
def test_lockf(self):
self._lock(fcntl.lockf, "lockf")
def test_flock(self):
self._lock(fcntl.flock, "flock")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()
Modify the following fnctl function to retry if interrupted by a signal
(EINTR): flock, lockf, fnctl
...@@ -64,6 +64,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) ...@@ -64,6 +64,7 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
char *str; char *str;
Py_ssize_t len; Py_ssize_t len;
char buf[1024]; char buf[1024];
int async_err = 0;
if (arg != NULL) { if (arg != NULL) {
int parse_result; int parse_result;
...@@ -75,12 +76,13 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) ...@@ -75,12 +76,13 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
return NULL; return NULL;
} }
memcpy(buf, str, len); memcpy(buf, str, len);
Py_BEGIN_ALLOW_THREADS do {
ret = fcntl(fd, code, buf); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS ret = fcntl(fd, code, buf);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (ret < 0) { if (ret < 0) {
PyErr_SetFromErrno(PyExc_OSError); return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
return NULL;
} }
return PyBytes_FromStringAndSize(buf, len); return PyBytes_FromStringAndSize(buf, len);
} }
...@@ -95,12 +97,13 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg) ...@@ -95,12 +97,13 @@ fcntl_fcntl_impl(PyObject *module, int fd, int code, PyObject *arg)
} }
} }
Py_BEGIN_ALLOW_THREADS do {
ret = fcntl(fd, code, (int)int_arg); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS ret = fcntl(fd, code, (int)int_arg);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
if (ret < 0) { if (ret < 0) {
PyErr_SetFromErrno(PyExc_OSError); return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
return NULL;
} }
return PyLong_FromLong((long)ret); return PyLong_FromLong((long)ret);
} }
...@@ -283,11 +286,14 @@ fcntl_flock_impl(PyObject *module, int fd, int code) ...@@ -283,11 +286,14 @@ fcntl_flock_impl(PyObject *module, int fd, int code)
/*[clinic end generated code: output=84059e2b37d2fc64 input=b70a0a41ca22a8a0]*/ /*[clinic end generated code: output=84059e2b37d2fc64 input=b70a0a41ca22a8a0]*/
{ {
int ret; int ret;
int async_err = 0;
#ifdef HAVE_FLOCK #ifdef HAVE_FLOCK
Py_BEGIN_ALLOW_THREADS do {
ret = flock(fd, code); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS ret = flock(fd, code);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
#else #else
#ifndef LOCK_SH #ifndef LOCK_SH
...@@ -310,14 +316,15 @@ fcntl_flock_impl(PyObject *module, int fd, int code) ...@@ -310,14 +316,15 @@ fcntl_flock_impl(PyObject *module, int fd, int code)
return NULL; return NULL;
} }
l.l_whence = l.l_start = l.l_len = 0; l.l_whence = l.l_start = l.l_len = 0;
Py_BEGIN_ALLOW_THREADS do {
ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
} }
#endif /* HAVE_FLOCK */ #endif /* HAVE_FLOCK */
if (ret < 0) { if (ret < 0) {
PyErr_SetFromErrno(PyExc_OSError); return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
return NULL;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
} }
...@@ -363,6 +370,7 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, ...@@ -363,6 +370,7 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj,
/*[clinic end generated code: output=4985e7a172e7461a input=3a5dc01b04371f1a]*/ /*[clinic end generated code: output=4985e7a172e7461a input=3a5dc01b04371f1a]*/
{ {
int ret; int ret;
int async_err = 0;
#ifndef LOCK_SH #ifndef LOCK_SH
#define LOCK_SH 1 /* shared lock */ #define LOCK_SH 1 /* shared lock */
...@@ -407,13 +415,14 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj, ...@@ -407,13 +415,14 @@ fcntl_lockf_impl(PyObject *module, int fd, int code, PyObject *lenobj,
return NULL; return NULL;
} }
l.l_whence = whence; l.l_whence = whence;
Py_BEGIN_ALLOW_THREADS do {
ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); Py_BEGIN_ALLOW_THREADS
Py_END_ALLOW_THREADS ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l);
Py_END_ALLOW_THREADS
} while (ret == -1 && errno == EINTR && !(async_err = PyErr_CheckSignals()));
} }
if (ret < 0) { if (ret < 0) {
PyErr_SetFromErrno(PyExc_OSError); return !async_err ? PyErr_SetFromErrno(PyExc_OSError) : NULL;
return NULL;
} }
Py_RETURN_NONE; Py_RETURN_NONE;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment