Embedding Python in Multi-Threaded C/C++ Applications

Python provides a clean intuitive interface to complex, threaded applications.

Developers often make use of high-level scripting languages as a way of quickly writing flexible code. Various shell scripting languages have long been used to automate processes on UNIX systems. More recently, software applications have begun to provide scripting layers that allow the user to automate common tasks or even extend the feature set. Think of all the well-known applications you use: GIMP, Emacs, Word, Photoshop, etc. It seems as though all can be scripted in some way.

In this article, I will describe how you can embed the Python language within your C applications. There are many reasons you would want to do this. For instance, you may want to provide your more advanced users with the ability to alter or customize the program. Or maybe you want to take advantage of a Python capability, rather than implement it yourself. Python is a good choice for this task because it provides a clean, intuitive C API. Since many complex applications are written using threads, I will also show you how to create a thread-safe interface to the Python interpreter.

All the examples assume you are using Python version 1.5.2, which comes pre-installed on most recent Linux distributions. The API to access the Python interpreter is the same for both C and C++. There are no special C++ constructs used, and all functions are declared extern “C”. For this reason, the concepts described and the example code given here should work equally well when using either C or C++.

Overview of the Python C/C++ API

There are two ways that C and Python code can work together within the same process. Simply put, Python code can call C code or C code can call Python code. These two methods are called “extending” and “embedding”, respectively. When extending, you create a new Python module implemented in C/C++. This allows you to provide new functionality to the Python language that cannot be implemented in Python. For instance, several core Python modules such as “time” and “nis” are implemented as C extensions, while others are written in Python. You never notice the difference between C and Python modules, because the act of importing and using these modules is the same. If you look around in your /usr/lib/python1.5 directory, you may see some shared library files (extension .so). These are Python module extensions written in C. You will also see various Python files (extension .py) which are modules written in Python.

Typically, when you embed Python, you will develop a C/C++ application that has the ability to load and execute Python scripts. The application will be linked against the Python interpreter library, called libpython1.5.a, which provides all functionality related to evaluating Python code. There is no Python executable involved, only an API for your application to use.

Embedded Python

Listing 1

Embedding Python is a relatively straightforward process. If your goal is merely to execute vanilla Python code from within a C program, it's actually quite easy. Listing 1 is the complete source to a program that embeds the Python interpreter. This illustrates one of the simplest programs you could write making use of the Python interpreter.

Listing 1 uses three Python-specific function calls. Py_Initialize starts up the Python interpreter library, causing it to allocate whatever internal resources it needs. You must call this function before calling most other functions in the Python API. PyEval_SimpleString provides a quick, no-frills way to execute arbitrary Python code. Interpretation of the code is immediate. In the above example, for instance, the import sys line causes Python to import the sys module before returning control to the C/C++ program. Each string passed to PyEval_SimpleString must be a complete Python statement of some kind. In other words, half statements are illegal, even if they are completed with another call to PyRun_SimpleString. For example, the following code will not work properly:

// Python will print first error here
PyRun_SimpleString("import ");<\n>
// Python will print second error here
PyRun_SimpleString("sys\n");<\n>

Py_Finalize is the last Python function which any application that embeds Python must call. This function shuts down the interpreter and frees any resources it allocated during its lifetime. You should call this when you are completely finished using the Python library. When you call Py_Finalize, Python will unload all imported modules one by one. Many modules must execute their own clean-up code when they are unloaded in order to free any global resources they may have allocated. For this reason, calling Py_Finalize can have the side effect of causing quite a bit of other code to run.

PyEval_SimpleString is just one way to execute Python code from within your C applications. In fact, there is a whole collection of similar high-level functions. PyEval_SimpleFile is just like PyEval_SimpleString, except it reads its input from a FILE pointer rather than a character buffer. See the Python documentation at www.python.org/docs/api/veryhigh.html for complete documentation on these high-level functions.

In addition to evaluating Python scripts, you can also manipulate Python objects and call Python functions directly from your C code. While this involves more complex C code than using PyEval_SimpleString, it also allows access to more detailed information. For example, you can access objects returned from Python functions or determine if an exception has been thrown.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

Still getting crashes...

Anonymous's picture

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

Gwang-Ho Kim's picture

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!!!

mainThreadState = PyEval_SaveThread();

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:

tstate = PyThreadState_GET();
interp = tstate->interp;        // <- At this point.
PyEval_RestoreThread(mainThreadState);

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:

// Constructor
Py_Initialize();
PyEval_InitThreads();
PyThreadState*  mainThreadState = PyEval_SaveThread();

......
PyGILState_STATE        gilState = PyGILState_Ensure(); // PyEval_RestoreThread
// Call Python/C API...
PyGILState_Release(gilState);                           // PyEval_SaveThread
......

// Create new thread...

......
PyGILState_STATE        gilState = PyGILState_Ensure(); // PyEval_RestoreThread
// Call Python/C API...
PyGILState_Release(gilState);                           // PyEval_SaveThread
......

// Destructor
PyEval_Restore(mainThreadState);
Py_Finalize();

New thread:

......
PyGILState_STATE        gilState = PyGILState_Ensure(); // PyEval_RestoreThread
// Call Python/C API...
PyGILState_Release(gilState);                           // PyEval_SaveThread
......

How does this code look if you use PyGILState_Ensure/Release?

freesteel's picture

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

vvk's picture

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?

F's picture

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

Anonymous's picture

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

mathgenius's picture

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

Anonymous's picture

Thanks :-)

extending instead of embedding

mathgenius's picture

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

mathgenius's picture

ok, i previewed OK this but it ignored pre markers in the final post...

doh!

Re: Embedding Python in Multi-Threaded C/C++ Applications

Anonymous's picture

excellent resource!

Good tutorial, forgot swap to main before Finalize

Anonymous's picture

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

Anonymous's picture

Excellent!

Re: Embedding Python in Multi-Threaded C/C++ Applications

Anonymous's picture

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

Anonymous's picture

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

Anonymous's picture

Very good article. Helped me solve a problem i was investigating for two days now.

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState