Mercurial > hg > vampy
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 |