Skip to content

Commit c2b83d2

Browse files
authored
Merge pull request #46 from mrkn/gvl
Release GVL while the Python interpreter is running [Fix #45]
2 parents f950b04 + 6dc02ce commit c2b83d2

File tree

14 files changed

+324
-126
lines changed

14 files changed

+324
-126
lines changed

.travis.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,10 @@ matrix:
3939
compiler: clang
4040
rvm: 2.4.1
4141
env: PYENV_VERSION=miniconda3-4.3.11
42-
allow_failure:
42+
allow_failures:
4343
- os: osx
44+
- rvm: 2.2.8
45+
- rvm: 2.1.10
4446

4547
before_install:
4648
- gem update --system

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@
66

77
*Naoto Takai*
88

9+
* Release GVL while the Python interpreter is running
10+
11+
* Drop support Ruby 2.2.x and 2.1.x
12+
913
## 1.0.3
1014

1115
* Fix anaconda support to define the environment variable `PYTHONHOME`.

Rakefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Rake::ExtensionTask.new('pycall', gem_spec) do |ext|
1717
end
1818
end
1919

20+
Rake::ExtensionTask.new('pycall/spec_helper')
21+
2022
desc "Compile binaries for mingw platform using rake-compiler-dock"
2123
task 'build:mingw' do
2224
require 'rake_compiler_dock'

appveyor.yml

Lines changed: 10 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,37 @@
11
---
22
environment:
33
matrix:
4-
# Ruby 2.1 (32bit)
5-
- ruby_version: "21"
4+
# Ruby 2.4 (32bit)
5+
- ruby_version: "24"
66
PYTHONDIR: "C:\\Python27"
77
PYTHON: "C:\\Python27\\python.exe"
88

9-
- ruby_version: "21"
9+
- ruby_version: "24"
1010
PYTHONDIR: "C:\\Python34"
1111
PYTHON: "C:\\Python34\\python.exe"
1212

13-
- ruby_version: "21"
13+
- ruby_version: "24"
1414
PYTHONDIR: "C:\\Python35"
1515
PYTHON: "C:\\Python35\\python.exe"
1616

17-
- ruby_version: "21"
17+
- ruby_version: "24"
1818
PYTHONDIR: "C:\\Python36"
1919
PYTHON: "C:\\Python36\\python.exe"
2020

21-
# Ruby 2.1 (64bit)
22-
- ruby_version: "21-x64"
21+
# Ruby 2.4 (64bit)
22+
- ruby_version: "24-x64"
2323
PYTHONDIR: "C:\\Python27-x64"
2424
PYTHON: "C:\\Python27-x64\\python.exe"
2525

26-
- ruby_version: "21-x64"
26+
- ruby_version: "24-x64"
2727
PYTHONDIR: "C:\\Python34-x64"
2828
PYTHON: "C:\\Python34-x64\\python.exe"
2929

30-
- ruby_version: "21-x64"
30+
- ruby_version: "24-x64"
3131
PYTHONDIR: "C:\\Python35-x64"
3232
PYTHON: "C:\\Python35-x64\\python.exe"
3333

34-
- ruby_version: "21-x64"
35-
PYTHONDIR: "C:\\Python36-x64"
36-
PYTHON: "C:\\Python36-x64\\python.exe"
37-
38-
# Ruby 2.2 (32bit)
39-
- ruby_version: "22"
40-
PYTHONDIR: "C:\\Python27"
41-
PYTHON: "C:\\Python27\\python.exe"
42-
43-
- ruby_version: "22"
44-
PYTHONDIR: "C:\\Python34"
45-
PYTHON: "C:\\Python34\\python.exe"
46-
47-
- ruby_version: "22"
48-
PYTHONDIR: "C:\\Python35"
49-
PYTHON: "C:\\Python35\\python.exe"
50-
51-
- ruby_version: "22"
52-
PYTHONDIR: "C:\\Python36"
53-
PYTHON: "C:\\Python36\\python.exe"
54-
55-
# Ruby 2.2 (64bit)
56-
- ruby_version: "22-x64"
57-
PYTHONDIR: "C:\\Python27-x64"
58-
PYTHON: "C:\\Python27-x64\\python.exe"
59-
60-
- ruby_version: "22-x64"
61-
PYTHONDIR: "C:\\Python34-x64"
62-
PYTHON: "C:\\Python34-x64\\python.exe"
63-
64-
- ruby_version: "22-x64"
65-
PYTHONDIR: "C:\\Python35-x64"
66-
PYTHON: "C:\\Python35-x64\\python.exe"
67-
68-
- ruby_version: "22-x64"
34+
- ruby_version: "24-x64"
6935
PYTHONDIR: "C:\\Python36-x64"
7036
PYTHON: "C:\\Python36-x64\\python.exe"
7137

ext/pycall/libpython.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,16 @@ pycall_init_libpython_api_table(VALUE libpython_handle)
154154

155155
INIT_API_TABLE_ENTRY(PyIter_Next, required);
156156

157+
INIT_API_TABLE_ENTRY(PyEval_ThreadsInitialized, required);
158+
INIT_API_TABLE_ENTRY(PyEval_InitThreads, required);
159+
157160
INIT_API_TABLE_ENTRY(PyErr_Occurred, required);
158161
INIT_API_TABLE_ENTRY(PyErr_Fetch, required);
159162
INIT_API_TABLE_ENTRY(PyErr_Restore, required);
160163
INIT_API_TABLE_ENTRY(PyErr_Clear, required);
161164
INIT_API_TABLE_ENTRY(PyErr_SetString, required);
162165
INIT_API_TABLE_ENTRY(PyErr_Format, required);
166+
INIT_API_TABLE_ENTRY(PyErr_SetInterrupt, required);
163167

164168
INIT_API_TABLE_ENTRY(PyImport_ImportModule, required);
165169
INIT_API_TABLE_ENTRY(PyImport_ImportModuleLevel, required);

ext/pycall/pycall.c

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,42 @@ pycall_extract_kwargs_from_ruby_hash(VALUE key, VALUE value, VALUE arg)
858858
return ST_CONTINUE;
859859
}
860860

861+
static void
862+
pycall_interrupt_python_thread(void *ptr)
863+
{
864+
Py_API(PyErr_SetInterrupt)();
865+
}
866+
867+
struct call_pyobject_call_params {
868+
PyObject *pycallable;
869+
PyObject *args;
870+
PyObject *kwargs;
871+
};
872+
873+
PyObject *
874+
call_pyobject_call(struct call_pyobject_call_params *params)
875+
{
876+
PyObject *res;
877+
res = Py_API(PyObject_Call)(params->pycallable, params->args, params->kwargs); /* New reference */
878+
return res;
879+
}
880+
881+
PyObject *
882+
pyobject_call_without_gvl(PyObject *pycallable, PyObject *args, PyObject *kwargs)
883+
{
884+
PyObject *res;
885+
struct call_pyobject_call_params params;
886+
params.pycallable = pycallable;
887+
params.args = args;
888+
params.kwargs = kwargs;
889+
890+
res = (PyObject *)rb_thread_call_without_gvl(
891+
(void * (*)(void *))call_pyobject_call, (void *)&params,
892+
(rb_unblock_function_t *)pycall_interrupt_python_thread, NULL);
893+
894+
return res;
895+
}
896+
861897
static VALUE
862898
pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
863899
{
@@ -905,7 +941,7 @@ pycall_call_python_callable(PyObject *pycallable, int argc, VALUE *argv)
905941
}
906942
}
907943

908-
res = Py_API(PyObject_Call)(pycallable, args, kwargs); /* New reference */
944+
res = pyobject_call_without_gvl(pycallable, args, kwargs); /* New reference */
909945
if (!res) {
910946
pycall_pyerror_fetch_and_raise("PyObject_Call in pycall_call_python_callable");
911947
}
@@ -1947,6 +1983,10 @@ init_python(void)
19471983
Py_API(Py_InitializeEx)(0);
19481984
Py_API(PySys_SetArgvEx)(0, (char **)argv, 0);
19491985

1986+
if (!Py_API(PyEval_ThreadsInitialized)()) {
1987+
Py_API(PyEval_InitThreads)();
1988+
}
1989+
19501990
/* check the availability of stackless extension */
19511991
python_has_stackless_extension = (Py_API(PyImport_ImportModule)("stackless") != NULL);
19521992
if (!python_has_stackless_extension) {

ext/pycall/pycall_internal.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ extern "C" {
1010

1111
#include <ruby.h>
1212
#include <ruby/encoding.h>
13+
#include <ruby/thread.h>
1314
#include <assert.h>
1415
#include <inttypes.h>
1516
#include <limits.h>
@@ -34,6 +35,9 @@ pycall_integer_type_p(VALUE obj)
3435
#endif
3536

3637
void ruby_debug_breakpoint();
38+
int ruby_thread_has_gvl_p(void);
39+
40+
#define CALL_WITH_GVL(func, data) rb_thread_call_with_gvl((void * (*)(void *))(func), (void *)(data))
3741

3842
/* ==== python ==== */
3943

@@ -581,12 +585,16 @@ typedef struct {
581585

582586
PyObject * (* PyIter_Next)(PyObject *);
583587

588+
int (* PyEval_ThreadsInitialized)(void);
589+
void (* PyEval_InitThreads)(void);
590+
584591
PyObject * (* PyErr_Occurred)(void);
585592
void (* PyErr_Fetch)(PyObject **, PyObject **, PyObject **);
586593
void (* PyErr_Restore)(PyObject *, PyObject *, PyObject *);
587594
void (* PyErr_Clear)(void);
588595
void (* PyErr_SetString)(PyObject *, const char *); /* decoded from utf-8 */
589596
void (* PyErr_Format)(PyObject *, const char *, ...); /* ASCII-encoded string */
597+
void (* PyErr_SetInterrupt)(void);
590598

591599
PyObject * (* PyImport_ImportModule)(char const*);
592600
PyObject * (* PyImport_ImportModuleLevel)(char const*, PyObject *, PyObject *, PyObject *, int);

0 commit comments

Comments
 (0)