From 67fe8ad82f6b30b2a9d92c41c5090429847fc8e5 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 30 Mar 2013 15:47:34 -0400 Subject: [PATCH] support async processing --- devsupApp/src/dbdset.c | 5 +++ devsupApp/src/dbrec.c | 72 +++++++++++++++++++++++++++++++++++++++--- testApp/test2.py | 12 +++++++ 3 files changed, 85 insertions(+), 4 deletions(-) diff --git a/devsupApp/src/dbdset.c b/devsupApp/src/dbdset.c index bdefdf8..e7ba095 100644 --- a/devsupApp/src/dbdset.c +++ b/devsupApp/src/dbdset.c @@ -216,6 +216,7 @@ static long process_record(dbCommon *prec) { pyDevice *priv = prec->dpvt; PyGILState_STATE pystate; + long pact = prec->pact; if(!priv || !priv->support) return 0; @@ -227,6 +228,10 @@ static long process_record(dbCommon *prec) PyErr_Clear(); } + /* always clear PACT if it was initially set */ + if(pact) + prec->pact = 0; + PyGILState_Release(pystate); return 0; } diff --git a/devsupApp/src/dbrec.c b/devsupApp/src/dbrec.c index 5088c96..3a20545 100644 --- a/devsupApp/src/dbrec.c +++ b/devsupApp/src/dbrec.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include "pydevsup.h" @@ -132,21 +133,81 @@ static PyObject* pyRecord_scan(pyRecord *self, PyObject *args, PyObject *kws) if(!PyObject_IsTrue(sync)) { scanOnce(prec); + Py_RETURN_NONE; + } else { + long ret; setCausePyRecord(prec, reason); Py_BEGIN_ALLOW_THREADS { dbScanLock(prec); - dbProcess(prec); + ret = dbProcess(prec); dbScanUnlock(prec); } Py_END_ALLOW_THREADS clearCausePyRecord(prec); + + return PyLong_FromLong(ret); + } +} + +static PyObject *pyRecord_asyncStart(pyRecord *self) +{ + dbCommon *prec=self->entry.precnode->precord; + epicsUInt8 pact = prec->pact; + if(!isPyRecord(prec)) { + PyErr_SetString(PyExc_RuntimeError, "Not a Python Device record"); + return NULL; + } + prec->pact = 1; + return PyLong_FromLong(pact); +} + +static PyObject *pyRecord_asyncFinish(pyRecord *self, PyObject *args, PyObject *kws) +{ + long pact, ret; + dbCommon *prec = self->entry.precnode->precord; + + static char* names[] = {"reason", NULL}; + PyObject *reason = Py_None; + + if(!PyArg_ParseTupleAndKeywords(args, kws, "|O", names, &reason)) + return NULL; + + if(!isPyRecord(prec)) { + PyErr_SetString(PyExc_RuntimeError, "Not a Python Device record"); + return NULL; } - Py_RETURN_NONE; + Py_INCREF(self); + + setCausePyRecord(prec, reason); + + Py_BEGIN_ALLOW_THREADS { + rset *rsup = prec->rset; + + dbScanLock(prec); + pact = prec->pact; + if(pact) { + ret = (*rsup->process)(prec); + /* Out devsup always clears PACT if initially set */ + } + dbScanUnlock(prec); + + } Py_END_ALLOW_THREADS + + clearCausePyRecord(prec); + + if(!pact) { + PyErr_SetString(PyExc_ValueError, "Python Device record was not active"); + return NULL; + } + + Py_DECREF(self); + + return PyLong_FromLong(ret); } static PyMethodDef pyRecord_methods[] = { @@ -159,8 +220,11 @@ static PyMethodDef pyRecord_methods[] = { {"scan", (PyCFunction)pyRecord_scan, METH_VARARGS|METH_KEYWORDS, "scan(sync=False)\nScan this record. If sync is False then" "a scan request is queued. If sync is True then the record" - "is scannined immidately on the current thread." - }, + "is scannined immidately on the current thread."}, + {"asyncStart", (PyCFunction)pyRecord_asyncStart, METH_NOARGS, + "Begin an asynchronous action. Record must be locked!"}, + {"asyncFinish", (PyCFunction)pyRecord_asyncFinish, METH_VARARGS|METH_KEYWORDS, + "Complete an asynchronous action. Record must *not* be locked!"}, {NULL, NULL, 0, NULL} }; diff --git a/testApp/test2.py b/testApp/test2.py index 09b5a43..685d658 100644 --- a/testApp/test2.py +++ b/testApp/test2.py @@ -1,12 +1,23 @@ +import weakref import threading, time from devsup.hooks import addHook insts = {} +def done(obj): + print obj,'Expires' + +_tracking = {} +def track(obj): + W = weakref.ref(obj, done) + print 'track',obj,'with',W + _tracking[id(obj)] = W + class Driver(threading.Thread): def __init__(self, name): super(Driver,self).__init__() + track(self) self.name = name self._lock = threading.Lock() self._recs = set() @@ -48,6 +59,7 @@ def addDrv(name): class Device(object): def __init__(self, rec, drv): + track(self) self.driver, self.record = drv, rec self.driver.addrec(self) self.val = rec.field('VAL')