chiark / gitweb /
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 7a3bec7f57e54da8a049663a439eefbf0f76d318..5731c53c291d9333de51dc91fbb8f69c64e520cd 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;
 }