Skip to content

Instantly share code, notes, and snippets.

@catern
Created January 28, 2018 22:14
Show Gist options
  • Save catern/008303b8ef29ad40c07f4191c787e0b6 to your computer and use it in GitHub Desktop.
Save catern/008303b8ef29ad40c07f4191c787e0b6 to your computer and use it in GitHub Desktop.
def start(coroutine):
"""Starts a coroutine running.
If the coroutine makes a blocking call before yielding for the
first time, start() will block. However, if the coroutine is
written to not use blocking operations, or at least doesn't call
them before its first yield, start() will return immediately
(after reaching the first yield).
In either case, this function will return None.
"""
def send(value):
try:
# Pass the value to the coroutine, and it will run until
# either it yields us a function to call...
func = coroutine.send(value)
except:
# ...or it throws an exception, bringing this coroutine's
# story to an end.
pass
else:
trampoline(func)
def throw(exn):
try:
func = coroutine.throw(exn)
except:
pass
else:
trampoline(func)
def trampoline(func):
# The coroutine has given us a function to run, so we keep running.
once = CallOnlyOnce("Only one of the continuations produced by callcc can be called, and only once.")
try:
# Now, actually call the function that the coroutine yielded us.
# We pass it wrapped callbacks for coroutine.send and
# coroutine.throw, so the function may send a value or
# throw an exception into the coroutine as it wishes.
func(once.wrap(send), once.wrap(throw))
# This function won't block for overly long, so this function will
# just return, its work done.
except:
raise RuntimeError("Functions called with callcc are not allowed to throw exceptions")
send(None)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment