def test_exception(self): seen = [] g1 = Fiber(target=fmain, args=(seen, )) g2 = Fiber(target=fmain, args=(seen, )) g1.switch() g2.switch() g2.parent = g1 self.assertEqual(seen, []) self.assertRaises(SomeError, g2.switch) self.assertEqual(seen, [SomeError])
def send_exception(g, exc): # note: send_exception(g, exc) can be now done with g.throw(exc). # the purpose of this test is to explicitely check the propagation rules. def crasher(exc): raise exc g1 = Fiber(target=crasher, args=(exc, ), parent=g) g1.switch()
def test_finished_parent(self): def f(): return 42 g = Fiber(f) g.switch() self.assertFalse(g.is_alive()) self.assertRaises(ValueError, Fiber, parent=g)
def __init__(self): if getattr(_tls, 'loop', None) is not None: raise RuntimeError( 'cannot instantiate more than one event loop per thread') _tls.loop = self self._loop = pyuv.Loop() self._loop.excepthook = self._handle_error self._loop.event_loop = self self._threadpool = ThreadPool(self) self.task = Fiber(self._run_loop) self._destroyed = False self._started = False self._running = False self._fd_map = dict() self._signals = dict() self._timers = set() self._ready = deque() self._ready_processor = pyuv.Idle(self._loop) self._waker = pyuv.Async(self._loop, self._async_cb) self._waker.unref() self._install_signal_checker()
def async (cls, func, *args, **kwargs): self = cls.get() def _f(): func(*args, **kwargs) self._loop_fiber.switch() fb = Fiber(target=_f) self._fibers.append(fb)
def f(): try: raise ValueError('fun') except: exc_info = sys.exc_info() t = Fiber(h) t.switch() self.assertEqual(exc_info, sys.exc_info()) del t
def test_two_children(self): lst = [] def f(): lst.append(1) current().parent.switch() lst.extend([1, 1]) g = Fiber(f) h = Fiber(f) g.switch() self.assertEqual(len(lst), 1) h.switch() self.assertEqual(len(lst), 2) h.switch() self.assertEqual(len(lst), 4) self.assertEqual(h.is_alive(), False) g.switch() self.assertEqual(len(lst), 6) self.assertEqual(g.is_alive(), False)
def worker(): # main and additional *finished* greenlets ll = current().ll = [] def additional(): ll.append(current()) for i in range(2): Fiber(additional).switch() gg.append(weakref.ref(current()))
def test_arg_refs(self): if not has_refcount: return args = ('a', 'b', 'c') refcount_before = sys.getrefcount(args) g = Fiber(target=lambda *x: None, args=args) self.assertEqual(sys.getrefcount(args), refcount_before + 1) g.switch() self.assertEqual(sys.getrefcount(args), refcount_before) del g self.assertEqual(sys.getrefcount(args), refcount_before)
def test_kwarg_refs(self): if not has_refcount: return kwargs = {'a': 1234} refcount_before = sys.getrefcount(kwargs) g = Fiber(lambda **x: None, kwargs=kwargs) self.assertEqual(sys.getrefcount(kwargs), refcount_before + 1) g.switch() self.assertEqual(sys.getrefcount(kwargs), refcount_before) del g self.assertEqual(sys.getrefcount(kwargs), refcount_before)
def test_throw_goes_to_original_parent(self): main = fibers.current() def f1(): try: main.switch("f1 ready to catch") except IndexError: return "caught" else: return "normal exit" def f2(): main.switch("from f2") g1 = Fiber(f1) g2 = Fiber(target=f2, parent=g1) self.assertRaises(IndexError, g2.throw, IndexError) self.assertFalse(g2.is_alive()) self.assertTrue( g1.is_alive()) # g1 is skipped because it was not started
def test_val(self): def f(): try: switch("ok") except RuntimeError: val = sys.exc_info()[1] if str(val) == "ciao": switch("ok") return switch("fail") g = Fiber(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(RuntimeError("ciao")) self.assertEqual(res, "ok") g = Fiber(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(RuntimeError, "ciao") self.assertEqual(res, "ok")
def test_throw_goes_to_original_parent2(self): main = fibers.current() def f1(): try: main.switch("f1 ready to catch") except IndexError: return "caught" else: return "normal exit" def f2(): main.switch("from f2") g1 = Fiber(f1) g2 = Fiber(target=f2, parent=g1) res = g1.switch() self.assertEqual(res, "f1 ready to catch") res = g2.throw(IndexError) self.assertEqual(res, "caught") self.assertFalse(g2.is_alive()) self.assertFalse(g1.is_alive())
def test_threaded_updatecurrent(self): # FIXME (hangs?) return # released when main thread should execute lock1 = threading.Lock() lock1.acquire() # released when another thread should execute lock2 = threading.Lock() lock2.acquire() class finalized(object): def __del__(self): # happens while in green_updatecurrent() in main greenlet # should be very careful not to accidentally call it again # at the same time we must make sure another thread executes lock2.release() lock1.acquire() # now ts_current belongs to another thread def deallocator(): current().parent.switch() def fthread(): lock2.acquire() current() del g[0] lock1.release() lock2.acquire() current() lock1.release() main = current() g = [Fiber(deallocator)] g[0].bomb = finalized() g[0].switch() t = threading.Thread(target=fthread) t.start() # let another thread grab ts_current and deallocate g[0] lock2.release() lock1.acquire() # this is the corner stone # getcurrent() will notice that ts_current belongs to another thread # and start the update process, which would notice that g[0] should # be deallocated, and that will execute an object's finalizer. Now, # that object will let another thread run so it can grab ts_current # again, which would likely crash the interpreter if there's no # check for this case at the end of green_updatecurrent(). This test # passes if getcurrent() returns correct result, but it's likely # to randomly crash if it's not anyway. self.assertEqual(current(), main) # wait for another thread to complete, just in case t.join()
def test_simple2(self): lst = [] def f(): lst.append(1) current().parent.switch() lst.append(3) g = Fiber(f) lst.append(0) g.switch() lst.append(2) g.switch() lst.append(4) self.assertEqual(lst, list(range(5)))
def test_class(self): def f(): try: switch("ok") except RuntimeError: switch("ok") return switch("fail") g = Fiber(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(RuntimeError) self.assertEqual(res, "ok")
def test_kill(self): def f(): try: switch("ok") switch("fail") except Exception as e: return e g = Fiber(f) res = g.switch() self.assertEqual(res, "ok") res = g.throw(ValueError) self.assertTrue(isinstance(res, ValueError)) self.assertFalse(g.is_alive())
def test_two_recursive_children(self): lst = [] def f(): lst.append(1) current().parent.switch() def h(): lst.append(1) i = Fiber(f) i.switch() lst.append(1) g = Fiber(h) g.switch() self.assertEqual(len(lst), 3)
def test_exc_state(self): def f(): try: raise ValueError('fun') except: exc_info = sys.exc_info() t = Fiber(h) t.switch() self.assertEqual(exc_info, sys.exc_info()) del t def h(): self.assertEqual(sys.exc_info(), (None, None, None)) g = Fiber(f) g.switch()
def test_instance_dict(self): if is_pypy: return def f(): current().test = 42 def deldict(g): del g.__dict__ def setdict(g, value): g.__dict__ = value g = Fiber(f) self.assertEqual(g.__dict__, {}) g.switch() self.assertEqual(g.test, 42) self.assertEqual(g.__dict__, {'test': 42}) g.__dict__ = g.__dict__ self.assertEqual(g.__dict__, {'test': 42}) self.assertRaises(AttributeError, deldict, g) self.assertRaises(TypeError, setdict, g, 42)
def do_async(cls, func, *args, **kwargs): """ do coroutine asynchronously. Examples --- Server.do_async( lambda : Task.create(...) ) Parameters --- tasks : list of Task callback : callable """ self = cls._get() def _f(): func(*args, **kwargs) self._loop_fiber.switch() fb = Fiber(target=_f) self._fibers.append(fb)
def test_finalizer_crash(self): # This test is designed to crash when active greenlets # are made garbage collectable, until the underlying # problem is resolved. How does it work: # - order of object creation is important # - array is created first, so it is moved to unreachable first # - we create a cycle between a greenlet and this array # - we create an object that participates in gc, is only # referenced by a greenlet, and would corrupt gc lists # on destruction, the easiest is to use an object with # a finalizer # - because array is the first object in unreachable it is # cleared first, which causes all references to greenlet # to disappear and causes greenlet to be destroyed, but since # it is still live it causes a switch during gc, which causes # an object with finalizer to be destroyed, which causes stack # corruption and then a crash class object_with_finalizer(object): def __del__(self): pass array = [] parent = current() def greenlet_body(): current().object = object_with_finalizer() try: parent.switch() finally: del current().object g = Fiber(greenlet_body) g.array = array array.append(g) g.switch() del array del g current() gc.collect()
def test_threaded_reparent(self): data = {} created_event = threading.Event() done_event = threading.Event() def foo(): data['g'] = Fiber(lambda: None) created_event.set() done_event.wait() def blank(): current().parent.switch() def setparent(g, value): g.parent = value thread = threading.Thread(target=foo) thread.start() created_event.wait() g = Fiber(blank) g.switch() self.assertRaises(ValueError, setparent, g, data['g']) done_event.set() thread.join()
def test_deepcopy(self): self.assertRaises(TypeError, copy.copy, Fiber()) self.assertRaises(TypeError, copy.deepcopy, Fiber())
def test_inactive_ref(self): o = Fiber(lambda: None) o = weakref.ref(o) gc.collect() self.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage)
def test_dead_circular_ref(self): o = weakref.ref(Fiber(current).switch()) gc.collect() self.assertTrue(o() is None) self.assertFalse(gc.garbage, gc.garbage)
def runner(x): g = Fiber(lambda: time.sleep(x)) g.switch()
def foo(): data['g'] = Fiber(lambda: None) created_event.set() done_event.wait()
def __enter__(self): self._loop_fiber = Fiber(target=self._loop)
def creator(): g = Fiber(worker) g.switch() result.append(g)