Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrap render function in SWIG threads macro, to disable Python GIL #102

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# RPi WS281x Python

This is the official Python distribution of the ws281x library: http://github.com/richardghirst/rpi_ws281x
This is the official Python distribution of the ws281x library: https://github.com/jgarff/rpi_ws281x

# Installing

Expand All @@ -11,3 +11,22 @@ Most users should simply run:
```
sudo pip install rpi_ws281x
```

## Building

Clone with submodules, and enter library directory:
```
git clone --recurse-submodules https://github.com/rpi-ws281x/rpi-ws281x-python.git
cd rpi-ws281x-python/library
```
To rebuild SWIG files if needed ("black" for code re-formatting only):
```
swig -python -threads rpi_ws281x.i
black rpi_ws281x.py
```

Build and Install:
```
python3 setup.py build
sudo python3 setup.py install
```
11 changes: 11 additions & 0 deletions library/rpi_ws281x.i
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// SWIG interface file to define rpi_ws281x library python wrapper.
// Author: Tony DiCola ([email protected]), Jeremy Garff ([email protected])

%nothread;

// Define module name rpi_ws281x. This will actually be imported under
// the name _rpi_ws281x following the SWIG & Python conventions.
%module rpi_ws281x
Expand Down Expand Up @@ -92,3 +94,12 @@ static int convert_iarray(PyObject *input, uint8_t *ptr, int size) {
return &ws->channel[channelnum];
}
%}

%thread;
%inline %{
ws2811_return_t ws2811_render_nogil(ws2811_t *ws2811)
{
return ws2811_render(ws2811);
}
%}
%nothread;
4 changes: 4 additions & 0 deletions library/rpi_ws281x.py
Original file line number Diff line number Diff line change
Expand Up @@ -221,3 +221,7 @@ def ws2811_led_set(channel, lednum, color):

def ws2811_channel_get(ws, channelnum):
return _rpi_ws281x.ws2811_channel_get(ws, channelnum)


def ws2811_render_nogil(ws2811):
return _rpi_ws281x.ws2811_render_nogil(ws2811)
18 changes: 13 additions & 5 deletions library/rpi_ws281x/rpi_ws281x.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# Author: Tony DiCola ([email protected]), Jeremy Garff ([email protected])
import _rpi_ws281x as ws
import atexit

from threading import Lock

class RGBW(int):
def __new__(self, r, g=None, b=None, w=None):
Expand Down Expand Up @@ -50,6 +50,9 @@ def __init__(self, num, pin, freq_hz=800000, dma=10, invert=False,
channel, the PWM channel to use (defaults to 0).
"""

self._mutex = Lock()
self._render_func = ws.ws2811_render

if gamma is None:
# Support gamma in place of strip_type for back-compat with
# previous version of forked library
Expand Down Expand Up @@ -142,12 +145,17 @@ def begin(self):
str_resp = ws.ws2811_get_return_t_str(resp)
raise RuntimeError('ws2811_init failed with code {0} ({1})'.format(resp, str_resp))

def releaseGIL(self, release = True)
"""Setup option to release GIL during render function."""
self._render_func = ws.ws2811_render_nogil if release else ws.ws2811_render

def show(self):
"""Update the display with the data from the LED buffer."""
resp = ws.ws2811_render(self._leds)
if resp != 0:
str_resp = ws.ws2811_get_return_t_str(resp)
raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, str_resp))
with self._mutex:
resp = self._render_func(self._leds)
if resp != 0:
str_resp = ws.ws2811_get_return_t_str(resp)
raise RuntimeError('ws2811_render failed with code {0} ({1})'.format(resp, str_resp))

def setPixelColor(self, n, color):
"""Set LED at position n to the provided 24-bit color value (in RGB order).
Expand Down
38 changes: 38 additions & 0 deletions library/rpi_ws281x_wrap.c
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#define SWIGPYTHON
#endif

#define SWIG_PYTHON_THREADS
#define SWIG_PYTHON_DIRECTOR_NO_VTABLE

/* -----------------------------------------------------------------------------
Expand Down Expand Up @@ -3132,6 +3133,12 @@ SWIG_FromCharPtr(const char *cptr)
return &ws->channel[channelnum];
}


ws2811_return_t ws2811_render_nogil(ws2811_t *ws2811)
{
return ws2811_render(ws2811);
}

#ifdef __cplusplus
extern "C" {
#endif
Expand Down Expand Up @@ -4370,6 +4377,33 @@ SWIGINTERN PyObject *_wrap_ws2811_channel_get(PyObject *SWIGUNUSEDPARM(self), Py
}


SWIGINTERN PyObject *_wrap_ws2811_render_nogil(PyObject *SWIGUNUSEDPARM(self), PyObject *args) {
PyObject *resultobj = 0;
ws2811_t *arg1 = (ws2811_t *) 0 ;
void *argp1 = 0 ;
int res1 = 0 ;
PyObject *swig_obj[1] ;
ws2811_return_t result;

if (!args) SWIG_fail;
swig_obj[0] = args;
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_ws2811_t, 0 | 0 );
if (!SWIG_IsOK(res1)) {
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "ws2811_render_nogil" "', argument " "1"" of type '" "ws2811_t *""'");
}
arg1 = (ws2811_t *)(argp1);
{
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
result = (ws2811_return_t)ws2811_render_nogil(arg1);
SWIG_PYTHON_THREAD_END_ALLOW;
}
resultobj = SWIG_From_int((int)(result));
return resultobj;
fail:
return NULL;
}


static PyMethodDef SwigMethods[] = {
{ "SWIG_PyInstanceMethod_New", SWIG_PyInstanceMethod_New, METH_O, NULL},
{ "ws2811_channel_t_gpionum_set", _wrap_ws2811_channel_t_gpionum_set, METH_VARARGS, NULL},
Expand Down Expand Up @@ -4423,6 +4457,7 @@ static PyMethodDef SwigMethods[] = {
{ "ws2811_led_get", _wrap_ws2811_led_get, METH_VARARGS, NULL},
{ "ws2811_led_set", _wrap_ws2811_led_set, METH_VARARGS, NULL},
{ "ws2811_channel_get", _wrap_ws2811_channel_get, METH_VARARGS, NULL},
{ "ws2811_render_nogil", _wrap_ws2811_render_nogil, METH_O, NULL},
{ NULL, NULL, 0, NULL }
};

Expand Down Expand Up @@ -5263,6 +5298,9 @@ SWIG_init(void) {
SWIG_Python_SetConstant(d, "WS2811_ERROR_SPI_SETUP",SWIG_From_int((int)(WS2811_ERROR_SPI_SETUP)));
SWIG_Python_SetConstant(d, "WS2811_ERROR_SPI_TRANSFER",SWIG_From_int((int)(WS2811_ERROR_SPI_TRANSFER)));
SWIG_Python_SetConstant(d, "WS2811_RETURN_STATE_COUNT",SWIG_From_int((int)(WS2811_RETURN_STATE_COUNT)));

/* Initialize threading */
SWIG_PYTHON_INITIALIZE_THREADS;
#if PY_VERSION_HEX >= 0x03000000
return m;
#else
Expand Down