Skip to content

[3.7] bpo-32604: Swap threads only if the interpreter is different. (gh-5778) #5783

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

Merged
merged 1 commit into from
Feb 21, 2018
Merged
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
49 changes: 49 additions & 0 deletions Lib/test/test__xxsubinterpreters.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import pickle
from textwrap import dedent, indent
import threading
import time
import unittest

from test import support
Expand Down Expand Up @@ -1147,6 +1148,54 @@ def test_send_recv_different_interpreters(self):

self.assertEqual(obj, b'spam')

def test_send_recv_different_threads(self):
cid = interpreters.channel_create()

def f():
while True:
try:
obj = interpreters.channel_recv(cid)
break
except interpreters.ChannelEmptyError:
time.sleep(0.1)
interpreters.channel_send(cid, obj)
t = threading.Thread(target=f)
t.start()

interpreters.channel_send(cid, b'spam')
t.join()
obj = interpreters.channel_recv(cid)

self.assertEqual(obj, b'spam')

def test_send_recv_different_interpreters_and_threads(self):
cid = interpreters.channel_create()
id1 = interpreters.create()
out = None

def f():
nonlocal out
out = _run_output(id1, dedent(f"""
import time
import _xxsubinterpreters as _interpreters
while True:
try:
obj = _interpreters.channel_recv({int(cid)})
break
except _interpreters.ChannelEmptyError:
time.sleep(0.1)
assert(obj == b'spam')
_interpreters.channel_send({int(cid)}, b'eggs')
"""))
t = threading.Thread(target=f)
t.start()

interpreters.channel_send(cid, b'spam')
t.join()
obj = interpreters.channel_recv(cid)

self.assertEqual(obj, b'eggs')

def test_send_not_found(self):
with self.assertRaises(interpreters.ChannelNotFoundError):
interpreters.channel_send(10, b'spam')
Expand Down
22 changes: 14 additions & 8 deletions Modules/_xxsubinterpretersmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -1759,8 +1759,13 @@ _run_script_in_interpreter(PyInterpreterState *interp, const char *codestr,
}

// Switch to interpreter.
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
PyThreadState *save_tstate = NULL;
if (interp != PyThreadState_Get()->interp) {
// XXX Using the "head" thread isn't strictly correct.
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
// XXX Possible GILState issues?
save_tstate = PyThreadState_Swap(tstate);
}

// Run the script.
_sharedexception *exc = NULL;
Expand Down Expand Up @@ -2079,9 +2084,9 @@ interp_create(PyObject *self, PyObject *args)
}

// Create and initialize the new interpreter.
PyThreadState *tstate, *save_tstate;
save_tstate = PyThreadState_Swap(NULL);
tstate = Py_NewInterpreter();
PyThreadState *save_tstate = PyThreadState_Swap(NULL);
// XXX Possible GILState issues?
PyThreadState *tstate = Py_NewInterpreter();
PyThreadState_Swap(save_tstate);
if (tstate == NULL) {
/* Since no new thread state was created, there is no exception to
Expand All @@ -2096,6 +2101,7 @@ interp_create(PyObject *self, PyObject *args)
return _get_id(tstate->interp);

error:
// XXX Possible GILState issues?
save_tstate = PyThreadState_Swap(tstate);
Py_EndInterpreter(tstate);
PyThreadState_Swap(save_tstate);
Expand Down Expand Up @@ -2146,9 +2152,9 @@ interp_destroy(PyObject *self, PyObject *args)

// Destroy the interpreter.
//PyInterpreterState_Delete(interp);
PyThreadState *tstate, *save_tstate;
tstate = PyInterpreterState_ThreadHead(interp);
save_tstate = PyThreadState_Swap(tstate);
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
// XXX Possible GILState issues?
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
Py_EndInterpreter(tstate);
PyThreadState_Swap(save_tstate);

Expand Down
20 changes: 14 additions & 6 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -331,9 +331,10 @@ _PyInterpreterState_IDDecref(PyInterpreterState *interp)
PyThread_release_lock(interp->id_mutex);

if (refcount == 0) {
PyThreadState *tstate, *save_tstate;
tstate = PyInterpreterState_ThreadHead(interp);
save_tstate = PyThreadState_Swap(tstate);
// XXX Using the "head" thread isn't strictly correct.
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
// XXX Possible GILState issues?
PyThreadState *save_tstate = PyThreadState_Swap(tstate);
Py_EndInterpreter(tstate);
PyThreadState_Swap(save_tstate);
}
Expand Down Expand Up @@ -1213,8 +1214,14 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
}
return;
}
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
PyThreadState *save_tstate = PyThreadState_Swap(tstate);

PyThreadState *save_tstate = NULL;
if (interp != PyThreadState_Get()->interp) {
// XXX Using the "head" thread isn't strictly correct.
PyThreadState *tstate = PyInterpreterState_ThreadHead(interp);
// XXX Possible GILState issues?
save_tstate = PyThreadState_Swap(tstate);
}

// "Release" the data and/or the object.
if (data->free != NULL) {
Expand All @@ -1223,8 +1230,9 @@ _PyCrossInterpreterData_Release(_PyCrossInterpreterData *data)
Py_XDECREF(data->obj);

// Switch back.
if (save_tstate != NULL)
if (save_tstate != NULL) {
PyThreadState_Swap(save_tstate);
}
}

PyObject *
Expand Down