Skip to content

Commit

Permalink
Stackless issue python#83: fix demos and remove broken and unmaintain…
Browse files Browse the repository at this point in the history
…ed ones

Clarify, that the scheduler callback must not be used to modify
the internal state of stackless.

Fix demo/tracing.py:
Don't use flextype features. Unfortunately it is no longer possible
to change the __repr__ method of class tasklet.

Fix tracing.py, fakechannel.py and fakechannel2.py and update
the copy of tracing.py in the documentation.

Remove Stackless/demo/paelike/PyOpenSteer and
Stackless/demo/stephan/stacklessness.

https://bitbucket.org/stackless-dev/stackless/issue/83
(grafted from 410ed1df77dfae9b48519a06a4c232d65caa9745, c5cac8a05d68 and
fb182bb2be4c)
  • Loading branch information
Anselm Kruis committed Aug 29, 2016
1 parent deaf98c commit d6d0830
Show file tree
Hide file tree
Showing 24 changed files with 191 additions and 3,985 deletions.
298 changes: 152 additions & 146 deletions Doc/library/stackless/debugging.rst
Original file line number Diff line number Diff line change
Expand Up @@ -39,152 +39,158 @@ ensure you enable tracing for all tasklets. This can be archived by the
schedule callback. This callback sees every task switch. Here is
a complete example::

from stackless import *
import types
import traceback
# Named tasklets
def _tasklet__repr__(self):
try:
return "<tasklet %s>" % ("main" if self.is_main else self.name,)
except AttributeError:
return super(tasklet, self).__repr__()
tasklet.__repr__ = _tasklet__repr__
class NamedTasklet(tasklet):
__slots__ = ["name"]
def __new__(self, func, name=None):
t = tasklet.__new__(self, func)
if name is None:
name = "at %08x" % (id(t))
t.name = name
return t
class Mutex(object):
"general purpose mutex class based on stackless.channels"
def __init__(self, capacity=1):
self.queue = channel()
self.capacity = capacity
def isLocked(self):
'''return non-zero if locked'''
return self.capacity == 0
def lock(self):
'''acquire the lock'''
currentTasklet = stackless.getcurrent()
atomic = currentTasklet.set_atomic(True)
try:
if self.capacity:
self.capacity -= 1
else:
self.queue.receive()
finally:
currentTasklet.set_atomic(atomic)
def unlock(self):
'''release the lock'''
currentTasklet = stackless.getcurrent()
atomic = currentTasklet.set_atomic(True)
try:
if self.queue.balance < 0:
self.queue.send(None)
else:
self.capacity += 1
finally:
currentTasklet.set_atomic(atomic)
m = Mutex()
def task():
name = getcurrent().name
print name, "acquiring"
m.lock()
print name, "switching"
schedule()
print name, "releasing"
m.unlock()
def trace_function(frame, event, arg):
if frame.f_code.co_name in ('schedule_cb', 'channel_cb'):
return None
print " trace_function: %s %s in %s, line %s" % \
(stackless.current, event, frame.f_code.co_name, frame.f_lineno)
if event in ('call', 'line', 'exception'):
return trace_function
return None
def channel_cb(channel, tasklet, sending, willblock):
tf = tasklet.trace_function
try:
tasklet.trace_function = None
print "Channel CB, tasklet %r, %s%s" % \
(tasklet, ("recv", "send")[sending], ("", " will block")[willblock])
finally:
tasklet.trace_function = tf
def schedule_cb(prev, next):
current = stackless.getcurrent()
current_tf = current.trace_function
try:
current.trace_function = None
current_info = "Schedule CB, current %r, " % (current,)
if current_tf is None:
# also look at the previous frame, in case this callback is exempt
# from tracing
f_back = current.frame.f_back
if f_back is not None:
current_tf = f_back.f_trace
if not prev:
print "%sstarting %r" % (current_info, next)
elif not next:
print "%sending %r" % (current_info, prev)
else:
print "%sjumping from %s to %s" % (current_info, prev, next)
prev_tf = current_tf if prev is current else prev.trace_function
next_tf = current_tf if next is current else next.trace_function
print " Current trace functions: prev: %r, next: %r" % \
(prev_tf, next_tf)
if next is not None:
if not next.is_main:
tf = trace_function
else:
tf = None
print " Setting trace function for next: %r" % (tf,)
task = next.frame
if next is current:
task = task.f_back
while task is not None:
if isinstance(task, types.FrameType):
task.f_trace = tf
task = task.f_back
next.trace_function = tf
except:
traceback.print_exc()
finally:
current.trace_function = current_tf
if __name__ == "__main__":
set_channel_callback(channel_cb)
set_schedule_callback(schedule_cb)
NamedTasklet(task, "tick")()
NamedTasklet(task, "trick")()
NamedTasklet(task, "track")()
run()
set_channel_callback(None)
set_schedule_callback(None)
from __future__ import absolute_import, print_function
import sys
import stackless
import traceback
class NamedTasklet(stackless.tasklet):
__slots__ = ("name",)
def __init__(self, func, name=None):
stackless.tasklet.__init__(self, func)
if name is None:
name = "at %08x" % (id(self))
self.name = name
def __repr__(self):
return "<tasklet %s>" % (self.name)
class Mutex(object):
def __init__(self, capacity=1):
self.queue = stackless.channel()
self.capacity = capacity
def isLocked(self):
'''return non-zero if locked'''
return self.capacity == 0
def lock(self):
'''acquire the lock'''
currentTasklet = stackless.getcurrent()
atomic = currentTasklet.set_atomic(True)
try:
if self.capacity:
self.capacity -= 1
else:
self.queue.receive()
finally:
currentTasklet.set_atomic(atomic)
def unlock(self):
'''release the lock'''
currentTasklet = stackless.getcurrent()
atomic = currentTasklet.set_atomic(True)
try:
if self.queue.balance < 0:
self.queue.send(None)
else:
self.capacity += 1
finally:
currentTasklet.set_atomic(atomic)
m = Mutex()
def task():
name = stackless.getcurrent().name
print(name, "acquiring")
m.lock()
print(name, "switching")
stackless.schedule()
print(name, "releasing")
m.unlock()
def trace_function(frame, event, arg):
if frame.f_code.co_name in ('schedule_cb', 'channel_cb'):
return None
print(" trace_function: %s %s in %s, line %s" %
(stackless.current, event, frame.f_code.co_name, frame.f_lineno))
if event in ('call', 'line', 'exception'):
return trace_function
return None
def channel_cb(channel, tasklet, sending, willblock):
tf = tasklet.trace_function
try:
tasklet.trace_function = None
print("Channel CB, tasklet %r, %s%s" %
(tasklet, ("recv", "send")[sending], ("", " will block")[willblock]))
finally:
tasklet.trace_function = tf
def schedule_cb(prev, next):
# During a tasklet switch (during the execution of this function) the
# the result of stackless.getcurrent() is implementation defined.
# Therefore this function avoids any assumptions about the current tasklet.
current_tf = sys.gettrace()
try:
sys.settrace(None) # don't trace this callback
current_frame = sys._getframe()
if current_tf is None:
# also look at the previous frame, in case this callback is exempt
# from tracing
f_back = current_frame.f_back
if f_back is not None:
current_tf = f_back.f_trace
current_info = "Schedule CB "
if not prev:
print("%sstarting %r" % (current_info, next))
elif not next:
print("%sending %r" % (current_info, prev))
else:
print("%sjumping from %s to %s" % (current_info, prev, next))
# Inform about the installed trace functions
prev_tf = current_tf if prev.frame is current_frame else prev.trace_function
next_tf = current_tf if next.frame is current_frame else next.trace_function
print(" Current trace functions: prev: %r, next: %r" % (prev_tf, next_tf))
# Eventually set a trace function
if next is not None:
if not next.is_main:
tf = trace_function
else:
tf = None
print(" Setting trace function for next: %r" % (tf,))
# Set the "global" trace function for the tasklet
next.trace_function = tf
# Set the "local" trace function for each frame
# This is required, if the tasklet is already running
frame = next.frame
if frame is current_frame:
frame = frame.f_back
while frame is not None:
frame.f_trace = tf
frame = frame.f_back
except:
traceback.print_exc()
finally:
sys.settrace(current_tf)
if __name__ == "__main__":
if len(sys.argv) > 1 and sys.argv[1] == 'hard':
stackless.enable_softswitch(False)
stackless.set_channel_callback(channel_cb)
stackless.set_schedule_callback(schedule_cb)
NamedTasklet(task, "tick")()
NamedTasklet(task, "trick")()
NamedTasklet(task, "track")()
stackless.run()
stackless.set_channel_callback(None)
stackless.set_schedule_callback(None)


-------------------------
Expand Down
7 changes: 7 additions & 0 deletions Doc/library/stackless/stackless.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@ Callback related functions:
The *prev* callback argument is the tasklet that was just running.

The *next* callback argument is the tasklet that is going to run now.

.. note::

During the execution of the scheduler callback the return value
of :func:`getcurrent` and the value of :attr:`current` are
implementation defined. You are not allowed to execute any methods, that
change the state of stackless for the current thread.

.. function:: get_schedule_callback()

Expand Down
4 changes: 4 additions & 0 deletions Stackless/changelog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ What's New in Stackless 3.X.X?

*Release date: 20XX-XX-XX*

- https://bitbucket.org/stackless-dev/stackless/issue/83
Fix demo/tracing.py: don't use flextype features. Unfortunately
it is no longer possible to change the __repr__ method of class tasklet.

- https://bitbucket.org/stackless-dev/stackless/issue/85
Fix a rare problem in the watchdog logic.

Expand Down
10 changes: 2 additions & 8 deletions Stackless/demo/fakechannel.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def send(self, data):
sender = stackless.current
self.queue.append((sender, data))
self.balance += 1
jump_off(sender)
stackless.schedule_remove()

def receive(self):
if self.balance > 0:
Expand All @@ -31,16 +31,10 @@ def receive(self):
receiver = stackless.current
self.queue.append(receiver)
self.balance -= 1
jump_off(receiver)
stackless.schedule_remove()
return self.temp


def jump_off(task):
stackless.tasklet().capture()
task.remove()
stackless.schedule()


def f1(ch):
for i in range(5):
ch.send(i)
Expand Down
10 changes: 2 additions & 8 deletions Stackless/demo/fakechannel2.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def send(self, data):
sender = stackless.current
self.queue.append(sender)
self.balance += 1
jump_off(sender, data)
stackless.schedule_remove(data)

def receive(self):
if self.balance > 0:
Expand All @@ -32,16 +32,10 @@ def receive(self):
receiver = stackless.current
self.queue.append(receiver)
self.balance -= 1
retval = jump_off(receiver)
retval = stackless.schedule_remove()
return retval


def jump_off(task, data=None):
stackless.tasklet().capture(data)
task.remove()
stackless.schedule()


def f1(ch):
for i in range(5):
ch.send(i)
Expand Down
Loading

0 comments on commit d6d0830

Please sign in to comment.