88#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
99#include " numpy/ndarrayobject.h"
1010#include < stdio.h>
11- #include < libgen.h>
1211#include < epicsTime.h>
1312#include " NDArray.h"
1413#include " adPythonPlugin.h"
3433#define Bad (errString ) NoGood(errString, BAD)
3534#define Ugly (errString ) NoGood(errString, UGLY)
3635
36+ // Used in setting the python module search path
37+ #ifdef _WIN32
38+ #define PATH_LIST_SEPARATOR " ;"
39+ #else
40+ #define PATH_LIST_SEPARATOR " :"
41+ #endif
42+
3743// Used in error printing
3844const char *driverName = " adPythonPlugin" ;
3945
40- // This holds the threadState of the thread that initialised python
41- // We need it to get a handle on the interpreter so create a threadState
42- // object for each port
43- static PyThreadState *mainThreadState = NULL ;
44-
4546adPythonPlugin::adPythonPlugin (const char *portNameArg, const char *filename,
4647 const char *classname, int queueSize, int blockingCallbacks,
4748 const char *NDArrayPort, int NDArrayAddr, int maxBuffers,
@@ -81,24 +82,24 @@ adPythonPlugin::adPythonPlugin(const char *portNameArg, const char *filename,
8182 */
8283void adPythonPlugin::initThreads ()
8384{
84- // First we tell python where to find adPythonPlugin.py and other scripts
85- char buffer[BIGBUFFER];
86- snprintf (buffer, sizeof (buffer), " PYTHONPATH=%s" , DATADIRS);
87- putenv (buffer);
88-
89- // Now we initialise python
85+ PyGILState_STATE gstate;
86+
87+ // Initialise python
9088 if (!Py_IsInitialized ()) {
9189 PyEval_InitThreads ();
9290 Py_Initialize ();
93-
94- // Be sure to save thread state to release the GIL and give a handle
95- // on the interpreter to this and other ports
96- mainThreadState = PyEval_SaveThread ();
91+ // Release the GIL
92+ PyEval_SaveThread ();
9793 }
9894
99- // Create a thread state just for us
100- this ->threadState = PyThreadState_New (mainThreadState->interp );
101- PyEval_RestoreThread (this ->threadState );
95+ // Acquire the GIL and set up non-python-created thread access
96+ gstate = PyGILState_Ensure ();
97+
98+ // Tell python where to find adPythonPlugin.py and other scripts
99+ char buffer[BIGBUFFER];
100+ snprintf (buffer, sizeof (buffer), " %s%s%s" , Py_GetPath (),
101+ PATH_LIST_SEPARATOR, DATADIRS);
102+ PySys_SetPath (buffer);
102103
103104 // Import our supporting library
104105 this ->importAdPythonModule ();
@@ -117,8 +118,8 @@ void adPythonPlugin::initThreads()
117118 this ->updateParamList (1 );
118119 }
119120
120- // Release the GIL and finish
121- this -> threadState = PyEval_SaveThread ( );
121+ // Release the GIL and tear down non-python-created thread access
122+ PyGILState_Release (gstate );
122123}
123124
124125/* * Callback function that is called by the NDArray driver with new NDArray data
@@ -130,6 +131,8 @@ void adPythonPlugin::initThreads()
130131 * Called with this->lock taken
131132 */
132133void adPythonPlugin::processCallbacks (NDArray *pArray) {
134+ PyGILState_STATE gstate;
135+
133136 // First call the base class method
134137 NDPluginDriver::processCallbacks (pArray);
135138
@@ -143,8 +146,8 @@ void adPythonPlugin::processCallbacks(NDArray *pArray) {
143146 this ->unlock ();
144147 epicsMutexLock (this ->dictMutex );
145148
146- // Make sure we're allowed to use the python API
147- PyEval_RestoreThread ( this -> threadState );
149+ // Acquire the GIL and set up non- python-created thread access
150+ gstate = PyGILState_Ensure ( );
148151 this ->lock ();
149152
150153 // Store the time at the beginning of processing for profiling
@@ -181,9 +184,11 @@ void adPythonPlugin::processCallbacks(NDArray *pArray) {
181184 setDoubleParam (adPythonTime, epicsTimeDiffInSeconds (&end, &start)*1000 );
182185 callParamCallbacks ();
183186
184- // release GIL and dict Mutex
187+ // Unlock
185188 this ->unlock ();
186- this ->threadState = PyEval_SaveThread ();
189+ // Release the GIL and tear down non-python-created thread access
190+ PyGILState_Release (gstate);
191+ // Release dict mutex
187192 epicsMutexUnlock (this ->dictMutex );
188193
189194 // Spit out the array
@@ -209,14 +214,15 @@ asynStatus adPythonPlugin::writeInt32(asynUser *pasynUser, epicsInt32 value) {
209214 int param = pasynUser->reason ;
210215 if (param == adPythonLoad ||
211216 (this ->nextParam && param >= adPythonUserParams[0 ])) {
217+ PyGILState_STATE gstate;
212218 // We have to modify our python dict to match our param list
213219 // Note: to avoid deadlocks we should always take locks in order:
214220 // dictMutex, then GIL, then this->lock
215221 // so unlock here to preserve this order
216222 this ->unlock ();
217223 epicsMutexLock (this ->dictMutex );
218- // Make sure we're allowed to use the python API
219- PyEval_RestoreThread ( this -> threadState );
224+ // Acquire the GIL and set up non- python-created thread access
225+ gstate = PyGILState_Ensure ( );
220226 // Now call the bast class to write the value to the param list
221227 this ->lock ();
222228 status |= NDPluginDriver::writeInt32 (pasynUser, value);
@@ -230,8 +236,9 @@ asynStatus adPythonPlugin::writeInt32(asynUser *pasynUser, epicsInt32 value) {
230236 // our param lib has changed, so update the dict and reprocess
231237 status |= this ->updateParamDict ();
232238 }
233- // release GIL and dict Mutex
234- this ->threadState = PyEval_SaveThread ();
239+ // Release the GIL and tear down non-python-created thread access
240+ PyGILState_Release (gstate);
241+ // Release dict mutex
235242 epicsMutexUnlock (this ->dictMutex );
236243 } else {
237244 status |= NDPluginDriver::writeInt32 (pasynUser, value);
@@ -252,21 +259,23 @@ asynStatus adPythonPlugin::writeFloat64(asynUser *pasynUser,
252259 int status = asynSuccess;
253260 int param = pasynUser->reason ;
254261 if (this ->nextParam && param >= adPythonUserParams[0 ]) {
262+ PyGILState_STATE gstate;
255263 // We have to modify our python dict to match our param list
256264 // Note: to avoid deadlocks we should always take locks in order:
257265 // dictMutex, then GIL, then this->lock
258266 // so unlock here to preserve this order
259267 this ->unlock ();
260268 epicsMutexLock (this ->dictMutex );
261- // Make sure we're allowed to use the python API
262- PyEval_RestoreThread ( this -> threadState );
269+ // Acquire the GIL and set up non- python-created thread access
270+ gstate = PyGILState_Ensure ( );
263271 // Now call the bast class to write the value to the param list
264272 this ->lock ();
265273 status |= NDPluginDriver::writeFloat64 (pasynUser, value);
266274 // our param lib has changed, so update the dict and reprocess
267275 status |= this ->updateParamDict ();
268- // release GIL and dict Mutex
269- this ->threadState = PyEval_SaveThread ();
276+ // Release the GIL and tear down non-python-created thread access
277+ PyGILState_Release (gstate);
278+ // Release dict mutex
270279 epicsMutexUnlock (this ->dictMutex );
271280 } else {
272281 status = NDPluginDriver::writeFloat64 (pasynUser, value);
@@ -289,21 +298,23 @@ asynStatus adPythonPlugin::writeOctet(asynUser *pasynUser, const char *value,
289298 int status = asynSuccess;
290299 int param = pasynUser->reason ;
291300 if (this ->nextParam && param >= adPythonUserParams[0 ]) {
301+ PyGILState_STATE gstate;
292302 // We have to modify our python dict to match our param list
293303 // Note: to avoid deadlocks we should always take locks in order:
294304 // dictMutex, then GIL, then this->lock
295305 // so unlock here to preserve this order
296306 this ->unlock ();
297307 epicsMutexLock (this ->dictMutex );
298- // Make sure we're allowed to use the python API
299- PyEval_RestoreThread ( this -> threadState );
308+ // Acquire the GIL and set up non- python-created thread access
309+ gstate = PyGILState_Ensure ( );
300310 // Now call the bast class to write the value to the param list
301311 this ->lock ();
302312 status |= NDPluginDriver::writeOctet (pasynUser, value, maxChars, nActual);
303313 // our param lib has changed, so update the dict and reprocess
304314 status |= this ->updateParamDict ();
305- // release GIL and dict Mutex
306- this ->threadState = PyEval_SaveThread ();
315+ // Release the GIL and tear down non-python-created thread access
316+ PyGILState_Release (gstate);
317+ // Release dict mutex
307318 epicsMutexUnlock (this ->dictMutex );
308319 } else {
309320 status |= NDPluginDriver::writeOctet (pasynUser, value, maxChars, nActual);
0 commit comments