buffer.c: Add a lock count which pins a write-buffer's backing store.
authorMark Wooding <mdw@distorted.org.uk>
Tue, 22 Oct 2019 16:14:46 +0000 (17:14 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 25 Nov 2019 17:43:08 +0000 (17:43 +0000)
Python 3's buffer protocol allows a long-term acquisition of a buffer's
backing-store address.  It would be Bad if one of our `WriteBuffer'
objects were to reallocate its backing store while something still
thought it had the old address, so keep track of how many times the
buffer has been referenced in this way.

Nothing actually makes use of this machinery yet, but its time will come.

buffer.c

index 7a3bec7..5731c53 100644 (file)
--- a/buffer.c
+++ b/buffer.c
@@ -34,6 +34,7 @@ typedef struct buf_pyobj {
   PyObject_HEAD
   buf b;
   PyObject *sub;
+  unsigned lk;
 } buf_pyobj;
 
 static PyTypeObject *rbuf_pytype, *wbuf_pytype;
@@ -41,6 +42,7 @@ static PyTypeObject *rbuf_pytype, *wbuf_pytype;
 #define WBUF_PYCHECK(o) PyObject_TypeCheck((o), wbuf_pytype)
 #define BUF_B(o) (&((buf_pyobj *)(o))->b)
 #define BUF_SUB(o) (((buf_pyobj *)(o))->sub)
+#define BUF_LK(o) (((buf_pyobj *)(o))->lk)
 
 /*----- Exceptions --------------------------------------------------------*/
 
@@ -62,7 +64,7 @@ static PyObject *rbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
   q = xmalloc(in.sz);
   memcpy(q, in.p, in.sz);
   me = (buf_pyobj *)ty->tp_alloc(ty, 0);
-  me->sub = 0;
+  me->sub = 0; me->lk = 0;
   buf_init(&me->b, q, in.sz);
 end:
   return ((PyObject *)me);
@@ -70,10 +72,9 @@ end:
 
 static void buf_pydealloc(PyObject *me)
 {
-  if (BUF_SUB(me))
-    Py_DECREF(BUF_SUB(me));
-  else
-    xfree(BBASE(BUF_B(me)));
+  assert(!BUF_LK(me));
+  if (BUF_SUB(me)) Py_DECREF(BUF_SUB(me));
+  else xfree(BBASE(BUF_B(me)));
   FREEOBJ(me);
 }
 
@@ -140,6 +141,7 @@ BUF_DOSUFFIXES(RBMETH_GETBLK_)
     b = PyObject_NEW(buf_pyobj, rbuf_pytype);                          \
     b->b = bb;                                                         \
     b->sub = me;                                                       \
+    b->lk = 0;                                                         \
     Py_INCREF(me);                                                     \
     return ((PyObject *)b);                                            \
   end:                                                                 \
@@ -331,20 +333,27 @@ static const PyTypeObject rbuf_pytype_skel = {
 
 /*----- Write buffers -----------------------------------------------------*/
 
-static void ensure(PyObject *me, size_t n)
+static int ensure(PyObject *me, size_t n)
 {
   buf *b = BUF_B(me);
+  size_t nn = BSZ(b);
+  octet *p;
+  size_t want = BLEN(b) + n;
 
-  if (BLEFT(b) < n) {
-    size_t nn = BSZ(b);
-    octet *p;
-    size_t want = BLEN(b) + n;
+  if (BLEFT(b) >= n)
+    return (0);
+  else if (BUF_LK(me))
+    BUFERR("buffer locked");
+  else {
     while (nn < want) nn <<= 1;
     p = xrealloc(BBASE(b), nn, BSZ(b));
     BCUR(b) = p + BLEN(b);
     BLIM(b) = p + nn;
     BBASE(b) = p;
+    return (0);
   }
+end:
+  return (-1);
 }
 
 static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
@@ -359,7 +368,7 @@ static PyObject *wbuf_pynew(PyTypeObject *ty, PyObject *arg, PyObject *kw)
     goto end;
   me = (buf_pyobj *)ty->tp_alloc(ty, 0);
   p = xmalloc(n);
-  me->sub = 0;
+  me->sub = 0; me->lk = 0;
   buf_init(&me->b, p, n);
 end:
   return ((PyObject *)me);
@@ -376,7 +385,7 @@ static PyObject *wbmeth_zero(PyObject *me, PyObject *arg)
   void *p;
   size_t n;
   if (!PyArg_ParseTuple(arg, "O&:zero", convszt, &n)) return (0);
-  ensure(me, n);
+  if (ensure(me, n)) return (0);
   p = buf_get(BUF_B(me), n); assert(p && BOK(BUF_B(me)));
   memset(p, 0, n);
   RETURN_ME;
@@ -386,7 +395,7 @@ static PyObject *wbmeth_put(PyObject *me, PyObject *arg)
 {
   struct bin in;
   if (!PyArg_ParseTuple(arg, "O&:put", convbin, &in)) return (0);
-  ensure(me, in.sz);
+  if (ensure(me, in.sz)) return (0);
   buf_put(BUF_B(me), in.p, in.sz); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
@@ -396,7 +405,7 @@ static PyObject *wbmeth_put(PyObject *me, PyObject *arg)
   {                                                                    \
     uint##n i;                                                         \
     if (!PyArg_ParseTuple(arg, "O&:putu" #w, convu##n, &i)) return (0);        \
-    ensure(me, SZ_##n);                                                        \
+    if (ensure(me, SZ_##n)) return (0);                                \
     buf_putu##w(BUF_B(me), i); assert(BOK(BUF_B(me)));                 \
     RETURN_ME;                                                         \
   }
@@ -410,7 +419,7 @@ DOUINTCONV(WBMETH_PUTU_)
     struct bin in;                                                     \
     if (!PyArg_ParseTuple(arg, "O&:putblk" #w, convbin, &in)) goto end;        \
     if (MASK##W && in.sz > MASK##W) VALERR("too large");               \
-    ensure(me, in.sz + SZ_##n);                                                \
+    if (ensure(me, in.sz + SZ_##n)) return (0);                        \
     buf_putmem##w(BUF_B(me), in.p, in.sz); assert(BOK(BUF_B(me)));     \
     RETURN_ME;                                                         \
   end:                                                                 \
@@ -422,7 +431,7 @@ static PyObject *wbmeth_putmp(PyObject *me, PyObject *arg)
 {
   mp *x = 0;
   if (!PyArg_ParseTuple(arg, "O&:putmp", convmp, &x)) return (0);
-  ensure(me, mp_octets(x) + 2);
+  if (ensure(me, mp_octets(x) + 2)) return (0);
   buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
@@ -431,7 +440,7 @@ static PyObject *wbmeth_putgf(PyObject *me, PyObject *arg)
 {
   mp *x = 0;
   if (!PyArg_ParseTuple(arg, "O&:putgf", convgf, &x)) return (0);
-  ensure(me, mp_octets(x) + 2);
+  if (ensure(me, mp_octets(x) + 2)) return (0);
   buf_putmp(BUF_B(me), x); assert(BOK(BUF_B(me)));
   MP_DROP(x);
   RETURN_ME;
@@ -441,7 +450,8 @@ static PyObject *wbmeth_putecpt(PyObject *me, PyObject *arg)
 {
   ec pt = EC_INIT;
   if (!PyArg_ParseTuple(arg, "O&:putecpt", convecpt, &pt)) return (0);
-  ensure(me, EC_ATINF(&pt) ? 2 : 6 + mp_octets(pt.x) + mp_octets(pt.y));
+  if (ensure(me, EC_ATINF(&pt) ? 2 : 6 + mp_octets(pt.x) + mp_octets(pt.y)))
+    return (0);
   buf_putec(BUF_B(me), &pt); assert(BOK(BUF_B(me)));
   EC_DESTROY(&pt);
   RETURN_ME;
@@ -454,7 +464,7 @@ static PyObject *wbmeth_putecptraw(PyObject *me, PyObject *arg)
   if (!PyArg_ParseTuple(arg, "O!:putecptraw", ecptcurve_pytype, &ptobj))
     return (0);
   EC_OUT(ECPT_C(ptobj), &pt, ECPT_P(ptobj));
-  ensure(me, ECPT_C(ptobj)->f->noctets * 2 + 1);
+  if (ensure(me, ECPT_C(ptobj)->f->noctets * 2 + 1)) return (0);
   ec_putraw(ECPT_C(ptobj), BUF_B(me), &pt); assert(BOK(BUF_B(me)));
   EC_DESTROY(&pt);
   RETURN_ME;
@@ -464,7 +474,7 @@ static PyObject *wbmeth_putge(PyObject *me, PyObject *arg)
 {
   PyObject *geobj;
   if (!PyArg_ParseTuple(arg, "O!:putge", ge_pytype, &geobj)) return (0);
-  ensure(me, GE_G(geobj)->noctets);
+  if (ensure(me, GE_G(geobj)->noctets)) return (0);
   G_TOBUF(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }
@@ -473,7 +483,7 @@ static PyObject *wbmeth_putgeraw(PyObject *me, PyObject *arg)
 {
   PyObject *geobj;
   if (!PyArg_ParseTuple(arg, "O!:putgeraw", ge_pytype, &geobj)) return (0);
-  ensure(me, GE_G(geobj)->noctets);
+  if (ensure(me, GE_G(geobj)->noctets)) return (0);
   G_TORAW(GE_G(geobj), BUF_B(me), GE_X(geobj)); assert(BOK(BUF_B(me)));
   RETURN_ME;
 }