Skip to content

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

Merged
merged 14 commits into from
Jan 27, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
84 changes: 84 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1101,6 +1101,45 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
.. versionadded:: 3.3


.. function:: pwritev(fd, buffers, offset, flags=0)

Combines the functionality of :func:`os.writev` and :func:`os.pwrite`. It
writes the contents of *buffers* to file descriptor *fd* at offset *offset*.
*buffers* must be a sequence of :term:`bytes-like objects <bytes-like object>`.
Buffers are processed in array order. Entire contents of first buffer is written
before proceeding to second, and so on. The operating system may set a limit
(sysconf() value SC_IOV_MAX) on the number of buffers that can be used.
:func:`~os.pwritev` writes the contents of each object to the file descriptor
and returns the total number of bytes written.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a single empty line is enough.

The *flags* argument contains a bitwise OR of zero or more of the following
flags:

- RWF_DSYNC
- RWF_SYNC

Using non-zero flags requires Linux 4.7 or newer.

Availability: Linux (version 2.6.30), FreeBSD 6.0 and newer,
OpenBSD (version 2.7 and newer).

.. versionadded:: 3.7

.. data:: RWF_DSYNC (since Linux 4.7)
Provide a per-write equivalent of the O_DSYNC open(2) flag. This flag
is meaningful only for pwritev2(), and its effect applies only to the
data range written by the system call.

.. versionadded:: 3.7

.. data:: RWF_SYNC (since Linux 4.7)
Provide a per-write equivalent of the O_SYNC open(2) flag. This flag is
meaningful only for pwritev2(), and its effect applies only to the data
range written by the system call.

.. versionadded:: 3.7


.. function:: read(fd, n)

Read at most *n* bytes from file descriptor *fd*. Return a bytestring containing the
Expand Down Expand Up @@ -1195,6 +1234,51 @@ or `the MSDN <https://msdn.microsoft.com/en-us/library/z0kc8e3z.aspx>`_ on Windo
.. versionadded:: 3.3


.. function:: preadv(fd, buffers, offset, flags=0)

Combines the functionality of :func:`os.readv` and :func:`os.pread`. It
reads from a file descriptor *fd* into a number of mutable :term:`bytes-like
objects <bytes-like object>` *buffers*. As :func:`os.readv`, it will transfer
data into each buffer until it is full and then move on to the next buffer in
the sequence to hold the rest of the data. Its fourth argument, *offset*,
specifies the file offset at which the input operation is to be performed.
:func:`~os.preadv` return the total number of bytes read (which can be less than
the total capacity of all the objects).

The flags argument contains a bitwise OR of zero or more of the following
flags:

- RWF_HIPRI
- RWF_NOWAIT

Using non-zero flags requires Linux 4.6 or newer.

Availability: Linux (version 2.6.30), FreeBSD 6.0 and newer,
OpenBSD (version 2.7 and newer).

.. versionadded:: 3.7


.. data:: RWF_HIPRI (since Linux 4.6)
High priority read/write. Allows block-based filesystems to use polling
of the device, which provides lower latency, but may use additional
resources. (Currently, this feature is usable only on a file descriptor
opened using the O_DIRECT flag.)

.. versionadded:: 3.7


.. data:: RWF_NOWAIT (since Linux 4.14)
Do not wait for data which is not immediately available. If this flag
is specified, the preadv2() system call will return instantly
if it would have to read data from the backing storage or wait for a lock.
If some data was successfully read, it will return the number of bytes
read. If no bytes were read, it will return -1 and set errno to EAGAIN.
Currently, this flag is meaningful only for preadv2().

.. versionadded:: 3.7


.. function:: tcgetpgrp(fd)

Return the process group associated with the terminal given by *fd* (an open
Expand Down
50 changes: 50 additions & 0 deletions Lib/test/test_posix.py
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,28 @@ def test_pread(self):
finally:
os.close(fd)

@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'test1tt2t3t5t6t6t8')
buf = [bytearray(i) for i in [5, 3, 2]]
self.assertEqual(posix.preadv(fd, buf, 3), 10)
self.assertEqual([b't1tt2', b't3t', b'5t'], list(buf))
finally:
os.close(fd)

@unittest.skipUnless(hasattr(posix, 'RWF_HIPRI'), "test needs posix.RWF_HIPRI")
def test_preadv_flags(self):
fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT)
try:
os.write(fd, b'test1tt2t3t5t6t6t8')
buf = [bytearray(i) for i in [5, 3, 2]]
self.assertEqual(posix.preadv(fd, buf, 3, os.RWF_HIPRI), 10)
self.assertEqual([b't1tt2', b't3t', b'5t'], list(buf))
finally:
os.close(fd)

@unittest.skipUnless(hasattr(posix, 'pwrite'), "test needs posix.pwrite()")
def test_pwrite(self):
fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT)
Expand All @@ -283,6 +305,34 @@ def test_pwrite(self):
finally:
os.close(fd)

@unittest.skipUnless(hasattr(posix, 'pwritev'), "test needs posix.pwritev()")
def test_pwritev(self):
fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT)
try:
os.write(fd, b"xx")
os.lseek(fd, 0, os.SEEK_SET)
n = os.pwritev(fd, [b'test1', b'tt2', b't3'], 2)
self.assertEqual(n, 10)

os.lseek(fd, 0, os.SEEK_SET)
self.assertEqual(b'xxtest1tt2t3', posix.read(fd, 100))
finally:
os.close(fd)

@unittest.skipUnless(hasattr(posix, 'os.RWF_SYNC'), "test needs os.RWF_SYNC")
def test_pwritev_flags(self):
fd = os.open(support.TESTFN, os.O_RDWR | os.O_CREAT)
try:
os.write(fd,b"xx")
os.lseek(fd, 0, os.SEEK_SET)
n = os.pwritev(fd, [b'test1', b'tt2', b't3'], 2, os.RWF_SYNC)
self.assertEqual(n, 10)

os.lseek(fd, 0, os.SEEK_SET)
self.assertEqual(b'xxtest1tt2', posix.read(fd, 100))
finally:
os.close(fd)

@unittest.skipUnless(hasattr(posix, 'posix_fallocate'),
"test needs posix.posix_fallocate()")
def test_posix_fallocate(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Expose preadv and pwritev system calls in the os module. Patch by Pablo Galindo
120 changes: 119 additions & 1 deletion Modules/clinic/posixmodule.c.h
Original file line number Diff line number Diff line change
Expand Up @@ -3705,6 +3705,61 @@ os_pread(PyObject *module, PyObject *const *args, Py_ssize_t nargs)

#endif /* defined(HAVE_PREAD) */

#if (defined(HAVE_PREADV) || defined (HAVE_PREADV2))

PyDoc_STRVAR(os_preadv__doc__,
"preadv($module, fd, buffers, offset, flags=0, /)\n"
"--\n"
"\n"
"Reads from a file descriptor into a number of mutable bytes-like objects.\n"
"\n"
"Combines the functionality of readv() and pread(). As readv(), it will\n"
"transfer data into each buffer until it is full and then move on to the next\n"
"buffer in the sequence to hold the rest of the data. Its fourth argument,\n"
"specifies the file offset at which the input operation is to be performed. It\n"
"will return the total number of bytes read (which can be less than the total\n"
"capacity of all the objects).\n"
"\n"
"The flags argument contains a bitwise OR of zero or more of the following flags:\n"
"\n"
"- RWF_HIPRI\n"
"- RWF_NOWAIT\n"
"\n"
"Using non-zero flags requires Linux 4.6 or newer.");

#define OS_PREADV_METHODDEF \
{"preadv", (PyCFunction)os_preadv, METH_FASTCALL, os_preadv__doc__},

static Py_ssize_t
os_preadv_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
int flags);

static PyObject *
os_preadv(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
int fd;
PyObject *buffers;
Py_off_t offset;
int flags = 0;
Py_ssize_t _return_value;

if (!_PyArg_ParseStack(args, nargs, "iOO&|i:preadv",
&fd, &buffers, Py_off_t_converter, &offset, &flags)) {
goto exit;
}
_return_value = os_preadv_impl(module, fd, buffers, offset, flags);
if ((_return_value == -1) && PyErr_Occurred()) {
goto exit;
}
return_value = PyLong_FromSsize_t(_return_value);

exit:
return return_value;
}

#endif /* (defined(HAVE_PREADV) || defined (HAVE_PREADV2)) */

PyDoc_STRVAR(os_write__doc__,
"write($module, fd, data, /)\n"
"--\n"
Expand Down Expand Up @@ -3963,6 +4018,61 @@ os_pwrite(PyObject *module, PyObject *const *args, Py_ssize_t nargs)

#endif /* defined(HAVE_PWRITE) */

#if (defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2))

PyDoc_STRVAR(os_pwritev__doc__,
"pwritev($module, fd, buffers, offset, flags=0, /)\n"
"--\n"
"\n"
"Writes the contents of bytes-like objects to a file descriptor at a given offset.\n"
"\n"
"Combines the functionality of writev() and pwrite(). All buffers must be a sequence\n"
"of bytes-like objects. Buffers are processed in array order. Entire contents of first\n"
"buffer is written before proceeding to second, and so on. The operating system may\n"
"set a limit (sysconf() value SC_IOV_MAX) on the number of buffers that can be used.\n"
"This function writes the contents of each object to the file descriptor and returns\n"
"the total number of bytes written.\n"
"\n"
"The flags argument contains a bitwise OR of zero or more of the following flags:\n"
"\n"
"- RWF_DSYNC\n"
"- RWF_SYNC\n"
"\n"
"Using non-zero flags requires Linux 4.7 or newer.");

#define OS_PWRITEV_METHODDEF \
{"pwritev", (PyCFunction)os_pwritev, METH_FASTCALL, os_pwritev__doc__},

static Py_ssize_t
os_pwritev_impl(PyObject *module, int fd, PyObject *buffers, Py_off_t offset,
int flags);

static PyObject *
os_pwritev(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
{
PyObject *return_value = NULL;
int fd;
PyObject *buffers;
Py_off_t offset;
int flags = 0;
Py_ssize_t _return_value;

if (!_PyArg_ParseStack(args, nargs, "iOO&|i:pwritev",
&fd, &buffers, Py_off_t_converter, &offset, &flags)) {
goto exit;
}
_return_value = os_pwritev_impl(module, fd, buffers, offset, flags);
if ((_return_value == -1) && PyErr_Occurred()) {
goto exit;
}
return_value = PyLong_FromSsize_t(_return_value);

exit:
return return_value;
}

#endif /* (defined(HAVE_PWRITEV) || defined (HAVE_PWRITEV2)) */

#if defined(HAVE_MKFIFO)

PyDoc_STRVAR(os_mkfifo__doc__,
Expand Down Expand Up @@ -6239,6 +6349,10 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#define OS_PREAD_METHODDEF
#endif /* !defined(OS_PREAD_METHODDEF) */

#ifndef OS_PREADV_METHODDEF
#define OS_PREADV_METHODDEF
#endif /* !defined(OS_PREADV_METHODDEF) */

#ifndef OS_PIPE_METHODDEF
#define OS_PIPE_METHODDEF
#endif /* !defined(OS_PIPE_METHODDEF) */
Expand All @@ -6255,6 +6369,10 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#define OS_PWRITE_METHODDEF
#endif /* !defined(OS_PWRITE_METHODDEF) */

#ifndef OS_PWRITEV_METHODDEF
#define OS_PWRITEV_METHODDEF
#endif /* !defined(OS_PWRITEV_METHODDEF) */

#ifndef OS_MKFIFO_METHODDEF
#define OS_MKFIFO_METHODDEF
#endif /* !defined(OS_MKFIFO_METHODDEF) */
Expand Down Expand Up @@ -6410,4 +6528,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
#ifndef OS_GETRANDOM_METHODDEF
#define OS_GETRANDOM_METHODDEF
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
/*[clinic end generated code: output=6345053cd5992caf input=a9049054013a1b77]*/
/*[clinic end generated code: output=06ace805893aa10c input=a9049054013a1b77]*/
Loading