comparison PyPlugin.cpp @ 3:134313c59d82

* Add global mutex to PyPlugin -- all plugin method calls are strictly serialised in order to avoid problems with Python interpreter's lack of thread safety.
author cannam
date Fri, 14 Mar 2008 12:02:15 +0000
parents e20e214bdfb5
children e1b508f2f914
comparison
equal deleted inserted replaced
2:211ebe55d521 3:134313c59d82
46 support multiple plugins per script in scanner 46 support multiple plugins per script in scanner
47 ensure proper cleanup, host do a good job though 47 ensure proper cleanup, host do a good job though
48 48
49 */ 49 */
50 50
51 //#include "Python.h" 51 #include <Python.h>
52 #include "/usr/include/python/Python.h"
53 #include "PyPlugin.h" 52 #include "PyPlugin.h"
54 53
55 #ifdef _WIN32 54 #ifdef _WIN32
56 #define pathsep ('\\') 55 #define pathsep ('\\')
57 #else 56 #else
64 using std::vector; 63 using std::vector;
65 using std::cerr; 64 using std::cerr;
66 using std::endl; 65 using std::endl;
67 using std::map; 66 using std::map;
68 67
69 extern volatile bool mutex;
70
71 // Maps to associate strings with enum values 68 // Maps to associate strings with enum values
72 static std::map<std::string, eOutDescriptors> outKeys; 69 static std::map<std::string, eOutDescriptors> outKeys;
73 static std::map<std::string, eSampleTypes> sampleKeys; 70 static std::map<std::string, eSampleTypes> sampleKeys;
74 static std::map<std::string, eFeatureFields> ffKeys; 71 static std::map<std::string, eFeatureFields> ffKeys;
75 static std::map<std::string, p::eParmDescriptors> parmKeys; 72 static std::map<std::string, p::eParmDescriptors> parmKeys;
73
74 Mutex PyPlugin::m_pythonInterpreterMutex;
76 75
77 void initMaps() 76 void initMaps()
78 { 77 {
79 outKeys["identifier"] = identifier; 78 outKeys["identifier"] = identifier;
80 outKeys["name"] = name; 79 outKeys["name"] = name;
168 m_previousSample(0.0f), 167 m_previousSample(0.0f),
169 m_plugin(pluginKey), 168 m_plugin(pluginKey),
170 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)), 169 m_class(pluginKey.substr(pluginKey.rfind(':')+1,pluginKey.size()-1)),
171 m_path((pluginKey.substr(0,pluginKey.rfind(pathsep)))) 170 m_path((pluginKey.substr(0,pluginKey.rfind(pathsep))))
172 { 171 {
173
174 /*TODO: this is a nasty way of ensuring the process is
175 finished before we create a new instance accessing the Python/C API.
176 The Python/C API is not fully thread safe.
177 Calling into a python class while the process is doing heavy
178 computation may cause segmentation fault.
179 Manipulating the GIL and thread states only gave me a grief so far.*/
180
181 if (mutex) {
182 cerr << "PyPlugin::PyPlugin:" << m_class
183 << " Warning: Waiting for clear signal from parallel process." << endl;
184 while (mutex) { }
185 }
186 } 172 }
187 173
188 PyPlugin::~PyPlugin() 174 PyPlugin::~PyPlugin()
189 { 175 {
190 cerr << "PyPlugin::PyPlugin:" << m_class 176 cerr << "PyPlugin::PyPlugin:" << m_class
191 << " Instance deleted." << endl; 177 << " Instance deleted." << endl;
192 //for safety only. has been cleared after process.
193 mutex = false;
194 } 178 }
195 179
196 180
197 string 181 string
198 PyPlugin::getIdentifier() const 182 PyPlugin::getIdentifier() const
199 { 183 {
184 MutexLocker locker(&m_pythonInterpreterMutex);
185
200 char method[]="getIdentifier"; 186 char method[]="getIdentifier";
201 cerr << "[call] " << method << endl; 187 cerr << "[call] " << method << endl;
202 string rString="VampPy-x"; 188 string rString="VampPy-x";
203 189
204 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 190 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
224 210
225 211
226 string 212 string
227 PyPlugin::getName() const 213 PyPlugin::getName() const
228 { 214 {
215 MutexLocker locker(&m_pythonInterpreterMutex);
229 216
230 char method[]="getName"; 217 char method[]="getName";
231 cerr << "[call] " << method << endl; 218 cerr << "[call] " << method << endl;
232 string rString="VamPy Plugin (Noname)"; 219 string rString="VamPy Plugin (Noname)";
233 220
252 } 239 }
253 240
254 string 241 string
255 PyPlugin::getDescription() const 242 PyPlugin::getDescription() const
256 { 243 {
244 MutexLocker locker(&m_pythonInterpreterMutex);
245
257 char method[]="getDescription"; 246 char method[]="getDescription";
258 cerr << "[call] " << method << endl; 247 cerr << "[call] " << method << endl;
259 string rString="Not given. (Hint: Implement getDescription method.)"; 248 string rString="Not given. (Hint: Implement getDescription method.)";
260 249
261 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 250 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
279 } 268 }
280 269
281 string 270 string
282 PyPlugin::getMaker() const 271 PyPlugin::getMaker() const
283 { 272 {
273 MutexLocker locker(&m_pythonInterpreterMutex);
274
284 char method[]="getMaker"; 275 char method[]="getMaker";
285 cerr << "[call] " << method << endl; 276 cerr << "[call] " << method << endl;
286 string rString="Generic VamPy Plugin."; 277 string rString="Generic VamPy Plugin.";
287 278
288 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 279 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
312 } 303 }
313 304
314 string 305 string
315 PyPlugin::getCopyright() const 306 PyPlugin::getCopyright() const
316 { 307 {
308 MutexLocker locker(&m_pythonInterpreterMutex);
309
317 char method[]="getCopyright"; 310 char method[]="getCopyright";
318 cerr << "[call] " << method << endl; 311 cerr << "[call] " << method << endl;
319 string rString="BSD License"; 312 string rString="BSD License";
320 313
321 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 314 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
341 334
342 335
343 bool 336 bool
344 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize) 337 PyPlugin::initialise(size_t channels, size_t stepSize, size_t blockSize)
345 { 338 {
339 MutexLocker locker(&m_pythonInterpreterMutex);
340
346 if (channels < getMinChannelCount() || 341 if (channels < getMinChannelCount() ||
347 channels > getMaxChannelCount()) return false; 342 channels > getMaxChannelCount()) return false;
348 343
349 m_stepSize = std::min(stepSize, blockSize); 344 m_stepSize = std::min(stepSize, blockSize);
350 char method[]="initialise"; 345 char method[]="initialise";
388 } 383 }
389 384
390 void 385 void
391 PyPlugin::reset() 386 PyPlugin::reset()
392 { 387 {
388 //!!! implement this!
393 m_previousSample = 0.0f; 389 m_previousSample = 0.0f;
394 } 390 }
395 391
396 PyPlugin::InputDomain PyPlugin::getInputDomain() const 392 PyPlugin::InputDomain PyPlugin::getInputDomain() const
397 { 393 {
394 MutexLocker locker(&m_pythonInterpreterMutex);
395
398 char method[]="getInputDomain"; 396 char method[]="getInputDomain";
399 cerr << "[call] " << method << endl; 397 cerr << "[call] " << method << endl;
400 PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain 398 PyPlugin::InputDomain rValue = TimeDomain; // TimeDomain
401 399
402 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 400 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
418 return rValue; 416 return rValue;
419 } 417 }
420 418
421 size_t PyPlugin::getPreferredBlockSize() const 419 size_t PyPlugin::getPreferredBlockSize() const
422 { 420 {
421 MutexLocker locker(&m_pythonInterpreterMutex);
422
423 char method[]="getPreferredBlockSize"; 423 char method[]="getPreferredBlockSize";
424 cerr << "[call] " << method << endl; 424 cerr << "[call] " << method << endl;
425 size_t rValue=0; //not set by default 425 size_t rValue=0; //not set by default
426 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 426 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
427 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); 427 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
441 } 441 }
442 442
443 //size_t PyPlugin::getPreferredStepSize() const { return 0; } 443 //size_t PyPlugin::getPreferredStepSize() const { return 0; }
444 size_t PyPlugin::getPreferredStepSize() const 444 size_t PyPlugin::getPreferredStepSize() const
445 { 445 {
446 MutexLocker locker(&m_pythonInterpreterMutex);
447
446 char method[]="getPreferredStepSize"; 448 char method[]="getPreferredStepSize";
447 cerr << "[call] " << method << endl; 449 cerr << "[call] " << method << endl;
448 size_t rValue=0; //not set by default 450 size_t rValue=0; //not set by default
449 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 451 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
450 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); 452 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
463 return rValue; 465 return rValue;
464 } 466 }
465 467
466 size_t PyPlugin::getMinChannelCount() const 468 size_t PyPlugin::getMinChannelCount() const
467 { 469 {
470 MutexLocker locker(&m_pythonInterpreterMutex);
471
468 char method[]="getMinChannelCount"; 472 char method[]="getMinChannelCount";
469 cerr << "[call] " << method << endl; 473 cerr << "[call] " << method << endl;
470 size_t rValue=1; //default value 474 size_t rValue=1; //default value
471 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 475 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
472 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); 476 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
485 return rValue; 489 return rValue;
486 } 490 }
487 491
488 size_t PyPlugin::getMaxChannelCount() const 492 size_t PyPlugin::getMaxChannelCount() const
489 { 493 {
494 MutexLocker locker(&m_pythonInterpreterMutex);
495
490 char method[]="getMaxChannelCount"; 496 char method[]="getMaxChannelCount";
491 cerr << "[call] " << method << endl; 497 cerr << "[call] " << method << endl;
492 size_t rValue=1; //default value 498 size_t rValue=1; //default value
493 if ( PyObject_HasAttrString(m_pyInstance,method) ) { 499 if ( PyObject_HasAttrString(m_pyInstance,method) ) {
494 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL); 500 PyObject *pyInt = PyObject_CallMethod(m_pyInstance, method, NULL);
509 515
510 516
511 PyPlugin::OutputList 517 PyPlugin::OutputList
512 PyPlugin::getOutputDescriptors() const 518 PyPlugin::getOutputDescriptors() const
513 { 519 {
520 MutexLocker locker(&m_pythonInterpreterMutex);
521
514 //PyEval_AcquireThread(newThreadState); 522 //PyEval_AcquireThread(newThreadState);
515 OutputList list; 523 OutputList list;
516 OutputDescriptor od; 524 OutputDescriptor od;
517 char method[]="getOutputDescriptors"; 525 char method[]="getOutputDescriptors";
518 cerr << "[call] " << method << endl; 526 cerr << "[call] " << method << endl;
610 } 618 }
611 619
612 PyPlugin::ParameterList 620 PyPlugin::ParameterList
613 PyPlugin::getParameterDescriptors() const 621 PyPlugin::getParameterDescriptors() const
614 { 622 {
623 MutexLocker locker(&m_pythonInterpreterMutex);
624
615 ParameterList list; 625 ParameterList list;
616 ParameterDescriptor pd; 626 ParameterDescriptor pd;
617 char method[]="getParameterDescriptors"; 627 char method[]="getParameterDescriptors";
618 cerr << "[call] " << method << endl; 628 cerr << "[call] " << method << endl;
619 629
691 return list; 701 return list;
692 } 702 }
693 703
694 void PyPlugin::setParameter(std::string paramid, float newval) 704 void PyPlugin::setParameter(std::string paramid, float newval)
695 { 705 {
706 MutexLocker locker(&m_pythonInterpreterMutex);
707
696 char method[]="setParameter"; 708 char method[]="setParameter";
697 cerr << "[call] " << method << endl; 709 cerr << "[call] " << method << endl;
698 710
699 //Check if the method is implemented in Python 711 //Check if the method is implemented in Python
700 if (PyObject_HasAttrString(m_pyInstance,method)) { 712 if (PyObject_HasAttrString(m_pyInstance,method)) {
720 } 732 }
721 } 733 }
722 734
723 float PyPlugin::getParameter(std::string paramid) const 735 float PyPlugin::getParameter(std::string paramid) const
724 { 736 {
737 MutexLocker locker(&m_pythonInterpreterMutex);
738
725 char method[]="getParameter"; 739 char method[]="getParameter";
726 cerr << "[call] " << method << endl; 740 cerr << "[call] " << method << endl;
727 float rValue = 0.0f; 741 float rValue = 0.0f;
728 742
729 //Check if the method is implemented in Python 743 //Check if the method is implemented in Python
761 775
762 PyPlugin::FeatureSet 776 PyPlugin::FeatureSet
763 PyPlugin::process(const float *const *inputBuffers, 777 PyPlugin::process(const float *const *inputBuffers,
764 Vamp::RealTime timestamp) 778 Vamp::RealTime timestamp)
765 { 779 {
780 MutexLocker locker(&m_pythonInterpreterMutex);
781
766 if (m_stepSize == 0) { 782 if (m_stepSize == 0) {
767 cerr << "ERROR: PyPlugin::process: " 783 cerr << "ERROR: PyPlugin::process: "
768 << "Plugin has not been initialised" << endl; 784 << "Plugin has not been initialised" << endl;
769 return FeatureSet(); 785 return FeatureSet();
770 } 786 }
771 mutex = true;
772 static char method[]="process"; 787 static char method[]="process";
773 788
774 #ifdef _DEBUG 789 #ifdef _DEBUG
775 cerr << "[call] " << method << " frame:" << proccounter << endl; 790 cerr << "[call] " << method << " frame:" << proccounter << endl;
776 proccounter++; 791 proccounter++;
810 cerr << "ERROR: In Python plugin [" << m_class << "::" << method 825 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
811 << "] Expected List return type." << endl; 826 << "] Expected List return type." << endl;
812 } 827 }
813 Py_CLEAR(pyMethod); 828 Py_CLEAR(pyMethod);
814 Py_CLEAR(pyOutputList); 829 Py_CLEAR(pyOutputList);
815 mutex = false;
816 return FeatureSet(); 830 return FeatureSet();
817 } 831 }
818 832
819 Py_DECREF(pyMethod); 833 Py_DECREF(pyMethod);
820 // Py_DECREF(pyList); 834 // Py_DECREF(pyList);
885 899
886 }// for j = FeatureList 900 }// for j = FeatureList
887 901
888 }//for i = FeatureSet 902 }//for i = FeatureSet
889 Py_CLEAR(pyOutputList); 903 Py_CLEAR(pyOutputList);
890 mutex = false;
891 return returnFeatures; 904 return returnFeatures;
892 905
893 }//if (has_attribute) 906 }//if (has_attribute)
894 mutex = false;
895 return FeatureSet(); 907 return FeatureSet();
896 } 908 }
897 909
898 910
899 911
900 PyPlugin::FeatureSet 912 PyPlugin::FeatureSet
901 PyPlugin::getRemainingFeatures() 913 PyPlugin::getRemainingFeatures()
902 { 914 {
915 MutexLocker locker(&m_pythonInterpreterMutex);
916
903 static char method[]="getRemainingFeatures"; 917 static char method[]="getRemainingFeatures";
904 cerr << "[call] " << method << endl; 918 cerr << "[call] " << method << endl;
905 919
906 //check if the method is implemented 920 //check if the method is implemented
907 mutex = true;
908 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) { 921 if ( ! PyObject_HasAttrString(m_pyInstance,method) ) {
909 return FeatureSet(); 922 return FeatureSet();
910 } 923 }
911 924
912 PyObject *pyMethod = PyString_FromString(method); 925 PyObject *pyMethod = PyString_FromString(method);
924 cerr << "ERROR: In Python plugin [" << m_class << "::" << method 937 cerr << "ERROR: In Python plugin [" << m_class << "::" << method
925 << "] Expected List return type." << endl; 938 << "] Expected List return type." << endl;
926 } 939 }
927 Py_CLEAR(pyMethod); 940 Py_CLEAR(pyMethod);
928 Py_CLEAR(pyOutputList); 941 Py_CLEAR(pyOutputList);
929 mutex = false;
930 return FeatureSet(); 942 return FeatureSet();
931 } 943 }
932 Py_DECREF(pyMethod); 944 Py_DECREF(pyMethod);
933 945
934 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue; 946 PyObject *pyFeatureList, *pyDict, *pyKey, *pyValue;
977 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl; 989 if (emptyFeature) cerr << "Warning: This feature is empty or badly formatted." << endl;
978 else returnFeatures[i].push_back(feature); 990 else returnFeatures[i].push_back(feature);
979 }// for j 991 }// for j
980 }//for i 992 }//for i
981 Py_CLEAR(pyOutputList); 993 Py_CLEAR(pyOutputList);
982 mutex = false;
983 return returnFeatures; 994 return returnFeatures;
984 } 995 }
985 996