thread safe processing reason

Use TLS to ensure that concurrent scanners
don't see our reason.
This commit is contained in:
Michael Davidsaver
2013-12-10 18:25:46 -05:00
parent 907db555b4
commit 1ab43405c4
4 changed files with 19 additions and 34 deletions

View File

@ -6,6 +6,7 @@
#include <Python.h>
#include <epicsVersion.h>
#include <epicsThread.h>
#include <assert.h>
#include <dbCommon.h>
#include <dbStaticLib.h>
@ -35,8 +36,6 @@ typedef struct {
int rawsupport;
IOSCANPVT scan;
PyObject *reason;
} pyDevice;
static long parse_link(dbCommon *prec, const char* src)
@ -131,11 +130,13 @@ static long detach_common(dbCommon *prec)
static long process_common(dbCommon *prec)
{
pyDevice *priv = prec->dpvt;
PyObject *reason = priv->reason;
PyObject *reason = epicsThreadPrivateGet(pyDevReasonID);
PyObject *ret;
if(!reason)
reason = Py_None;
else
epicsThreadPrivateSet(pyDevReasonID, NULL);
ret = PyObject_CallMethod(priv->support, "process", "OO", priv->pyrecord, reason);
if(!ret)
@ -165,11 +166,6 @@ static long report(int lvl)
obj = priv->scanobj ? priv->scanobj : Py_None;
printf("IOSCAN: ");
PyObject_Print(obj, stdout, 0);
if(priv->reason) {
printf("Leftover reason!: ");
PyObject_Print(priv->reason, stdout, 0);
}
}
}
PyGILState_Release(pystate);
@ -351,26 +347,6 @@ static long process_record2(dbCommon *prec)
return ret;
}
int setReasonPyRecord(dbCommon *prec, PyObject *reason)
{
pyDevice *priv=prec->dpvt;
if(!isPyRecord(prec) || !priv || priv->reason)
return 0;
Py_INCREF(reason);
priv->reason = reason;
return 1;
}
int clearReasonPyRecord(dbCommon *prec)
{
pyDevice *priv=prec->dpvt;
if(!isPyRecord(prec) || !priv || !priv->reason)
return 0;
Py_DECREF(priv->reason);
priv->reason = NULL;
return 1;
}
static dsxt pydevsupExt = {&add_record, &del_record};
@ -438,8 +414,7 @@ void pyDBD_cleanup(void)
Py_XDECREF(priv->support);
Py_XDECREF(priv->pyrecord);
Py_XDECREF(priv->reason);
priv->support = priv->pyrecord = priv->reason = NULL;
priv->support = priv->pyrecord = NULL;
free(priv);
}

View File

@ -190,7 +190,7 @@ static PyObject* pyRecord_scan(pyRecord *self, PyObject *args, PyObject *kws)
} else {
long ret=-1;
int ran=0;
setReasonPyRecord(prec, reason);
epicsThreadPrivateSet(pyDevReasonID, reason);
Py_BEGIN_ALLOW_THREADS {
@ -206,7 +206,7 @@ static PyObject* pyRecord_scan(pyRecord *self, PyObject *args, PyObject *kws)
} Py_END_ALLOW_THREADS
clearReasonPyRecord(prec);
epicsThreadPrivateSet(pyDevReasonID, NULL);
if(ran)
return PyLong_FromLong(ret);
@ -247,7 +247,7 @@ static PyObject *pyRecord_asyncFinish(pyRecord *self, PyObject *args, PyObject *
Py_INCREF(self); /* necessary? */
setReasonPyRecord(prec, reason);
epicsThreadPrivateSet(pyDevReasonID, reason);
Py_BEGIN_ALLOW_THREADS {
rset *rsup = prec->rset;
@ -262,7 +262,7 @@ static PyObject *pyRecord_asyncFinish(pyRecord *self, PyObject *args, PyObject *
} Py_END_ALLOW_THREADS
clearReasonPyRecord(prec);
epicsThreadPrivateSet(pyDevReasonID, NULL);
if(!pact) {
PyErr_SetString(PyExc_ValueError, "Python Device record was not active");

View File

@ -1,6 +1,8 @@
#ifndef PYDEVSUP_H
#define PYDEVSUP_H
#include <epicsThread.h>
#if PY_MAJOR_VERSION >= 3
#define PyInt_FromLong PyLong_FromLong
#define PyInt_AsLong PyLong_AsLong
@ -23,4 +25,6 @@ int canIOScanRecord(dbCommon *);
int setReasonPyRecord(dbCommon *, PyObject *);
int clearReasonPyRecord(dbCommon *);
extern epicsThreadPrivateId pyDevReasonID;
#endif // PYDEVSUP_H

View File

@ -29,6 +29,8 @@ typedef struct {
const char * const name;
} pystate;
epicsThreadPrivateId pyDevReasonID;
#define INITST(hook) {initHook ## hook, #hook }
static pystate statenames[] = {
INITST(AtIocBuild),
@ -284,6 +286,8 @@ static void cleanupPy(void *junk)
pyField_cleanup();
Py_Finalize();
epicsThreadPrivateDelete(pyDevReasonID);
}
/* Initialize the interpreter environment
@ -384,6 +388,8 @@ static void pySetupReg(void)
{
PyGILState_STATE state;
pyDevReasonID = epicsThreadPrivateCreate();
setupPyInit();
iocshRegister(&codeDef, &codeRun);
iocshRegister(&fileDef, &fileRun);