Skip to content

Instantly share code, notes, and snippets.

@jimbaker
Created April 8, 2014 16:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jimbaker/10152999 to your computer and use it in GitHub Desktop.
Save jimbaker/10152999 to your computer and use it in GitHub Desktop.
Greenlet emulation for Jython using threads. Development has moved to https://github.com/jythontools/artificialturf, but this shows my original thoughts. There are reports of running the JVM with 1000s or low 10000s of threads on Linux, but this emulation at least can be used as a bridge to better solutions, such as asyncio or Quasar.
import threading
from java.util.concurrent import ArrayBlockingQueue
class GreenletExit(Exception):
pass
# Consider two greenlets, which we will name alice and bob. Some code
# is running in the context of alice, then it calls
# bob.switch(...). This code has a reference to the bob greenlet,
# because of the user-directed and explicit scheduling in the greenlet
# model. But it needs to retrieve the alice context. As we usually do
# in such cases, we model this context with a thread local.
context = threading.local()
# FIXME arrange for a implicit main greenlet to root the tree of
# greenlets in the current thread! this provides the starting point
# Mailbox is used in this code to highlight that it's a specialized
# queue of length 1, used for the specific synchronization model of
# greenlet.switch
context._mailbox = ArrayBlockingQueue(1) # FIXME starting point to consider rooting
class greenlet(object):
def __init__(self, run=None, parent=None):
self.run = run
self.parent = parent
self._mailbox = ArrayBlockingQueue(1)
self._thread = threading.Thread(target=self._wrapper)
self._thread.daemon = True # greenlets don't block exit
self._thread.start()
def switch(self, *args, **kwargs):
# Using add ensures that we will quickly fail if multiple greenlets
# switch to the same one. Should not happen in actual greenlets,
# and presumably the user-directed scheduling of switch should ensure
# the same for this emulation
self._mailbox.add((args, kwargs))
try:
result = context._mailbox.take()
if isinstance(result, BaseException):
# FIXME presumably this is what is done,
# need to look at unit tests since docs
# are underspecified here
raise result
except GreenletExit:
result = None # FIXME what value?
return result
@property
def dead(self):
return not self._thread.is_alive()
def _wrapper(self):
# Now that this thread is started, we need to be prepared to switched
# to it on a subsequent scheduling (all user directed of course)
context._mailbox = self._mailbox
args, kwargs = self._mailbox.take()
print "Running greenlet thread {} args={} kwargs={}".format(self._thread.name, args, kwargs)
result = self.run(*args, **kwargs)
print "Completed greenlet thread {}".format(self._thread.name)
# FIXME need to support switching up the parent hierarchy
#self.parent.switch(result)
from greenlet import greenlet
def test1():
while True:
print 12
gr2.switch()
print 34
def test2():
while True:
print 56
gr1.switch()
print 78
gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr1.switch()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment