[pypy-svn] r56127 - in pypy/branch/async-del/pypy: interpreter module/signal module/thread translator/c/src
arigo at codespeak.net
arigo at codespeak.net
Fri Jun 27 18:33:13 CEST 2008
Author: arigo
Date: Fri Jun 27 18:33:10 2008
New Revision: 56127
Modified:
pypy/branch/async-del/pypy/interpreter/executioncontext.py
pypy/branch/async-del/pypy/module/signal/__init__.py
pypy/branch/async-del/pypy/module/signal/interp_signal.py
pypy/branch/async-del/pypy/module/thread/gil.py
pypy/branch/async-del/pypy/translator/c/src/signals.h
Log:
Not completely nice, but this should make signals and threads
work again.
Modified: pypy/branch/async-del/pypy/interpreter/executioncontext.py
==============================================================================
--- pypy/branch/async-del/pypy/interpreter/executioncontext.py (original)
+++ pypy/branch/async-del/pypy/interpreter/executioncontext.py Fri Jun 27 18:33:10 2008
@@ -343,7 +343,7 @@
BYTECODE_COUNTER_OVERFLOW_BIT = (1 << 20)
# Free bits
- FREE_BITS = [1 << _b for _b in range(21, LONG_BIT)]
+ FREE_BITS = [1 << _b for _b in range(21, LONG_BIT-1)]
# The acceptable range of values for sys.checkinterval, so that
# the bytecode_counter fits in 20 bits
@@ -366,6 +366,14 @@
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 portential thread switch).
+ Don't call this if threads are not enabled.
+ """
+ from pypy.module.thread.gil import spacestate
+ spacestate.set_actionflag_bit_after_thread_switch |= self.bitmask
+
def perform(self, executioncontext):
"""To be overridden."""
Modified: pypy/branch/async-del/pypy/module/signal/__init__.py
==============================================================================
--- pypy/branch/async-del/pypy/module/signal/__init__.py (original)
+++ pypy/branch/async-del/pypy/module/signal/__init__.py Fri Jun 27 18:33:10 2008
@@ -28,8 +28,10 @@
from pypy.module.signal import interp_signal
MixedModule.__init__(self, space, *args)
# add the signal-checking callback as an action on the space
- space.pending_actions.append(interp_signal.CheckSignalAction(space))
- # use the C-level pypysig_occurred variable as the tick counter
+ space.check_signal_action = interp_signal.CheckSignalAction(space)
+ space.actionflag.register_action(space.check_signal_action)
+ # use the C-level pypysig_occurred variable as the action flag
+ # (the result is that the C-level signal handler will directly
+ # set the flag for the CheckSignalAction)
space.actionflag.get = interp_signal.pypysig_get_occurred
space.actionflag.set = interp_signal.pypysig_set_occurred
-
Modified: pypy/branch/async-del/pypy/module/signal/interp_signal.py
==============================================================================
--- pypy/branch/async-del/pypy/module/signal/interp_signal.py (original)
+++ pypy/branch/async-del/pypy/module/signal/interp_signal.py Fri Jun 27 18:33:10 2008
@@ -1,6 +1,7 @@
from pypy.interpreter.error import OperationError
from pypy.interpreter.baseobjspace import W_Root, ObjSpace
-from pypy.interpreter.miscutils import Action
+from pypy.interpreter.executioncontext import AsyncAction
+from pypy.rlib.rarithmetic import LONG_BIT, intmask
import signal as cpy_signal
from pypy.rpython.lltypesystem import lltype, rffi
from pypy.translator.tool.cbuild import ExternalCompilationInfo
@@ -40,55 +41,89 @@
pypysig_set_occurred = external('pypysig_set_occurred', [lltype.Signed],
lltype.Void, _nowrapper=True)
-class CheckSignalAction(Action):
- """A repeatitive action at the space level, checking if the
- signal_occurred flag is set and if so, scheduling ReportSignal actions.
- """
- repeat = True
+class CheckSignalAction(AsyncAction):
+ """An action that is automatically invoked when a signal is received."""
+
+ # The C-level signal handler sets the highest bit of pypysig_occurred:
+ bitmask = intmask(1 << (LONG_BIT-1))
def __init__(self, space):
- self.space = space
+ AsyncAction.__init__(self, space)
self.handlers_w = {}
+ if space.config.objspace.usemodules.thread:
+ # need a helper action in case signals arrive in a non-main thread
+ self.pending_signals = {}
+ self.reissue_signal_action = ReissueSignalAction(space)
+ space.actionflag.register_action(self.reissue_signal_action)
+ else:
+ self.reissue_signal_action = None
- def perform(self):
+ def perform(self, executioncontext):
while True:
n = pypysig_poll()
if n < 0:
break
- main_ec = self.space.threadlocals.getmainthreadvalue()
- main_ec.add_pending_action(ReportSignal(self, n))
+ if self.reissue_signal_action is None:
+ # no threads: we can report the signal immediately
+ self.report_signal(n)
+ else:
+ main_ec = self.space.threadlocals.getmainthreadvalue()
+ if executioncontext is main_ec:
+ # running in the main thread: we can report the
+ # signal immediately
+ self.report_signal(n)
+ else:
+ # running in another thread: we need to hack a bit
+ self.pending_signals[n] = None
+ self.reissue_signal_action.fire_after_thread_switch()
- def get(space):
- for action in space.pending_actions:
- if isinstance(action, CheckSignalAction):
- return action
- raise OperationError(space.w_RuntimeError,
- space.wrap("lost CheckSignalAction"))
- get = staticmethod(get)
-
-
-class ReportSignal(Action):
- """A one-shot action for the main thread's execution context."""
-
- def __init__(self, action, signum):
- self.action = action
- self.signum = signum
-
- def perform(self):
+ def report_signal(self, n):
try:
- w_handler = self.action.handlers_w[self.signum]
+ w_handler = self.handlers_w[n]
except KeyError:
return # no handler, ignore signal
# re-install signal handler, for OSes that clear it
- pypysig_setflag(self.signum)
+ pypysig_setflag(n)
# invoke the app-level handler
- space = self.action.space
+ space = self.space
ec = space.getexecutioncontext()
try:
w_frame = ec.framestack.top()
except IndexError:
w_frame = space.w_None
- space.call_function(w_handler, space.wrap(self.signum), w_frame)
+ space.call_function(w_handler, space.wrap(n), w_frame)
+
+ def report_pending_signals(self):
+ # XXX this logic isn't so complicated but I have no clue how
+ # to test it :-(
+ pending_signals = self.pending_signals.keys()
+ self.pending_signals.clear()
+ try:
+ while pending_signals:
+ self.report_signal(pending_signals.pop())
+ finally:
+ # in case of exception, put the undelivered signals back
+ # into the dict instead of silently swallowing them
+ if pending_signals:
+ for n in pending_signals:
+ self.pending_signals[n] = None
+ self.reissue_signal_action.fire()
+
+
+class ReissueSignalAction(AsyncAction):
+ """A special action to help deliver signals to the main thread. If
+ a non-main thread caught a signal, this action fires after every
+ thread switch until we land in the main thread.
+ """
+
+ def perform(self):
+ main_ec = self.space.threadlocals.getmainthreadvalue()
+ if executioncontext is main_ec:
+ # now running in the main thread: we can really report the signals
+ self.space.check_signal_action.report_pending_signals()
+ else:
+ # still running in some other thread: try again later
+ self.fire_after_thread_switch()
def getsignal(space, signum):
@@ -101,7 +136,7 @@
None -- if an unknown handler is in effect (XXX UNIMPLEMENTED)
anything else -- the callable Python object used as a handler
"""
- action = CheckSignalAction.get(space)
+ action = space.check_signal_action
if signum in action.handlers_w:
return action.handlers_w[signum]
return space.wrap(SIG_DFL)
@@ -129,7 +164,7 @@
raise OperationError(space.w_ValueError,
space.wrap("signal() must be called from the "
"main thread"))
- action = CheckSignalAction.get(space)
+ action = space.check_signal_action
if space.eq_w(w_handler, space.wrap(SIG_DFL)):
pypysig_default(signum)
action.handlers_w[signum] = w_handler
Modified: pypy/branch/async-del/pypy/module/thread/gil.py
==============================================================================
--- pypy/branch/async-del/pypy/module/thread/gil.py (original)
+++ pypy/branch/async-del/pypy/module/thread/gil.py Fri Jun 27 18:33:10 2008
@@ -42,6 +42,7 @@
# test_compile_lock. As a workaround, we repatch these global
# fields systematically.
spacestate.ll_GIL = self.ll_GIL
+ spacestate.actionflag = space.actionflag
invoke_around_extcall(before_external_call, after_external_call)
return result
@@ -68,9 +69,22 @@
class SpaceState:
+
def _freeze_(self):
self.ll_GIL = thread.null_ll_lock
+ self.actionflag = None
+ self.set_actionflag_bit_after_thread_switch = 0
return False
+
+ def after_thread_switch(self):
+ # this is support logic for the signal module, to help it deliver
+ # signals to the main thread.
+ actionflag = self.actionflag
+ if actionflag is not None:
+ flag = actionflag.get()
+ flag |= self.set_actionflag_bit_after_thread_switch
+ actionflag.set(flag)
+
spacestate = SpaceState()
# Fragile code below. We have to preserve the C-level errno manually...
@@ -87,5 +101,6 @@
e = get_errno()
thread.acquire_NOAUTO(spacestate.ll_GIL, True)
thread.gc_thread_run()
+ spacestate.after_thread_switch()
set_errno(e)
after_external_call._gctransformer_hint_cannot_collect_ = True
Modified: pypy/branch/async-del/pypy/translator/c/src/signals.h
==============================================================================
--- pypy/branch/async-del/pypy/translator/c/src/signals.h (original)
+++ pypy/branch/async-del/pypy/translator/c/src/signals.h Fri Jun 27 18:33:10 2008
@@ -4,6 +4,8 @@
#ifndef _PYPY_SIGNALS_H
#define _PYPY_SIGNALS_H
+#include "Python.h" /* XXX for LONG_MIN */
+
#include <stdlib.h>
#ifdef MS_WINDOWS
@@ -56,9 +58,19 @@
#define PENDING_SIGNAL_BIT (LONG_MIN) /* high bit */
extern long pypysig_occurred;
-/* inlinable helpers to get/set the variable as efficiently as possible */
-static long pypysig_get_occurred(void) { return pypysig_occurred; }
-static void pypysig_set_occurred(long x) { pypysig_occurred = x; }
+/* some C tricks to get/set the variable as efficiently as possible:
+ use macros when compiling as a stand-alone program, but still
+ export a function with the correct name for testing */
+#undef pypysig_get_occurred
+#undef pypysig_set_occurred
+long pypysig_get_occurred(void);
+void pypysig_set_occurred(long x);
+#ifndef PYPY_NOT_MAIN_FILE
+long pypysig_get_occurred(void) { return pypysig_occurred; }
+void pypysig_set_occurred(long x) { pypysig_occurred = x; }
+#endif
+#define pypysig_get_occurred() (pypysig_occurred)
+#define pypysig_set_occurred(x) (pypysig_occurred=(x))
/************************************************************/
/* Implementation */
@@ -123,10 +135,15 @@
int pypysig_poll(void)
{
- if (pypysig_occurred & PENDING_SIGNAL_BIT)
+ /* the two commented out lines below are useful for performance in
+ normal usage of pypysig_poll(); however, pypy/module/signal/ is
+ not normal usage. It only calls pypysig_poll() if the
+ PENDING_SIGNAL_BIT is set, and it clears that bit first. */
+
+/* if (pypysig_occurred & PENDING_SIGNAL_BIT) */
{
int i;
- pypysig_occurred &= ~PENDING_SIGNAL_BIT;
+/* pypysig_occurred &= ~PENDING_SIGNAL_BIT; */
for (i=0; i<NSIG; i++)
if (pypysig_flags[i])
{
More information about the Pypy-commit
mailing list