Skip to content

Commit

Permalink
bpo-31368: Expose preadv and pwritev in the os module (python#5239)
Browse files Browse the repository at this point in the history
  • Loading branch information
pablogsal authored and vstinner committed Jan 27, 2018
1 parent 60da99b commit 4defba3
Show file tree
Hide file tree
Showing 9 changed files with 470 additions and 79 deletions.
84 changes: 84 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,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.

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 @@ -1196,6 +1235,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

0 comments on commit 4defba3

Please sign in to comment.