[Libguestfs] [PATCH libnbd 1/5] python: Change aio_buffer into nbd.Buffer class.

Richard W.M. Jones rjones at redhat.com
Sat Aug 10 17:08:56 UTC 2019


On reflection I have pushed this patch because it's an improvement to
the current Python bindings and pretty much a change we would always
want to make no matter how we deal with the buffer lifetimes issue.

The rest of the series however, same as I wrote in the cover letter.

On Sat, Aug 10, 2019 at 06:02:56PM +0100, Richard W.M. Jones wrote:
> Create a class for AIO buffers.  This is a mostly neutral code
> refactoring, but we add a convenient function for getting the size of
> the buffer (same as previous commit for OCaml).
> ---
>  generator/generator                | 111 +++++++++++++++++------------
>  python/handle.c                    |  26 +++++--
>  python/t/500-aio-pread.py          |   4 +-
>  python/t/505-aio-pread-callback.py |  10 +--
>  python/t/510-aio-pwrite.py         |   6 +-
>  5 files changed, 96 insertions(+), 61 deletions(-)
> 
> diff --git a/generator/generator b/generator/generator
> index 26ab365..0107724 100755
> --- a/generator/generator
> +++ b/generator/generator
> @@ -3981,8 +3981,10 @@ raise_exception ()
>        pr "extern PyObject *nbd_internal_py_%s (PyObject *self, PyObject *args);\n"
>           name;
>    ) ([ "create"; "close";
> -       "alloc_aio_buffer"; "aio_buffer_from_bytearray";
> -       "aio_buffer_to_bytearray" ] @ List.map fst handle_calls);
> +       "alloc_aio_buffer";
> +       "aio_buffer_from_bytearray";
> +       "aio_buffer_to_bytearray";
> +       "aio_buffer_size" ] @ List.map fst handle_calls);
>  
>    pr "\n";
>    pr "#endif /* LIBNBD_METHODS_H */\n"
> @@ -4009,8 +4011,10 @@ let generate_python_libnbdmod_c () =
>        pr "  { (char *) \"%s\", nbd_internal_py_%s, METH_VARARGS, NULL },\n"
>           name name;
>    ) ([ "create"; "close";
> -       "alloc_aio_buffer"; "aio_buffer_from_bytearray";
> -       "aio_buffer_to_bytearray" ] @ List.map fst handle_calls);
> +       "alloc_aio_buffer";
> +       "aio_buffer_from_bytearray";
> +       "aio_buffer_to_bytearray";
> +       "aio_buffer_size" ] @ List.map fst handle_calls);
>    pr "  { NULL, NULL, 0, NULL }\n";
>    pr "};\n";
>    pr "\n";
> @@ -4502,17 +4506,28 @@ Error.__str__ = _str
>  
>    pr "\
>  
> -# AIO buffer functions.
> -def aio_buffer (len):
> -    '''allocate an AIO buffer used for nbd.aio_pread and nbd.aio_pwrite'''
> -    return libnbdmod.alloc_aio_buffer (len)
> -
> -def aio_buffer_from_bytearray (ba):
> -    '''create an AIO buffer from a bytearray'''
> -    return libnbdmod.aio_buffer_from_bytearray (ba)
> -def aio_buffer_to_bytearray (buf):
> -    '''copy an AIO buffer into a bytearray'''
> -    return libnbdmod.aio_buffer_to_bytearray (buf)
> +class Buffer (object):
> +    '''Asynchronous I/O persistent buffer'''
> +
> +    def __init__ (self, len):
> +        '''allocate an uninitialized AIO buffer used for nbd.aio_pread'''
> +        self._o = libnbdmod.alloc_aio_buffer (len)
> +
> +    @classmethod
> +    def from_bytearray (cls, ba):
> +        '''create an AIO buffer from a bytearray'''
> +        o = libnbdmod.aio_buffer_from_bytearray (ba)
> +        self = cls (0)
> +        self._o = o
> +        return self
> +
> +    def to_bytearray (self):
> +        '''copy an AIO buffer into a bytearray'''
> +        return libnbdmod.aio_buffer_to_bytearray (self._o)
> +
> +    def size (self):
> +        '''return the size of an AIO buffer'''
> +        return libnbdmod.aio_buffer_size (self._o)
>  
>  class NBD (object):
>      '''NBD handle'''
> @@ -4532,42 +4547,46 @@ class NBD (object):
>        let args =
>          List.map (
>            function
> -          | Bool n -> n
> -          | BytesIn (n, _) | BytesPersistIn (n, _) -> n
> -          | BytesPersistOut (n, _) -> n
> -          | BytesOut (_, count) -> count
> -          | Closure { cbname } -> cbname
> -          | Int n -> n
> -          | Int64 n -> n
> -          | Path n -> n
> -          | SockAddrAndLen (n, _) -> n
> -          | String n -> n
> -          | StringList n -> n
> -          | UInt n -> n
> -          | UInt32 n -> n
> -          | UInt64 n -> n
> +          | Bool n -> n, None, None
> +          | BytesIn (n, _) -> n, None, None
> +          | BytesOut (_, count) -> count, None, None
> +          | BytesPersistIn (n, _) -> n, None, Some (sprintf "%s._o" n)
> +          | BytesPersistOut (n, _) -> n, None, Some (sprintf "%s._o" n)
> +          | Closure { cbname } -> cbname, None, None
> +          | Int n -> n, None, None
> +          | Int64 n -> n, None, None
> +          | Path n -> n, None, None
> +          | SockAddrAndLen (n, _) -> n, None, None
> +          | String n -> n, None, None
> +          | StringList n -> n, None, None
> +          | UInt n -> n, None, None
> +          | UInt32 n -> n, None, None
> +          | UInt64 n -> n, None, None
>          ) args in
>        let optargs =
>          List.map (
>            function
> -          | OFlags n -> n, "0"
> +          | OFlags n -> n, Some "0", None
>          ) optargs in
> -      let () =
> -        let params = args @ List.map (fun (n, def) -> n ^ "=" ^ def) optargs in
> -        let params = List.map ((^) ", ") params in
> -        let params = String.concat "" params in
> -        pr "    def %s (self%s):\n" name params in
> -      let () =
> -        let longdesc = Str.global_replace py_fn_rex "C<nbd." longdesc in
> -        let longdesc = Str.global_replace py_const_rex "C<" longdesc in
> -        let longdesc = pod2text longdesc in
> -        pr "        '''▶ %s\n\n%s'''\n"
> -           shortdesc (String.concat "\n" longdesc) in
> -      let () =
> -        let vars = args @ List.map fst optargs in
> -        let vars = List.map ((^) ", ") vars in
> -        let vars = String.concat "" vars in
> -        pr "        return libnbdmod.%s (self._o%s)\n" name vars in
> +      let args = args @ optargs in
> +      pr "    def %s (self" name;
> +      List.iter (
> +        function
> +        | n, None, _ -> pr ", %s" n
> +        | n, Some default, _ -> pr ", %s=%s" n default
> +      ) args;
> +      pr "):\n";
> +      let longdesc = Str.global_replace py_fn_rex "C<nbd." longdesc in
> +      let longdesc = Str.global_replace py_const_rex "C<" longdesc in
> +      let longdesc = pod2text longdesc in
> +      pr "        '''▶ %s\n\n%s'''\n" shortdesc (String.concat "\n" longdesc);
> +      pr "        return libnbdmod.%s (self._o" name;
> +      List.iter (
> +        function
> +        | _, _, Some getter -> pr ", %s" getter
> +        | n, _, None -> pr ", %s" n
> +      ) args;
> +      pr ")\n";
>        pr "\n"
>    ) handle_calls;
>  
> diff --git a/python/handle.c b/python/handle.c
> index 7cff41a..4b510ad 100644
> --- a/python/handle.c
> +++ b/python/handle.c
> @@ -72,7 +72,7 @@ nbd_internal_py_close (PyObject *self, PyObject *args)
>    return Py_None;
>  }
>  
> -static char aio_buffer_name[] = "struct py_aio_buffer";
> +static const char aio_buffer_name[] = "nbd.Buffer";
>  
>  struct py_aio_buffer *
>  nbd_internal_py_get_aio_buffer (PyObject *capsule)
> @@ -89,9 +89,7 @@ free_aio_buffer (PyObject *capsule)
>    free (buf);
>  }
>  
> -/* Allocate a persistent buffer used for nbd_aio_pread and
> - * nbd_aio_pwrite.
> - */
> +/* Allocate a persistent buffer used for nbd_aio_pread. */
>  PyObject *
>  nbd_internal_py_alloc_aio_buffer (PyObject *self, PyObject *args)
>  {
> @@ -115,7 +113,7 @@ nbd_internal_py_alloc_aio_buffer (PyObject *self, PyObject *args)
>      free (buf);
>      return NULL;
>    }
> -  buf->data = calloc (1, buf->len);
> +  buf->data = malloc (buf->len);
>    if (buf->data == NULL) {
>      PyErr_NoMemory ();
>      free (buf);
> @@ -193,3 +191,21 @@ nbd_internal_py_aio_buffer_to_bytearray (PyObject *self, PyObject *args)
>  
>    return PyByteArray_FromStringAndSize (buf->data, buf->len);
>  }
> +
> +PyObject *
> +nbd_internal_py_aio_buffer_size (PyObject *self, PyObject *args)
> +{
> +  PyObject *obj;
> +  struct py_aio_buffer *buf;
> +
> +  if (!PyArg_ParseTuple (args,
> +                         (char *) "O:nbd_internal_py_aio_buffer_size",
> +                         &obj))
> +    return NULL;
> +
> +  buf = nbd_internal_py_get_aio_buffer (obj);
> +  if (buf == NULL)
> +    return NULL;
> +
> +  return PyLong_FromSsize_t (buf->len);
> +}
> diff --git a/python/t/500-aio-pread.py b/python/t/500-aio-pread.py
> index 6ff06fd..0c5a07a 100644
> --- a/python/t/500-aio-pread.py
> +++ b/python/t/500-aio-pread.py
> @@ -20,12 +20,12 @@ import nbd
>  h = nbd.NBD ()
>  h.connect_command (["nbdkit", "-s", "--exit-with-parent", "-v",
>                      "pattern", "size=512"])
> -buf = nbd.aio_buffer (512)
> +buf = nbd.Buffer (512)
>  cookie = h.aio_pread (buf, 0)
>  while not (h.aio_command_completed (cookie)):
>      h.poll (-1)
>  
> -buf = nbd.aio_buffer_to_bytearray (buf)
> +buf = buf.to_bytearray ()
>  
>  print ("%r" % buf)
>  
> diff --git a/python/t/505-aio-pread-callback.py b/python/t/505-aio-pread-callback.py
> index 9246616..e552db8 100644
> --- a/python/t/505-aio-pread-callback.py
> +++ b/python/t/505-aio-pread-callback.py
> @@ -43,21 +43,21 @@ def callback (user_data, err):
>      assert user_data == 42
>  
>  # First try: succeed in both callbacks
> -buf = nbd.aio_buffer (512)
> +buf = nbd.Buffer (512)
>  cookie = h.aio_pread_structured_callback (buf, 0,
>                                            lambda *args: chunk (42, *args),
>                                            lambda *args: callback (42, *args))
>  while not (h.aio_command_completed (cookie)):
>      h.poll (-1)
>  
> -buf = nbd.aio_buffer_to_bytearray (buf)
> +buf = buf.to_bytearray ()
>  
>  print ("%r" % buf)
>  
>  assert buf == expected
>  
>  # Second try: fail only during callback
> -buf = nbd.aio_buffer (512)
> +buf = nbd.Buffer (512)
>  cookie = h.aio_pread_structured_callback (buf, 0,
>                                            lambda *args: chunk (42, *args),
>                                            lambda *args: callback (43, *args))
> @@ -69,7 +69,7 @@ except nbd.Error as ex:
>      assert ex.errnum == errno.ENOMEM
>  
>  # Third try: fail during both
> -buf = nbd.aio_buffer (512)
> +buf = nbd.Buffer (512)
>  cookie = h.aio_pread_structured_callback (buf, 0,
>                                            lambda *args: chunk (43, *args),
>                                            lambda *args: callback (44, *args))
> @@ -81,7 +81,7 @@ except nbd.Error as ex:
>      assert ex.errnum == errno.ENOMEM
>  
>  # Fourth try: fail only during chunk
> -buf = nbd.aio_buffer (512)
> +buf = nbd.Buffer (512)
>  cookie = h.aio_pread_structured_callback (buf, 0,
>                                            lambda *args: chunk (43, *args),
>                                            lambda *args: callback (42, *args))
> diff --git a/python/t/510-aio-pwrite.py b/python/t/510-aio-pwrite.py
> index 4bbc494..71aa9ba 100644
> --- a/python/t/510-aio-pwrite.py
> +++ b/python/t/510-aio-pwrite.py
> @@ -33,17 +33,17 @@ h = nbd.NBD ()
>  h.connect_command (["nbdkit", "-s", "--exit-with-parent", "-v",
>                      "file", datafile])
>  
> -buf1 = nbd.aio_buffer_from_bytearray (buf)
> +buf1 = nbd.Buffer.from_bytearray (buf)
>  cookie = h.aio_pwrite (buf1, 0, nbd.CMD_FLAG_FUA)
>  while not (h.aio_command_completed (cookie)):
>      h.poll (-1)
>  
> -buf2 = nbd.aio_buffer (512)
> +buf2 = nbd.Buffer (512)
>  cookie = h.aio_pread (buf2, 0)
>  while not (h.aio_command_completed (cookie)):
>      h.poll (-1)
>  
> -assert buf == nbd.aio_buffer_to_bytearray (buf2)
> +assert buf == buf2.to_bytearray ()
>  
>  with open (datafile, "rb") as f:
>      content = f.read ()

Rich.

-- 
Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones
Read my programming and virtualization blog: http://rwmj.wordpress.com
virt-top is 'top' for virtual machines.  Tiny program with many
powerful monitoring features, net stats, disk stats, logging, etc.
http://people.redhat.com/~rjones/virt-top




More information about the Libguestfs mailing list