[RFA,01/14] Introduce py-ref.h

Message ID 8760nsiozp.fsf@tromey.com
State New, archived
Headers

Commit Message

Tom Tromey Nov. 12, 2016, 5:31 p.m. UTC
  >>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> I think this should say something about supporting explicitly
Pedro> wrapping a NULL PyObject *, and how to check whether there's
Pedro> a managed object.
[... other review comments...]

Here's a new version of this patch, which I think addresses all the
comments, from both you and others.

Tom
  

Comments

Pedro Alves Nov. 15, 2016, 2:32 p.m. UTC | #1
Hi Tom,

On 11/12/2016 05:31 PM, Tom Tromey wrote:

> +
> +  /* Transfer ownership from OTHER.  */
> +  gdbpy_ref &operator= (gdbpy_ref &&other)
> +  {
> +    /* Note that this handles self-assignment properly.  */
> +    std::swap (m_obj, other.m_obj);

Since there's no need to worry about noexcept garantees here
(destructing a gdbpy_ref can't throw), I'd rather that
move would not be implemented as a swap, as that extends the
lifetime of m_obj in OTHER.

I.e., bugs like these will go unnoticed longer:

gdbpy_ref bar = make_whatever (....);
gdbpy_ref foo = make_whatever (....);

if (condition)
{
  bar = std::move (foo);
  some_function (foo.get ()); // whoops, passed bar.get () instead of NULL and crashing early.
}

I'd be happy with the idiom of implementing move-in-term-of-swap
by first move-constructing, and then swapping that temporary:

  gdbpy_ref &operator= (gdbpy_ref &&other)
  {
    gdbpy_ref (std::move (other)).swap (*this);
    return *this;
  }

Or perhaps simpler, go with something like what you had in the
first version:

+  /* Transfer ownership from another instance.  */
+  gdbpy_reference &operator= (gdbpy_reference &&other)
+  {
+    Py_XDECREF (m_obj);
+    m_obj = other.m_obj;
+    other.m_obj = NULL;
+    return *this;
+  }
+

But add a "if (this != &other)" check.  I'd tweak it to be
in terms of reset() while at it:

  /* Transfer ownership from another instance.  */
  gdbpy_reference &operator= (gdbpy_reference &&other)
  {
    if (this != &other)
      {
        reset (other.m_obj);
        other.m_obj = NULL;
      }
    return *this;
  }

Otherwise LGTM.

Thanks,
Pedro Alves
  
Tom Tromey Nov. 16, 2016, 11:18 p.m. UTC | #2
>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:

Pedro> Since there's no need to worry about noexcept garantees here
Pedro> (destructing a gdbpy_ref can't throw), I'd rather that
Pedro> move would not be implemented as a swap, as that extends the
Pedro> lifetime of m_obj in OTHER.
[...]
Pedro> Or perhaps simpler, go with something like what you had in the
Pedro> first version:

I made this change.

Pedro> But add a "if (this != &other)" check.  I'd tweak it to be
Pedro> in terms of reset() while at it:

I also changed the other operator= to use reset(), and I changed the
code to use Py_XINCREF rather than a NULL check.

Tom
  
Pedro Alves Nov. 16, 2016, 11:34 p.m. UTC | #3
On 11/16/2016 11:18 PM, Tom Tromey wrote:
>>>>>> "Pedro" == Pedro Alves <palves@redhat.com> writes:
> 
> Pedro> Since there's no need to worry about noexcept garantees here
> Pedro> (destructing a gdbpy_ref can't throw), I'd rather that
> Pedro> move would not be implemented as a swap, as that extends the
> Pedro> lifetime of m_obj in OTHER.
> [...]
> Pedro> Or perhaps simpler, go with something like what you had in the
> Pedro> first version:
> 
> I made this change.
> 
> Pedro> But add a "if (this != &other)" check.  I'd tweak it to be
> Pedro> in terms of reset() while at it:
> 
> I also changed the other operator= to use reset(), and I changed the
> code to use Py_XINCREF rather than a NULL check.

Sounds good.

Thanks,
Pedro Alves
  

Patch

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index 99edafc..da23621 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,3 +1,7 @@ 
+2016-11-10  Tom Tromey  <tom@tromey.com>
+
+	* python/py-ref.h: New file.
+
 2016-11-11  Yao Qi  <yao.qi@linaro.org>
 
 	* spu-tdep.c (spu_software_single_step): Don't call
diff --git a/gdb/python/py-ref.h b/gdb/python/py-ref.h
new file mode 100644
index 0000000..ae3b5cc
--- /dev/null
+++ b/gdb/python/py-ref.h
@@ -0,0 +1,158 @@ 
+/* Python reference-holding class
+
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDB_PYTHON_REF_H
+#define GDB_PYTHON_REF_H
+
+/* An instance of this class either holds a reference to a PyObject,
+   or is "NULL".  If it holds a reference, then when the object is
+   destroyed, the PyObject is decref'd.
+
+   Normally an instance is constructed using a PyObject*.  This sort
+   of initialization lets this class manage the lifetime of that
+   reference.
+
+   Assignment and copy construction will make a new reference as
+   appropriate.  Assignment from a plain PyObject* is disallowed to
+   avoid confusion about whether this acquires a new reference;
+   instead use the "reset" method -- which, like the PyObject*
+   constructor, transfers ownership.
+*/
+class gdbpy_ref
+{
+ public:
+
+  /* Create a new NULL instance.  */
+  gdbpy_ref ()
+    : m_obj (NULL)
+  {
+  }
+
+  /* Create a new instance.  OBJ is a reference, management of which
+     is now transferred to this class.  */
+  explicit gdbpy_ref (PyObject *obj)
+    : m_obj (obj)
+  {
+  }
+
+  /* Copy another instance.  */
+  gdbpy_ref (const gdbpy_ref &other)
+    : m_obj (other.m_obj)
+  {
+    if (m_obj != NULL)
+      Py_INCREF (m_obj);
+  }
+
+  /* Transfer ownership from OTHER.  */
+  gdbpy_ref (gdbpy_ref &&other)
+    : m_obj (other.m_obj)
+  {
+    other.m_obj = NULL;
+  }
+
+  /* Destroy this instance.  */
+  ~gdbpy_ref ()
+  {
+    Py_XDECREF (m_obj);
+  }
+
+  /* Copy another instance.  */
+  gdbpy_ref &operator= (const gdbpy_ref &other)
+  {
+    /* Do nothing on self-assignment.  */
+    if (this != &other)
+      {
+	Py_XDECREF (m_obj);
+	m_obj = other.m_obj;
+	if (m_obj != NULL)
+	  Py_INCREF (m_obj);
+      }
+    return *this;
+  }
+
+  /* Transfer ownership from OTHER.  */
+  gdbpy_ref &operator= (gdbpy_ref &&other)
+  {
+    /* Note that this handles self-assignment properly.  */
+    std::swap (m_obj, other.m_obj);
+    return *this;
+  }
+
+  /* Change this instance's referent.  OBJ is a reference, management
+     of which is now transferred to this class.  */
+  void reset (PyObject *obj)
+  {
+    Py_XDECREF (m_obj);
+    m_obj = obj;
+  }
+
+  /* Return this instance's referent.  In Python terms this is a
+     borrowed pointer.  */
+  PyObject *get () const
+  {
+    return m_obj;
+  }
+
+  /* Return this instance's referent, and stop managing this
+     reference.  The caller is now responsible for the ownership of
+     the reference.  */
+  PyObject *release ()
+  {
+    PyObject *result = m_obj;
+
+    m_obj = NULL;
+    return result;
+  }
+
+ private:
+
+  PyObject *m_obj;
+};
+
+inline bool operator== (const gdbpy_ref &self, const gdbpy_ref &other)
+{
+  return self.get () == other.get ();
+}
+
+inline bool operator== (const gdbpy_ref &self, const PyObject *other)
+{
+  return self.get () == other;
+}
+
+inline bool operator== (const PyObject *self, const gdbpy_ref &other)
+{
+  return self == other.get ();
+}
+
+inline bool operator!= (const gdbpy_ref &self, const gdbpy_ref &other)
+{
+  return self.get () != other.get ();
+}
+
+inline bool operator!= (const gdbpy_ref &self, const PyObject *other)
+{
+  return self.get () != other;
+}
+
+inline bool operator!= (const PyObject *self, const gdbpy_ref &other)
+{
+  return self != other.get ();
+}
+
+#endif /* GDB_PYTHON_REF_H */