diff -r 1eb586d5b321 Doc/library/fcntl.rst --- a/Doc/library/fcntl.rst Fri Apr 15 17:52:27 2016 +0200 +++ b/Doc/library/fcntl.rst Fri Apr 15 18:27:19 2016 +0200 @@ -108,6 +108,11 @@ The module defines the following functio If the :c:func:`flock` fails, an :exc:`OSError` exception is raised. + .. versionchanged:: 3.6 + If the function is interrupted by a signal and the signal handler does + not raise an exception, the function is now retried. See the :pep:`475` + for the rationale. + .. function:: lockf(fd, cmd, len=0, start=0, whence=0) @@ -140,6 +145,11 @@ The module defines the following functio The default for *len* is 0 which means to lock to the end of the file. The default for *whence* is also 0. + .. versionchanged:: 3.6 + If the function is interrupted by a signal and the signal handler does + not raise an exception, the function is now retried. See the :pep:`475` + for the rationale. + Examples (all on a SVR4 compliant system):: import struct, fcntl, os diff -r 1eb586d5b321 Lib/test/eintrdata/eintr_tester.py --- a/Lib/test/eintrdata/eintr_tester.py Fri Apr 15 17:52:27 2016 +0200 +++ b/Lib/test/eintrdata/eintr_tester.py Fri Apr 15 18:27:19 2016 +0200 @@ -19,6 +19,10 @@ import subprocess import sys import time import unittest +try: + import fcntl +except ImportError: + fcntl = None from test import support @@ -475,13 +479,104 @@ class SelectEINTRTest(EINTRBaseTest): self.assertGreaterEqual(dt, self.sleep_time) +@unittest.skipIf(fcntl is None, "need the fcntl module") +class FcntlEINTRTest(EINTRBaseTest): + def test_flock(self): + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + rpipe, wpipe = os.pipe() + + code = '\n'.join(( + 'import fcntl, os, time', + 'filename = %a' % filename, + 'wpipe = %r' % wpipe, + 'sleep_time = %r' % self.sleep_time, + + 'with open(filename, "wb", 0) as fp:', + + # lock + ' fcntl.flock(fp, fcntl.LOCK_EX)', + + # sync child-parent + ' os.write(wpipe, b"y")', + ' os.close(wpipe)', + + # wait and unlock + ' time.sleep(sleep_time)', + ' fcntl.flock(fp, fcntl.LOCK_UN)', + )) + + with open(filename, "xb", 0) as fp: + proc = self.subprocess(code, pass_fds=(wpipe,)) + os.close(wpipe) + with kill_on_error(proc): + # wait until the child locked the file + os.read(rpipe, 1) + os.close(rpipe) + + # try to lock whereas the child hold the lock + fcntl.flock(fp, fcntl.LOCK_EX) + + # the child released the lock after sleep_time, unlock + # the lock + fcntl.flock(fp, fcntl.LOCK_UN) + + self.assertEqual(proc.wait(), 0) + + def test_lockf(self): + filename = support.TESTFN + self.addCleanup(support.unlink, filename) + + rpipe, wpipe = os.pipe() + + code = '\n'.join(( + 'import fcntl, os, time', + 'filename = %a' % filename, + 'wpipe = %r' % wpipe, + 'sleep_time = %r' % self.sleep_time, + + 'with open(filename, "wb", 0) as fp:', + + # lock + ' fcntl.lockf(fp, fcntl.LOCK_EX)', + + # sync child-parent + ' os.write(wpipe, b"y")', + ' os.close(wpipe)', + + # wait and unlock + ' time.sleep(sleep_time)', + ' fcntl.lockf(fp, fcntl.LOCK_UN)', + )) + + with open(filename, "xb", 0) as fp: + proc = self.subprocess(code, pass_fds=(wpipe,)) + os.close(wpipe) + with kill_on_error(proc): + # wait until the child locked the file + os.read(rpipe, 1) + os.close(rpipe) + + # try to lock whereas the child hold the lock + fcntl.lockf(fp, fcntl.LOCK_EX) + + # the child released the lock after sleep_time, unlock + # the lock + fcntl.lockf(fp, fcntl.LOCK_UN) + + self.assertEqual(proc.wait(), 0) + + def test_main(): support.run_unittest( OSEINTRTest, SocketEINTRTest, TimeEINTRTest, SignalEINTRTest, - SelectEINTRTest) + SelectEINTRTest, + FcntlEINTRTest, + ) if __name__ == "__main__": diff -r 1eb586d5b321 Modules/fcntlmodule.c --- a/Modules/fcntlmodule.c Fri Apr 15 17:52:27 2016 +0200 +++ b/Modules/fcntlmodule.c Fri Apr 15 18:27:19 2016 +0200 @@ -285,9 +285,20 @@ fcntl_flock_impl(PyModuleDef *module, in int ret; #ifdef HAVE_FLOCK - Py_BEGIN_ALLOW_THREADS - ret = flock(fd, code); - Py_END_ALLOW_THREADS + while (1) { + Py_BEGIN_ALLOW_THREADS + ret = flock(fd, code); + Py_END_ALLOW_THREADS + + if (!(ret < 0 && errno == EINTR)) { + break; + } + if (PyErr_CheckSignals()) { + return NULL; + } + /* flock() was interrupted by a signal (failed with EINTR), + retry the call */ + } #else #ifndef LOCK_SH @@ -310,9 +321,21 @@ fcntl_flock_impl(PyModuleDef *module, in return NULL; } l.l_whence = l.l_start = l.l_len = 0; - Py_BEGIN_ALLOW_THREADS - ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); - Py_END_ALLOW_THREADS + + while (1) { + Py_BEGIN_ALLOW_THREADS + ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); + Py_END_ALLOW_THREADS + + if (!(ret < 0 && errno == EINTR)) { + break; + } + if (PyErr_CheckSignals()) { + return NULL; + } + /* fcntl() was interrupted by a signal (failed with EINTR), + retry the call */ + } } #endif /* HAVE_FLOCK */ if (ret < 0) { @@ -407,9 +430,21 @@ fcntl_lockf_impl(PyModuleDef *module, in return NULL; } l.l_whence = whence; - Py_BEGIN_ALLOW_THREADS - ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); - Py_END_ALLOW_THREADS + + while (1) { + Py_BEGIN_ALLOW_THREADS + ret = fcntl(fd, (code & LOCK_NB) ? F_SETLK : F_SETLKW, &l); + Py_END_ALLOW_THREADS + + if (!(ret < 0 && errno == EINTR)) { + break; + } + if (PyErr_CheckSignals()) { + return NULL; + } + /* fcntl() was interrupted by a signal (failed with EINTR), + retry the call */ + } } if (ret < 0) { PyErr_SetFromErrno(PyExc_IOError);