Repository navigation
bpo-31368: Expose preadv (preadv2) and pwritev (pwritev2) in the os module #5239
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
ea21128
4570976
21a3004
8a2a2a3
c0c53dc
4ddbca9
cac62ac
c7b7285
58910d0
f7cdf22
47f65b5
87c6970
cb74d38
5563b8d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -272,18 +272,29 @@ def test_pread(self): | |
| finally: | ||
| os.close(fd) | ||
|
|
||
| @unittest.skipUnless(hasattr(posix, 'preadv2'), "test needs posix.preadv2()") | ||
| def test_preadv2(self): | ||
| @unittest.skipUnless(hasattr(posix, 'preadv'), "test needs posix.preadv()") | ||
| def test_preadv(self): | ||
| fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) | ||
| try: | ||
| os.write(fd, b'test1tt2t3') | ||
| os.lseek(fd, 0, os.SEEK_SET) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hum, is it really needed to seek back at offset 0? It would be more interesting to test preadv() with an offset different than the current offset. |
||
| buf = [bytearray(i) for i in [5, 3, 2]] | ||
| self.assertEqual(posix.preadv2(fd, buf, 0, os.RWF_SYNC), 10) | ||
| self.assertEqual(posix.preadv(fd, buf, 0), 10) | ||
| self.assertEqual([b'test1', b'tt2', b't3'], [bytes(i) for i in buf]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure that bytes() cast is needed here. Just write list(buf) for the second argument? |
||
| finally: | ||
| os.close(fd) | ||
|
|
||
| @unittest.skipUnless(hasattr(posix, 'RWF_SYNC'), "test needs posix.preadv2()") | ||
| def test_preadv2(self): | ||
| fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT) | ||
| try: | ||
| os.write(fd, b'test1tt2t3') | ||
| os.lseek(fd, 0, os.SEEK_SET) | ||
| buf = [bytearray(i) for i in [5, 3, 2]] | ||
| self.assertEqual(posix.preadv(fd, buf, 0, os.RWF_SYNC), 10) | ||
| self.assertEqual([b'test1', b'tt2', b't3'], [bytes(i) for i in buf]) | ||
| finally: | ||
| os.close(fd) | ||
|
|
||
| @unittest.skipUnless(hasattr(posix, 'pwrite'), "test needs posix.pwrite()") | ||
| def test_pwrite(self): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8163,14 +8163,14 @@ os_pread_impl(PyObject *module, int fd, int length, Py_off_t offset) | |
| } | ||
| #endif /* HAVE_PREAD */ | ||
|
|
||
| #ifdef HAVE_PREADV2 | ||
| #if defined(HAVE_PREADV) || defined (HAVE_PREADV2) | ||
| /*[clinic input] | ||
| os.preadv2 -> Py_ssize_t | ||
| os.preadv -> Py_ssize_t | ||
|
|
||
| fd: int | ||
| buffers: object | ||
| offset: Py_off_t | ||
| flags: int | ||
| flags: int = 0 | ||
| / | ||
|
|
||
| Read a number of bytes from a file descriptor starting at a particular offset. | ||
|
|
@@ -8180,9 +8180,9 @@ the beginning of the file. The file offset remains unchanged. | |
| [clinic start generated code]*/ | ||
|
|
||
| static Py_ssize_t | ||
| os_preadv2_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, | ||
| int flags) | ||
| /*[clinic end generated code: output=fd040c20cf504631 input=f4a633d90005eb65]*/ | ||
| os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, | ||
| int flags) | ||
| /*[clinic end generated code: output=26fc9c6e58e7ada5 input=5b07b7e2d1825627]*/ | ||
| { | ||
| Py_ssize_t cnt, n; | ||
| int async_err = 0; | ||
|
|
@@ -8201,12 +8201,23 @@ os_preadv2_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, | |
|
|
||
| if (iov_setup(&iov, &buf, buffers, cnt, PyBUF_WRITABLE) < 0) | ||
| return -1; | ||
|
|
||
| #ifdef HAVE_PREADV2 | ||
| do { | ||
| Py_BEGIN_ALLOW_THREADS | ||
| n = preadv2(fd, iov, cnt, offset, flags); | ||
| Py_END_ALLOW_THREADS | ||
| } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do you know if it's safe to call preadv2() again if it fals with EINTR?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not completely sure. I assumed that it is the case because is implemented internally using
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't worry. It's not really our issue for make sure that preadv()/preadv2() can be called again with same arguments on EINTR. The kernel and libc must make that doable and safe.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't that how literally every syscall except |
||
| #else | ||
| if(flags != 0){ | ||
| PyErr_SetString(PyExc_OSError, "preadv2() not available in this system"); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. PyExc_NotImplementedError is preferred on such case: maybe reuse argument_unavailable_error()? |
||
| return -1; | ||
| } | ||
| do { | ||
| Py_BEGIN_ALLOW_THREADS | ||
| n = preadv(fd, iov, cnt, offset); | ||
| Py_END_ALLOW_THREADS | ||
| } while (n < 0 && errno == EINTR && !(async_err = PyErr_CheckSignals())); | ||
| #endif | ||
|
|
||
| iov_cleanup(iov, buf, cnt); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we exit early (e.g. due to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have corrected it in 4ddbca9. I could not use easily a goto label to have a clean exit section due to the check for async error in the signal handler so I just clean before exiting if |
||
| if (n < 0) { | ||
|
|
@@ -8217,9 +8228,7 @@ os_preadv2_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset, | |
|
|
||
| return n; | ||
| } | ||
|
|
||
|
|
||
| #endif /* HAVE PREADV2 */ | ||
| #endif /* HAVE_PREADV2 */ | ||
|
|
||
|
|
||
| /*[clinic input] | ||
|
|
@@ -12564,7 +12573,7 @@ static PyMethodDef posix_methods[] = { | |
| OS_READ_METHODDEF | ||
| OS_READV_METHODDEF | ||
| OS_PREAD_METHODDEF | ||
| OS_PREADV2_METHODDEF | ||
| OS_PREADV_METHODDEF | ||
| OS_WRITE_METHODDEF | ||
| OS_WRITEV_METHODDEF | ||
| OS_PWRITE_METHODDEF | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please write more than 10 bytes, to make sure that preadv2() doesn't read more than expected.