Author: Armin Rigo <[email protected]>
Branch:
Changeset: r60607:05672556b297
Date: 2013-01-28 14:28 +0100
http://bitbucket.org/pypy/pypy/changeset/05672556b297/
Log: Replace the fire_after_thread_switch() logic with different logic,
which is just as subtle as the original one but maybe more correct
:-/
diff --git a/pypy/interpreter/executioncontext.py
b/pypy/interpreter/executioncontext.py
--- a/pypy/interpreter/executioncontext.py
+++ b/pypy/interpreter/executioncontext.py
@@ -343,9 +343,13 @@
signal, the tick counter is set to -1 by C code in signals.h.
"""
assert isinstance(action, PeriodicAsyncAction)
- self._periodic_actions.append(action)
+ # hack to put the release-the-GIL one at the end of the list,
+ # and the report-the-signals one at the start of the list.
if use_bytecode_counter:
+ self._periodic_actions.append(action)
self.has_bytecode_counter = True
+ else:
+ self._periodic_actions.insert(0, action)
self._rebuild_action_dispatcher()
def getcheckinterval(self):
@@ -419,15 +423,6 @@
The action must have been registered at space initalization time."""
self.space.actionflag.fire(self)
- def fire_after_thread_switch(self):
- """Bit of a hack: fire() the action but only the next time the GIL
- is released and re-acquired (i.e. after a potential thread switch).
- Don't call this if threads are not enabled. Currently limited to
- one action (i.e. reserved for CheckSignalAction from module/signal).
- """
- from pypy.module.thread.gil import spacestate
- spacestate.action_after_thread_switch = self
-
def perform(self, executioncontext, frame):
"""To be overridden."""
diff --git a/pypy/module/signal/interp_signal.py
b/pypy/module/signal/interp_signal.py
--- a/pypy/module/signal/interp_signal.py
+++ b/pypy/module/signal/interp_signal.py
@@ -8,7 +8,8 @@
PeriodicAsyncAction)
from pypy.interpreter.gateway import unwrap_spec
-from rpython.rlib import jit, rposix
+from rpython.rlib import jit, rposix, rgc
+from rpython.rlib.objectmodel import we_are_translated
from rpython.rlib.rarithmetic import intmask
from rpython.rlib.rsignal import *
from rpython.rtyper.lltypesystem import lltype, rffi
@@ -31,6 +32,11 @@
p = pypysig_getaddr_occurred()
p.c_value = value
+ @staticmethod
+ def rearm_ticker():
+ p = pypysig_getaddr_occurred()
+ p.c_value = -1
+
def decrement_ticker(self, by):
p = pypysig_getaddr_occurred()
value = p.c_value
@@ -46,41 +52,62 @@
class CheckSignalAction(PeriodicAsyncAction):
"""An action that is automatically invoked when a signal is received."""
+ # Note that this is a PeriodicAsyncAction: it means more precisely
+ # that it is called whenever the C-level ticker becomes < 0.
+ # Without threads, it is only ever set to -1 when we receive a
+ # signal. With threads, it also decrements steadily (but slowly).
+
def __init__(self, space):
+ "NOT_RPYTHON"
AsyncAction.__init__(self, space)
self.handlers_w = {}
- self.emulated_sigint = False
+ self.pending_signal = -1
+ self.fire_in_main_thread = False
+ if self.space.config.objspace.usemodules.thread:
+ from pypy.module.thread import gil
+ gil.after_thread_switch = self._after_thread_switch
+
+ @rgc.no_collect
+ def _after_thread_switch(self):
+ if self.fire_in_main_thread:
+ if self.space.threadlocals.ismainthread():
+ self.fire_in_main_thread = False
+ SignalActionFlag.rearm_ticker()
+ # this occurs when we just switched to the main thread
+ # and there is a signal pending: we force the ticker to
+ # -1, which should ensure perform() is called quickly.
@jit.dont_look_inside
def perform(self, executioncontext, frame):
- if self.space.config.objspace.usemodules.thread:
- main_ec = self.space.threadlocals.getmainthreadvalue()
- in_main = executioncontext is main_ec
- else:
- in_main = True
- # If we are in the main thread, poll and report the signals now.
- if in_main:
- if self.emulated_sigint:
- self.emulated_sigint = False
- self._report_signal(cpy_signal.SIGINT)
- while True:
- n = pypysig_poll()
- if n < 0:
- break
+ # Poll for the next signal, if any
+ n = self.pending_signal
+ if n < 0: n = pypysig_poll()
+ while n >= 0:
+ if self.space.config.objspace.usemodules.thread:
+ in_main = self.space.threadlocals.ismainthread()
+ else:
+ in_main = True
+ if in_main:
+ # If we are in the main thread, report the signal now,
+ # and poll more
+ self.pending_signal = -1
self._report_signal(n)
- else:
- # Otherwise, don't call pypysig_poll() at all. Instead,
- # arrange for perform() to be called again after a thread
- # switch. It might be called again and again, until we
- # land in the main thread.
- self.fire_after_thread_switch()
+ n = self.pending_signal
+ if n < 0: n = pypysig_poll()
+ else:
+ # Otherwise, arrange for perform() to be called again
+ # after we switch to the main thread.
+ self.fire_in_main_thread = True
+ break
- @jit.dont_look_inside
def set_interrupt(self):
"Simulates the effect of a SIGINT signal arriving"
- ec = self.space.getexecutioncontext()
- self.emulated_sigint = True
- self.perform(ec, None)
+ if not we_are_translated():
+ self.pending_signal = cpy_signal.SIGINT
+ # ^^^ may override another signal, but it's just for testing
+ else:
+ pypysig_pushback(cpy_signal.SIGINT)
+ self.fire_in_main_thread = True
def _report_signal(self, n):
try:
diff --git a/pypy/module/thread/gil.py b/pypy/module/thread/gil.py
--- a/pypy/module/thread/gil.py
+++ b/pypy/module/thread/gil.py
@@ -62,22 +62,7 @@
do_yield_thread()
-class SpaceState:
-
- def _cleanup_(self):
- self.action_after_thread_switch = None
- # ^^^ set by AsyncAction.fire_after_thread_switch()
-
- def after_thread_switch(self):
- # this is support logic for the signal module, to help it deliver
- # signals to the main thread.
- action = self.action_after_thread_switch
- if action is not None:
- self.action_after_thread_switch = None
- action.fire()
-
-spacestate = SpaceState()
-spacestate._cleanup_()
+after_thread_switch = lambda: None # hook for signal.py
# Fragile code below. We have to preserve the C-level errno manually...
@@ -94,7 +79,7 @@
e = get_errno()
thread.gil_acquire()
thread.gc_thread_run()
- spacestate.after_thread_switch()
+ after_thread_switch()
set_errno(e)
after_external_call._gctransformer_hint_cannot_collect_ = True
after_external_call._dont_reach_me_in_del_ = True
@@ -112,7 +97,7 @@
# the same thread).
if thread.gil_yield_thread():
thread.gc_thread_run()
- spacestate.after_thread_switch()
+ after_thread_switch()
do_yield_thread._gctransformer_hint_close_stack_ = True
do_yield_thread._dont_reach_me_in_del_ = True
do_yield_thread._dont_inline_ = True
diff --git a/pypy/module/thread/threadlocals.py
b/pypy/module/thread/threadlocals.py
--- a/pypy/module/thread/threadlocals.py
+++ b/pypy/module/thread/threadlocals.py
@@ -48,6 +48,9 @@
ident = self._mainthreadident
return self._valuedict.get(ident, None)
+ def ismainthread(self):
+ return thread.get_ident() == self._mainthreadident
+
def getallvalues(self):
return self._valuedict
diff --git a/rpython/rlib/rsignal.py b/rpython/rlib/rsignal.py
--- a/rpython/rlib/rsignal.py
+++ b/rpython/rlib/rsignal.py
@@ -80,6 +80,8 @@
pypysig_poll = external('pypysig_poll', [], rffi.INT, threadsafe=False)
# don't bother releasing the GIL around a call to pypysig_poll: it's
# pointless and a performance issue
+pypysig_pushback = external('pypysig_pushback', [rffi.INT], lltype.Void,
+ threadsafe=False)
# don't use rffi.LONGP because the JIT doesn't support raw arrays so far
struct_name = 'pypysig_long_struct'
diff --git a/rpython/translator/c/src/signals.c
b/rpython/translator/c/src/signals.c
--- a/rpython/translator/c/src/signals.c
+++ b/rpython/translator/c/src/signals.c
@@ -71,7 +71,7 @@
#endif
}
-static void signal_setflag_handler(int signum)
+static void pypysig_pushback(int signum)
{
if (0 <= signum && signum < NSIG)
{
@@ -79,6 +79,11 @@
pypysig_occurred = 1;
pypysig_counter.value = -1;
}
+}
+
+static void signal_setflag_handler(int signum)
+{
+ pypysig_pushback(signum);
if (wakeup_fd != -1)
{
diff --git a/rpython/translator/c/src/signals.h
b/rpython/translator/c/src/signals.h
--- a/rpython/translator/c/src/signals.h
+++ b/rpython/translator/c/src/signals.h
@@ -11,6 +11,7 @@
/* utility to poll for signals that arrived */
int pypysig_poll(void); /* => signum or -1 */
+void pypysig_pushback(int signum);
/* When a signal is received, pypysig_counter is set to -1. */
/* This is a struct for the JIT. See rsignal.py. */
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit