Skip to content

Commit

Permalink
Fix leak in nrnpy_hoc. (#3220)
Browse files Browse the repository at this point in the history
The facts:
  * PyTuple_Pack returns a new reference [1].
  * nrn_type_from_metaclass calls either:
    - PyType_FromMetaclass
    - PyType_FromSpecWithBases
  * PyType_FromSpecWithBases doesn't state that it steals `bases` [2].
  * PyType_FromMetaclass doesn't state that it steals `bases` [3].
  * nrn_type_from_metaclass is called in a loop.

Therefore, using "local"/"relative" reference counts, i.e. the number
of INCREFs that can't be paired up with a DECREF is:

  * After `PyTuple_Pack` it's at `+1`.
  * After `Py_INCREF` it's at `+2`.
  * After `nrn_type_from_metaclass` it stays the same: `+2`.
  * After `Py_DECREF` it drops to `+1`.

The new version is:
  * After `nb::steal` we're at `+0` due to the dtor of `bases`.
  * Calling `nrn_type_from_metaclass` doesn't change the relative
    reference count: `+0`.

[1]: https://docs.python.org/3/c-api/type.html#c.PyTuple_Pack
[2]: https://docs.python.org/3/c-api/type.html#c.PyType_FromMetaclass
[3]: https://docs.python.org/3/c-api/type.html#c.PyType_FromSpecWithBases
  • Loading branch information
1uc authored Nov 20, 2024
1 parent 120a4d0 commit 9aeafb3
Showing 1 changed file with 2 additions and 5 deletions.
7 changes: 2 additions & 5 deletions src/nrnpython/nrnpy_hoc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3370,7 +3370,6 @@ extern PyObject* nrn_type_from_metaclass(PyTypeObject* meta,

extern "C" NRN_EXPORT PyObject* nrnpy_hoc() {
PyObject* m;
PyObject* bases;
PyTypeObject* pto;
PyType_Spec spec;
nrnpy_vec_from_python_p_ = nrnpy_vec_from_python;
Expand Down Expand Up @@ -3439,14 +3438,13 @@ extern "C" NRN_EXPORT PyObject* nrnpy_hoc() {
}


bases = PyTuple_Pack(1, hocobject_type);
Py_INCREF(bases);
auto bases = nb::steal(PyTuple_Pack(1, hocobject_type));
for (auto name: py_exposed_classes) {
// TODO: obj_spec_from_name needs a hoc. prepended
exposed_py_type_names.push_back(std::string("hoc.") + name);
spec = obj_spec_from_name(exposed_py_type_names.back().c_str());
pto = (PyTypeObject*)
nrn_type_from_metaclass((PyTypeObject*) custom_hocclass, m, &spec, bases);
nrn_type_from_metaclass((PyTypeObject*) custom_hocclass, m, &spec, bases.ptr());
hocclass* hclass = (hocclass*) pto;
hclass->sym = hoc_lookup(name);
// printf("%s hocclass pto->tp_basicsize = %zd sizeof(*pto)=%zd\n",
Expand All @@ -3460,7 +3458,6 @@ extern "C" NRN_EXPORT PyObject* nrnpy_hoc() {
return NULL;
}
}
Py_DECREF(bases);

topmethdict = PyDict_New();
for (PyMethodDef* meth = toplevel_methods; meth->ml_name != NULL; meth++) {
Expand Down

0 comments on commit 9aeafb3

Please sign in to comment.