Embedding Python in Multi-Threaded C/C++ Applications
Once your C thread is no longer using the Python interpreter, you must dispose of its resources. To do this, delete your PyThreadState object. This is accomplished with the following code:
// grab the lock PyEval_AcquireLock(); // swap my thread state out of the interpreter PyThreadState_Swap(NULL); // clear out any cruft from thread state object PyThreadState_Clear(myThreadState); // delete my thread state object PyThreadState_Delete(myThreadState); // release the lock PyEval_ReleaseLock();
This thread is now effectively done using the Python API. You may safely call pthread_exit at this point to halt execution of the thread.
Once your application has finished using the Python interpreter, you can shut down Python support with the following code:
// shut down the interpreter PyEval_AcquireLock(); Py_Finalize();
Note there is no reason to release the lock, because Python has been shut down. Be certain to delete all your thread-state objects with PyThreadState_Clear and PyThreadState_Delete before calling Py_Finalize.
Python is a good choice for use as an embedded language. The interpreter provides support for both embedding and extending, which allows two-way communication between C application code and embedded Python scripts. In addition, the threading support facilitates integration with multi-threaded applications without compromising performance.
You can download example source code at ftp.linuxjournal.com/pub/lj/listings/issue73/3641.tgz. This includes an example implementation of a multi-threaded HTTP server with an embedded Python interpreter. In order to learn more about the implementation details, I recommend reading the Python C API documentation at http://www.python.org/docs/api/. In addition, I have found the Python interpreter code itself to be an invaluable reference.
email: ivan@torpid.com
Ivan Pulleyn can be reached via e-mail at ivan@torpid.com.
- « first
- ‹ previous
- 1
- 2
- 3
- 4
Today’s modular x86 servers are compute-centric, designed as a least common denominator to support a wide range of IT workloads. Those generic, virtualized IT workloads have much different resource optimization requirements than hyperscale and cloud applications. They have resulted in a “one size fits all” enterprise IT architecture that is not optimized for a specific set of IT workloads, and especially not emerging hyperscale workloads, such as web applications, big data, and object storage. In this report, you will learn how shifting the focus from traditional compute-centric IT architectures to an innovative disaggregated fabric-based architecture can optimize and scale your data center.
Sponsored by AMD
Built-in forensics, incident response, and security with Red Hat Enterprise Linux 6
Every security policy provides guidance and requirements for ensuring adequate protection of information and data, as well as high-level technical and administrative security requirements for a system in a given environment. Traditionally, providing security for a system focuses on the confidentiality of the information on it. However, protecting the data integrity and system and data availability is just as important. For example, when processing United States intelligence information, there are three attributes that require protection: confidentiality, integrity, and availability.
Learn more about catching the bad guy in this free white paper.
Sponsored by DLT Solutions
| Making Linux and Android Get Along (It's Not as Hard as It Sounds) | May 16, 2013 |
| Drupal Is a Framework: Why Everyone Needs to Understand This | May 15, 2013 |
| Home, My Backup Data Center | May 13, 2013 |
| Non-Linux FOSS: Seashore | May 10, 2013 |
| Trying to Tame the Tablet | May 08, 2013 |
| Dart: a New Web Programming Experience | May 07, 2013 |
- New Products
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Drupal Is a Framework: Why Everyone Needs to Understand This
- A Topic for Discussion - Open Source Feature-Richness?
- Home, My Backup Data Center
- What's the tweeting protocol?
- New Products
- Readers' Choice Awards
- RSS Feeds
- Dart: a New Web Programming Experience
- Reply to comment | Linux Journal
11 hours 47 min ago - Reply to comment | Linux Journal
14 hours 20 min ago - Reply to comment | Linux Journal
15 hours 37 min ago - great post
16 hours 12 min ago - Google Docs
16 hours 34 min ago - Reply to comment | Linux Journal
21 hours 23 min ago - Reply to comment | Linux Journal
22 hours 10 min ago - Web Hosting IQ
23 hours 43 min ago - Thanks for taking the time to
1 day 1 hour ago - Linux is good
1 day 3 hours ago




Comments
Still getting crashes...
Thanks for the article, helped to understand the GIL a little more.
Since python 2.3 you can do the whole GIL lock things with the GILState_Ensure and Release functions. Look at my code:
class CExecuteHandler { public: CExecuteHandler(CHandler *, PyObject *); ~CExecuteHandler(); CHandler *handler; /* a class where python functions are saved in a vector*/ PyObject *args; }; void _ExecuteHandler(void *_ExecHandler) { CExecuteHandler *ExecHandler = (CExecuteHandler *)_ExecHandler; CHandler *Handler = ExecHandler->handler; for( vector::iterator j = Handler->m_PyFunctions.begin(); j != Handler->m_PyFunctions.end(); j++ ) { PyGILState_STATE gilState = PyGILState_Ensure(); PyObject *result = PyObject_CallObject( *j, Thread->args ); if(!result) PyErr_Print(); else Py_DECREF(result); PyGILState_Release(gilState); } delete Thread; #ifdef WIN32 _endthread(); #else pthread_exit(NULL); #endif } void ExecuteHandler(CHandler *i, PyObject *args) { CExecuteHandler *ExecHandler = new CExecuteHandler( *i, args ); #ifdef WIN32 _beginthread( _ExecuteHandler, 0, (void *)ExecHandler ); #else pthread_create( &thread, NULL, _ExecuteHandler, (void*)ExecHandler ); #endif }kay this was the code basically. So again the handler class saves a python function pointer of a certain event. E.g. if i want to call a python function when (lets suppose you coded a chat program) some sends a message to others, you call the CHandler fitting to "ChatMessage" with arguments built like Py_BuildValue("(ss)", playerName, message) and call ExecuteHandler(handler, args /* built with above BuildValue */). The problem is then if someone excessively spams and there are many many threads which call the function, the program crashes sometime.
Full code can be seen at:
http://pyghost.googlecode.com
Using PyGILState_Ensure/PyGILState_Release
contructor:
-----------
PyGILState_Ensure ONLY ensure that one thread use the same PyThreadState;
if two threads call PyGILState_Ensure,
one thread might invalidate the PyThreadState of the other WITHOUT locking!
(See the source of Python; Python/pystate.c)
PyGILState_Ensure:
tcur = (PyThreadState *)PyThread_get_key_value(autoTLSkey); if (tcur == NULL) { /* Create a new thread state for this thread */ tcur = PyThreadState_New(autoInterpreterState); if (tcur == NULL) Py_FatalError("Couldn't create thread-state for new thread"); /* This is our thread state! We'll need to delete it in the matching call to PyGILState_Release(). */ tcur->gilstate_counter = 0; current = 0; /* new thread state is never current */ } else current = PyThreadState_IsCurrent(tcur); if (current == 0) PyEval_RestoreThread(tcur);Locking is done in PyEval_RestoreThread(See the source in Python/ceval.c),
which called only if current = 0, i.e.,
there is no saved PyThreadState(_PyThreadState_Current in terms of pystate.c).
So one MUST have to call PyEval_SaveThread not just PyEval_ReleaseLock!!!
Destructor:
-----------
Since there is no explicit PyThreadState in main thread,(See the contructor above.)
one MUST restore PyThreadState of main thread by PyEval_RestoreThread.
Otherwise there is segmentation fault because Py_Finalize use the current PyThreadState!
(See the source of Python; Python/pythonrun.c)
Py_Finalize:
Note that there is one pair; one is PyEval_SaveThread in contructor,
the other PyEval_RestoreThread in destructor.
There is another pair in PyGILState_Ensure(PyEval_RestoreThread) and
PyGILState_Release(PyEval_SaveThread).
The overall structures for multi-threaded Python/C API calling look like:
Main thread:
New thread:
How does this code look if you use PyGILState_Ensure/Release?
I wonder how you implement this example using the PyGILState API that was introduced in version 2.3? Does the PyGILState_Ensure replace this, for example:
...
#idfef USE_GILSTATE
PyGILState* state = PyGILState_Ensure();
#else
// get the global lock
PyEval_AcquireLock();
// get a reference to the PyInterpreterState
PyInterpreterState * mainInterpreterState = mainThreadState->interp;
// create a thread state object for this thread
PyThreadState * myThreadState = PyThreadState_New(mainInterpreterState);
// free the lock
PyEval_ReleaseLock();
#endif
and likewise
...
#ifdef USE_GILSTATE
PyGILState_Release(state);
#else
// grab the lock
PyEval_AcquireLock();
// swap my thread state out of the interpreter
PyThreadState_Swap(NULL);
// clear out any cruft from thread state object
PyThreadState_Clear(myThreadState);
// delete my thread state object
PyThreadState_Delete(myThreadState);
// release the lock
PyEval_ReleaseLock();
#endif // USE_GILSTATE
...
I also found that if you run the original example in version 2.4 and have python compiled with Py_DEBUG defined, you will get fatal errors in pystate.c. The reason is that we can't have more than one thread state per thread:
The exception is thrown from
pystate.c, line 306:
Py_FatalError("Invalid thread state for this thread");
Has anybody else tried it?
agree, (PyGILState_*) is much simpler
This much more simplier locking model (PyGILState*())was introduced in the python2.3.
In my app each call of the embeded python code is locked by object of this class:
class PythonThreadLocker
{
PyGILState_STATE state;
public:
PythonThreadLocker() : state(PyGILState_Ensure())
{}
~PythonThreadLocker() {
PyGILState_Release(state);
}
};
It works safely. I must confess, that at first I wrote special singleton, which stored interpreted states for each thread (with API which is described in article), and then I found this very handy PyGILState_(Ensure/Realise).
I found this usage on koders.com, while quering "PyGILState_Ensure", thanks for the aiming:)
example.. missing?
Seems like the example code contains only code snippets from the article. Am I missing something? :) (i.e. no mentioned "http server with embedded python" thing :))
Done to perfection
Thanks for this useful article. We're embedding into a Win32 C++ multi-threaded app.
Ditto on the above comment -- needed to add a step to shutdown: swap the main thread state back in before shutting down the interpreter.
extending instead of embedding
With python 2.2, I am using an audio library (portaudio) that uses callbacks for
audio buffer filling. This is extending rather than embedding.
First of all:
PyInterpreterState * mis;
PyThreadState * mts;
mts = PyThreadState_Get();
mis = mts->interp;
ts = PyThreadState_New(mis); /* stored away somewhere */
Note: we don't need to PyEval_AcquireLock, as we already have the lock.
Inside the callback:
PyEval_AcquireLock();
PyThreadState_Swap(ts);
/* call python code here */
PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
Finishing up:
PyThreadState_Swap(NULL);
PyThreadState_Clear(ts);
PyThreadState_Delete(ts);
Also, I found it necessary to do
PyEval_InitThreads();
before all the above.
Simon.
Re: extending instead of embedding
Thanks :-)
extending instead of embedding
With python 2.2, I am using an audio library (portaudio) that uses callbacks for
audio buffer filling. This is extending rather than embedding.
First of all:
PyInterpreterState * mis;
PyThreadState * mts;
mts = PyThreadState_Get();
mis = mts->interp;
ts = PyThreadState_New(mis); /* stored away somewhere */
Note: we don't need to PyEval_AcquireLock, as we already have the lock.
Inside the callback:
PyEval_AcquireLock();
PyThreadState_Swap(ts);
/* call python code here */
PyThreadState_Swap(NULL);
PyEval_ReleaseLock();
Finishing up:
PyThreadState_Swap(NULL);
PyThreadState_Clear(ts);
PyThreadState_Delete(ts);
Also, I found it necessary to do
PyEval_InitThreads();
before all the above.
Simon.
Re: extending instead of embedding
ok, i previewed OK this but it ignored pre markers in the final post...
doh!
Re: Embedding Python in Multi-Threaded C/C++ Applications
excellent resource!
Good tutorial, forgot swap to main before Finalize
Shutting down the interpreter should have
// shut down the interpreter
PyEval_AcquireLock();
PyThreadState_Swap(mainThreadState);
Py_Finalize();
otherwise you get this error message and segfault
Fatal Python error: PyThreadState_Get: no current thread
Thanks.
Re: Embedding Python in Multi-Threaded C/C++ Applications
Excellent!
Re: Embedding Python in Multi-Threaded C/C++ Applications
This article is so useful to make sense out of Python's involvement with threads that it should be added to the standard documentation shipping with the language.
It just helped me to sove a problem that I had been wrestling with for 24 hours.
Regards,
Fabien.
Re: Embedding Python in Multi-Threaded C/C++ Applications
thank you - it was stright forward to create an extension with a separate thread and a callback...
it saved quite some time.
Very good article. Helped me
Very good article. Helped me solve a problem i was investigating for two days now.