Removed spawn_link and AsyncJob; added Job class that does the same

This commit is contained in:
Denis Bilenko
2008-12-10 18:00:27 +06:00
parent 59b59acd23
commit bd2444f8bf
4 changed files with 47 additions and 27 deletions

View File

@@ -238,11 +238,23 @@ class event(object):
return self.send(None, args) return self.send(None, args)
class AsyncJob(object): class Job(object):
"""Spawn a new coroutine to execute a function and collect its result.
def __init__(self, greenlet_ref, event): use wait() method to collect the result of the function;
self.greenlet_ref = greenlet_ref use kill() method to kill the greenlet running the function.
self.event = event """
def __init__(self, function, *args, **kwargs):
"""Create a new coroutine, or cooperative thread of control, within which
to execute *function*.
``Job()`` returns control to the caller immediately, and *function*
will be called in a future main loop iteration.
"""
self.event = event()
g = api.spawn(wrap_result_in_event, weakref.ref(self.event), function, *args, **kwargs)
self.greenlet_ref = weakref.ref(g)
@property @property
def greenlet(self): def greenlet(self):
@@ -263,6 +275,13 @@ class AsyncJob(object):
return '<%s greenlet=%r%s event=%s>' % (klass, self.greenlet, dead, self.event) return '<%s greenlet=%r%s event=%s>' % (klass, self.greenlet, dead, self.event)
def wait(self): def wait(self):
"""Wait for the function to return or raise.
Return the return value of the function if it has returned one,
re-raise the exception that was raised by the function otherwise.
If the greenlet was killed(), e.g. with kill() method, GreenletExit()
object is returned.
"""
return self.event.wait() return self.event.wait()
def poll(self, notready=None): def poll(self, notready=None):
@@ -287,9 +306,15 @@ def _kill_by_ref(async_job_ref):
if async_job is not None: if async_job is not None:
async_job.kill() async_job.kill()
def _wrap_result_in_event(event_ref, func, *args, **kwargs): def wrap_result_in_event(event_ref, function, *args, **kwargs):
"""Execute *function*, send its result to event_ref().
If function raises GreenletExit() it's trapped and sent as a regular value.
If event_ref() is not available (event was GC-ed) the return value
is thrown away and the exception is re-raised (allowing hub to log
the exception)
"""
try: try:
result = func(*args, **kwargs) result = function(*args, **kwargs)
except api.GreenletExit, ex: except api.GreenletExit, ex:
event = event_ref() event = event_ref()
if event is not None: if event is not None:
@@ -305,11 +330,6 @@ def _wrap_result_in_event(event_ref, func, *args, **kwargs):
if event is not None: if event is not None:
event.send(result) event.send(result)
def spawn_link(func, *args, **kwargs):
result = event()
g = api.spawn(_wrap_result_in_event, weakref.ref(result), func, *args, **kwargs)
return AsyncJob(weakref.ref(g), result)
class multievent(object): class multievent(object):
"""is an event that can hold more than one value (it cannot be cancelled though) """is an event that can hold more than one value (it cannot be cancelled though)

View File

@@ -1,12 +1,12 @@
"""Spawn multiple greenlet-workers and collect their results. """Spawn multiple greenlet-workers and collect their results.
Demonstrates how to use spawn_link. Demonstrates how to use coros.Job.
""" """
import sys import sys
import string import string
from eventlet.api import sleep from eventlet.api import sleep
from eventlet.green import socket from eventlet.green import socket
from eventlet.coros import spawn_link from eventlet.coros import Job
# this example works with both standard eventlet hubs and with twisted-based hub # this example works with both standard eventlet hubs and with twisted-based hub
# comment out the following line to use standard eventlet hub # comment out the following line to use standard eventlet hub
@@ -24,10 +24,10 @@ def progress_indicator():
sys.stderr.write('.') sys.stderr.write('.')
sleep(0.5) sleep(0.5)
spawn_link(progress_indicator) Job(progress_indicator)
urls = ['www.%s.com' % (x*3) for x in string.letters] urls = ['www.%s.com' % (x*3) for x in string.letters]
jobs = [spawn_link(geturl, x) for x in urls] jobs = [Job(geturl, x) for x in urls]
print 'spawned %s jobs' % len(jobs) print 'spawned %s jobs' % len(jobs)

View File

@@ -1,6 +1,6 @@
import sys import sys
from twisted.internet import reactor from twisted.internet import reactor
from eventlet.coros import event, spawn_link from eventlet.coros import event, Job
from eventlet.twistedutil import join_reactor from eventlet.twistedutil import join_reactor
from eventlet.twistedutil.protocol import GreenClientCreator, SpawnFactory, UnbufferedTransport from eventlet.twistedutil.protocol import GreenClientCreator, SpawnFactory, UnbufferedTransport
@@ -18,8 +18,8 @@ def forward(from_, to):
def handler(local): def handler(local):
remote = GreenClientCreator(reactor, UnbufferedTransport).connectTCP(remote_host, remote_port) remote = GreenClientCreator(reactor, UnbufferedTransport).connectTCP(remote_host, remote_port)
error = event() error = event()
a = spawn_link(forward, remote, local) a = Job(forward, remote, local)
b = spawn_link(forward, local, remote) b = Job(forward, local, remote)
a.wait() a.wait()
b.wait() b.wait()

View File

@@ -1,6 +1,6 @@
import unittest import unittest
import sys import sys
from eventlet.coros import event, spawn_link from eventlet.coros import event, Job
from eventlet.api import spawn, sleep, GreenletExit, exc_after from eventlet.api import spawn, sleep, GreenletExit, exc_after
class TestEvent(unittest.TestCase): class TestEvent(unittest.TestCase):
@@ -22,15 +22,15 @@ class TestEvent(unittest.TestCase):
assert log == [('catched', 'Exception')], log assert log == [('catched', 'Exception')], log
class TestSpawnLink(unittest.TestCase): class TestJob(unittest.TestCase):
def test_simple_return(self): def test_simple_return(self):
res = spawn_link(lambda: 25).wait() res = Job(lambda: 25).wait()
assert res==25, res assert res==25, res
def test_exception(self): def test_exception(self):
try: try:
spawn_link(sys.exit, 'bye').wait() Job(sys.exit, 'bye').wait()
except SystemExit, ex: except SystemExit, ex:
assert ex.args == ('bye', ) assert ex.args == ('bye', )
else: else:
@@ -40,7 +40,7 @@ class TestSpawnLink(unittest.TestCase):
def func(): def func():
sleep(0.1) sleep(0.1)
return 101 return 101
res = spawn_link(func) res = Job(func)
assert res assert res
if sync: if sync:
res.kill() res.kill()
@@ -60,14 +60,14 @@ class TestSpawnLink(unittest.TestCase):
def func(): def func():
sleep(0.1) sleep(0.1)
return 25 return 25
job = spawn_link(func) job = Job(func)
self.assertEqual(job.poll(), None) self.assertEqual(job.poll(), None)
assert job, repr(job) assert job, repr(job)
self.assertEqual(job.wait(), 25) self.assertEqual(job.wait(), 25)
self.assertEqual(job.poll(), 25) self.assertEqual(job.poll(), 25)
assert not job, repr(job) assert not job, repr(job)
job = spawn_link(func) job = Job(func)
self.assertEqual(job.poll(5), 5) self.assertEqual(job.poll(5), 5)
assert job, repr(job) assert job, repr(job)
self.assertEqual(job.wait(), 25) self.assertEqual(job.wait(), 25)
@@ -78,12 +78,12 @@ class TestSpawnLink(unittest.TestCase):
def func(): def func():
sleep(0.1) sleep(0.1)
return 25 return 25
job = spawn_link(func) job = Job(func)
job.kill_after(0.05) job.kill_after(0.05)
result = job.wait() result = job.wait()
assert isinstance(result, GreenletExit), repr(result) assert isinstance(result, GreenletExit), repr(result)
job = spawn_link(func) job = Job(func)
job.kill_after(0.2) job.kill_after(0.2)
self.assertEqual(job.wait(), 25) self.assertEqual(job.wait(), 25)
sleep(0.2) sleep(0.2)