Author: Armin Rigo <[email protected]>
Branch: continulet-pickle
Changeset: r47268:e85840b1268d
Date: 2011-09-14 17:36 +0200
http://bitbucket.org/pypy/pypy/changeset/e85840b1268d/
Log: Refactoring: replace the dummy_pycode with a real one that contains
the start-up logic. Should be easier to pickle.
diff --git a/pypy/module/_continuation/interp_continuation.py
b/pypy/module/_continuation/interp_continuation.py
--- a/pypy/module/_continuation/interp_continuation.py
+++ b/pypy/module/_continuation/interp_continuation.py
@@ -28,19 +28,28 @@
def descr_init(self, w_callable, __args__):
if self.sthread is not None:
raise geterror(self.space, "continulet already __init__ialized")
+ #
+ # hackish: build the frame "by hand", passing it the correct arguments
+ space = self.space
+ w_args, w_kwds = __args__.topacked()
+ bottomframe = space.createframe(get_entrypoint_pycode(space),
+ get_w_module_dict(space), None)
+ bottomframe.locals_stack_w[0] = space.wrap(self)
+ bottomframe.locals_stack_w[1] = w_callable
+ bottomframe.locals_stack_w[2] = w_args
+ bottomframe.locals_stack_w[3] = w_kwds
+ self.bottomframe = bottomframe
+ #
global_state.origin = self
- global_state.w_callable = w_callable
- global_state.args = __args__
- self.bottomframe = make_fresh_frame(self.space)
- self.sthread = build_sthread(self.space)
+ sthread = build_sthread(self.space)
+ self.sthread = sthread
try:
- self.h = self.sthread.new(new_stacklet_callback)
- if self.sthread.is_empty_handle(self.h): # early return
- raise MemoryError
+ h = sthread.new(new_stacklet_callback)
except MemoryError:
- self.sthread = None
global_state.clear()
raise getmemoryerror(self.space)
+ #
+ post_switch(sthread, h)
def switch(self, w_to):
sthread = self.sthread
@@ -77,12 +86,12 @@
global_state.destination = to
#
try:
- do_switch(sthread, global_state.destination.h)
+ h = sthread.switch(global_state.destination.h)
except MemoryError:
global_state.clear()
raise getmemoryerror(self.space)
#
- return get_result()
+ return post_switch(sthread, h)
def descr_switch(self, w_value=None, w_to=None):
global_state.w_value = w_value
@@ -161,15 +170,29 @@
class State:
def __init__(self, space):
from pypy.interpreter.astcompiler.consts import CO_OPTIMIZED
- self.space = space
+ self.space = space
w_module = space.getbuiltinmodule('_continuation')
self.w_error = space.getattr(w_module, space.wrap('error'))
self.w_memoryerror = OperationError(space.w_MemoryError, space.w_None)
- self.dummy_pycode = PyCode(space, 0, 0, 0, CO_OPTIMIZED,
- '', [], [], [], '',
- '<bottom of continulet>', 0, '', [], [],
- hidden_applevel=True)
+ # the following function switches away immediately, so that
+ # continulet.__init__() doesn't immediately run func(), but it
+ # also has the hidden purpose of making sure we have a single
+ # bottomframe for the whole duration of the continulet's run.
+ # Hackish: only the func_code is used, and used in the context
+ # of w_globals == this module, so we can access the name
+ # 'continulet' directly.
+ w_code = space.appexec([], '''():
+ def start(c, func, args, kwds):
+ if continulet.switch(c) is not None:
+ raise TypeError(
+ "can\'t send non-None value to a just-started continulet")
+ return func(c, *args, **kwds)
+ return start.func_code
+ ''')
+ self.entrypoint_pycode = space.interp_w(PyCode, w_code)
+ self.entrypoint_pycode.hidden_applevel = True
self.w_unpickle = w_module.get('_p')
+ self.w_module_dict = w_module.getdict(space)
def geterror(space, message):
cs = space.fromcache(State)
@@ -179,9 +202,13 @@
cs = space.fromcache(State)
return cs.w_memoryerror
-def make_fresh_frame(space):
+def get_entrypoint_pycode(space):
cs = space.fromcache(State)
- return space.FrameClass(space, cs.dummy_pycode, None, None)
+ return cs.entrypoint_pycode
+
+def get_w_module_dict(space):
+ cs = space.fromcache(State)
+ return cs.w_module_dict
def getunpickle(space):
cs = space.fromcache(State)
@@ -206,8 +233,6 @@
def clear(self):
self.origin = None
self.destination = None
- self.w_callable = None
- self.args = None
self.w_value = None
self.propagate_exception = None
global_state = GlobalState()
@@ -215,27 +240,13 @@
def new_stacklet_callback(h, arg):
- self = global_state.origin
- w_callable = global_state.w_callable
- args = global_state.args
+ self = global_state.origin
+ self.h = h
global_state.clear()
- try:
- do_switch(self.sthread, h)
- except MemoryError:
- return h # oups! do an early return in this case
- #
space = self.space
try:
- assert self.sthread.ec.topframeref() is None
- self.sthread.ec.topframeref = jit.non_virtual_ref(self.bottomframe)
- if global_state.propagate_exception is not None:
- raise global_state.propagate_exception # just propagate it further
- if global_state.w_value is not space.w_None:
- raise OperationError(space.w_TypeError, space.wrap(
- "can't send non-None value to a just-started continulet"))
-
- args = args.prepend(self.space.wrap(self))
- w_result = space.call_args(w_callable, args)
+ frame = self.bottomframe
+ w_result = frame.execute_frame()
except Exception, e:
global_state.propagate_exception = e
else:
@@ -246,8 +257,7 @@
return self.h
-def do_switch(sthread, h):
- h = sthread.switch(h)
+def post_switch(sthread, h):
origin = global_state.origin
self = global_state.destination
global_state.origin = None
@@ -258,6 +268,8 @@
sthread.ec.topframeref = self.bottomframe.f_backref
self.bottomframe.f_backref = origin.bottomframe.f_backref
origin.bottomframe.f_backref = current
+ #
+ return get_result()
def get_result():
if global_state.propagate_exception:
diff --git a/pypy/module/_continuation/test/test_stacklet.py
b/pypy/module/_continuation/test/test_stacklet.py
--- a/pypy/module/_continuation/test/test_stacklet.py
+++ b/pypy/module/_continuation/test/test_stacklet.py
@@ -13,7 +13,7 @@
from _continuation import continulet
#
def empty_callback(c):
- pass
+ never_called
#
c = continulet(empty_callback)
assert type(c) is continulet
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit