The Case For Do-Once

Lawrence D'Oliveiro ldo at geek-central.gen.new_zealand
Mon Feb 8 09:53:24 CET 2010


I wrote my first Python extension library over the last couple of weeks. I 
took note of all the recommendations to keep track of reference counts, to 
ensure that objects were not disposed when they shouldn’t be, and were when 
they should. However, the example code seems to use gotos. And the trouble 
with these is that they don’t nest and un-nest easily; try to do too much 
refactoring with them, and you run into the age-old “spaghetti code” 
problem.

Which is where the do-once block comes in. The basic control flow is this:
    * Unconditionally initialize all dynamic storage to nil
    * Do the main body of the code, aborting in any error
    * Regardless of success or failure of the above, dispose of all
      allocated dynamic storage, using disposal calls which turn into
      noops if passed pointers that are already nil.

For example, here’s a utility routine from my extension that, passed a 
Python array object, returns the address and length of the storage:

static void GetBufferInfo
  (
    PyObject * FromArray,
    unsigned long * addr,
    unsigned long * len
  )
  /* returns the address and length of the data in a Python array object. */
  {
    PyObject * TheBufferInfo = 0;
    PyObject * AddrObj = 0;
    PyObject * LenObj = 0;
    do /*once*/
      {
        TheBufferInfo = PyObject_CallMethod(FromArray, "buffer_info", "");
        if (TheBufferInfo == 0)
            break;
        AddrObj = PyTuple_GetItem(TheBufferInfo, 0);
        LenObj = PyTuple_GetItem(TheBufferInfo, 1);
        if (PyErr_Occurred())
            break;
        Py_INCREF(AddrObj);
        Py_INCREF(LenObj);
        *addr = PyInt_AsUnsignedLongMask(AddrObj);
        *len = PyInt_AsUnsignedLongMask(LenObj);
        if (PyErr_Occurred())
            break;
      }
    while (false);
    Py_XDECREF(AddrObj);
    Py_XDECREF(LenObj);
    Py_XDECREF(TheBufferInfo);
  } /*GetBufferInfo*/

You can pretty much determine by inspection that all the reference counts 
are properly maintained, no need to trace through convoluted flows of 
control.



More information about the Python-list mailing list