Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
10 lines (8 sloc) 2.54 KB
---
layout: post
title: "Managing the Python GIL via RAII"
date: 2009-10-06
comments: false
---
<div class='post'>
One of the killer features of C++ is <a href="http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization"><acronym title="Resource Acquisition is Initialization">RAII</acronym></a>. RAII means that the amount of special-case cleanup code in the case of exceptions or early exits is minimized. For more on exactly how this happens, I recommend the Wikipedia article linked above.<br /><br />This feature became useful to me when making our Python Kroll module work properly with the Python GIL. There were two usage patterns I was interested in for this work.<br /><br /><h3>Letting Python code in other threads run</h3><pre><code>PyThreadState* threadState = PyEval_SaveThread();<br />...do some expensive non-Python work here...<br />PyEval_RestoreThread(threadState);</code></pre><h3>Taking back the GIL to use the Python API</h3><pre><code>PyGILState gstate = PyGILState_Ensure();<br />...use the Python API here...<br />PyGILState_Release(gstate);</code></pre><br />The problem with this approach is that if an uncaught exception escapes from anywhere between the ellipses, the cleanup code (<tt>PyEval_RestoreThread</tt> or <tt>PyGILState_Release</tt>) will not run, likely leaving the program in a bad state. It turns out that RAII has a very elegant solution to this problem.<code><pre><br /> class PyLockGIL<br /> {<br /> PyLockGIL() : gstate(PyGILState_Ensure())<br /> { }<br /><br /> ~PyLockGIL()<br /> {<br /> PyGILState_Release(gstate);<br /> }<br /><br /> PyGILState_STATE gstate;<br /> };<br /><br /> class PyAllowThreads<br /> {<br /> PyAllowThreads() : threadState(PyEval_SaveThread())<br /> { }<br /><br /> ~PyAllowThreads()<br /> {<br /> PyEval_RestoreThread(threadState);<br /> }<br /><br /> PyThreadState* threadState;<br /> };<br /></code></pre>The acquisition and release of the GIL is just wrapped in the constructors and destructors of objects that are allocated on the stack. Here is the previous example using these new objects:<br /><pre><code><br />{<br /> PyLockGIL lock<br /> ...do some expensive non-Python work here...<br />}<br /><br />{<br /> PyAllowThreads allow;<br /> ...use the Python API here...<br />}<br /></code></pre>Notice that braces can be used to fine tune the amount of code with the desired GIL state. The destructor for these objects will be called as soon as they go out of scope.</div>