您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

628 行
19KB

  1. import gc
  2. import sys
  3. import time
  4. import threading
  5. import unittest
  6. from abc import ABCMeta, abstractmethod
  7. from greenlet import greenlet
  8. class SomeError(Exception):
  9. pass
  10. def fmain(seen):
  11. try:
  12. greenlet.getcurrent().parent.switch()
  13. except:
  14. seen.append(sys.exc_info()[0])
  15. raise
  16. raise SomeError
  17. def send_exception(g, exc):
  18. # note: send_exception(g, exc) can be now done with g.throw(exc).
  19. # the purpose of this test is to explicitely check the propagation rules.
  20. def crasher(exc):
  21. raise exc
  22. g1 = greenlet(crasher, parent=g)
  23. g1.switch(exc)
  24. class GreenletTests(unittest.TestCase):
  25. def test_simple(self):
  26. lst = []
  27. def f():
  28. lst.append(1)
  29. greenlet.getcurrent().parent.switch()
  30. lst.append(3)
  31. g = greenlet(f)
  32. lst.append(0)
  33. g.switch()
  34. lst.append(2)
  35. g.switch()
  36. lst.append(4)
  37. self.assertEqual(lst, list(range(5)))
  38. def test_parent_equals_None(self):
  39. g = greenlet(parent=None)
  40. self.assertIsNotNone(g)
  41. self.assertIs(g.parent, greenlet.getcurrent())
  42. def test_run_equals_None(self):
  43. g = greenlet(run=None)
  44. self.assertIsNotNone(g)
  45. self.assertIsNone(g.run)
  46. def test_two_children(self):
  47. lst = []
  48. def f():
  49. lst.append(1)
  50. greenlet.getcurrent().parent.switch()
  51. lst.extend([1, 1])
  52. g = greenlet(f)
  53. h = greenlet(f)
  54. g.switch()
  55. self.assertEqual(len(lst), 1)
  56. h.switch()
  57. self.assertEqual(len(lst), 2)
  58. h.switch()
  59. self.assertEqual(len(lst), 4)
  60. self.assertEqual(h.dead, True)
  61. g.switch()
  62. self.assertEqual(len(lst), 6)
  63. self.assertEqual(g.dead, True)
  64. def test_two_recursive_children(self):
  65. lst = []
  66. def f():
  67. lst.append(1)
  68. greenlet.getcurrent().parent.switch()
  69. def g():
  70. lst.append(1)
  71. g = greenlet(f)
  72. g.switch()
  73. lst.append(1)
  74. g = greenlet(g)
  75. g.switch()
  76. self.assertEqual(len(lst), 3)
  77. self.assertEqual(sys.getrefcount(g), 2)
  78. def test_threads(self):
  79. success = []
  80. def f():
  81. self.test_simple()
  82. success.append(True)
  83. ths = [threading.Thread(target=f) for i in range(10)]
  84. for th in ths:
  85. th.start()
  86. for th in ths:
  87. th.join()
  88. self.assertEqual(len(success), len(ths))
  89. def test_exception(self):
  90. seen = []
  91. g1 = greenlet(fmain)
  92. g2 = greenlet(fmain)
  93. g1.switch(seen)
  94. g2.switch(seen)
  95. g2.parent = g1
  96. self.assertEqual(seen, [])
  97. self.assertRaises(SomeError, g2.switch)
  98. self.assertEqual(seen, [SomeError])
  99. g2.switch()
  100. self.assertEqual(seen, [SomeError])
  101. def test_send_exception(self):
  102. seen = []
  103. g1 = greenlet(fmain)
  104. g1.switch(seen)
  105. self.assertRaises(KeyError, send_exception, g1, KeyError)
  106. self.assertEqual(seen, [KeyError])
  107. def test_dealloc(self):
  108. seen = []
  109. g1 = greenlet(fmain)
  110. g2 = greenlet(fmain)
  111. g1.switch(seen)
  112. g2.switch(seen)
  113. self.assertEqual(seen, [])
  114. del g1
  115. gc.collect()
  116. self.assertEqual(seen, [greenlet.GreenletExit])
  117. del g2
  118. gc.collect()
  119. self.assertEqual(seen, [greenlet.GreenletExit, greenlet.GreenletExit])
  120. def test_dealloc_other_thread(self):
  121. seen = []
  122. someref = []
  123. lock = threading.Lock()
  124. lock.acquire()
  125. lock2 = threading.Lock()
  126. lock2.acquire()
  127. def f():
  128. g1 = greenlet(fmain)
  129. g1.switch(seen)
  130. someref.append(g1)
  131. del g1
  132. gc.collect()
  133. lock.release()
  134. lock2.acquire()
  135. greenlet() # trigger release
  136. lock.release()
  137. lock2.acquire()
  138. t = threading.Thread(target=f)
  139. t.start()
  140. lock.acquire()
  141. self.assertEqual(seen, [])
  142. self.assertEqual(len(someref), 1)
  143. del someref[:]
  144. gc.collect()
  145. # g1 is not released immediately because it's from another thread
  146. self.assertEqual(seen, [])
  147. lock2.release()
  148. lock.acquire()
  149. self.assertEqual(seen, [greenlet.GreenletExit])
  150. lock2.release()
  151. t.join()
  152. def test_frame(self):
  153. def f1():
  154. f = sys._getframe(0) # pylint:disable=protected-access
  155. self.assertEqual(f.f_back, None)
  156. greenlet.getcurrent().parent.switch(f)
  157. return "meaning of life"
  158. g = greenlet(f1)
  159. frame = g.switch()
  160. self.assertTrue(frame is g.gr_frame)
  161. self.assertTrue(g)
  162. from_g = g.switch()
  163. self.assertFalse(g)
  164. self.assertEqual(from_g, 'meaning of life')
  165. self.assertEqual(g.gr_frame, None)
  166. def test_thread_bug(self):
  167. def runner(x):
  168. g = greenlet(lambda: time.sleep(x))
  169. g.switch()
  170. t1 = threading.Thread(target=runner, args=(0.2,))
  171. t2 = threading.Thread(target=runner, args=(0.3,))
  172. t1.start()
  173. t2.start()
  174. t1.join()
  175. t2.join()
  176. def test_switch_kwargs(self):
  177. def run(a, b):
  178. self.assertEqual(a, 4)
  179. self.assertEqual(b, 2)
  180. return 42
  181. x = greenlet(run).switch(a=4, b=2)
  182. self.assertEqual(x, 42)
  183. def test_switch_kwargs_to_parent(self):
  184. def run(x):
  185. greenlet.getcurrent().parent.switch(x=x)
  186. greenlet.getcurrent().parent.switch(2, x=3)
  187. return x, x ** 2
  188. g = greenlet(run)
  189. self.assertEqual({'x': 3}, g.switch(3))
  190. self.assertEqual(((2,), {'x': 3}), g.switch())
  191. self.assertEqual((3, 9), g.switch())
  192. def test_switch_to_another_thread(self):
  193. data = {}
  194. error = None
  195. created_event = threading.Event()
  196. done_event = threading.Event()
  197. def run():
  198. data['g'] = greenlet(lambda: None)
  199. created_event.set()
  200. done_event.wait()
  201. thread = threading.Thread(target=run)
  202. thread.start()
  203. created_event.wait()
  204. try:
  205. data['g'].switch()
  206. except greenlet.error:
  207. error = sys.exc_info()[1]
  208. self.assertIsNotNone(error, "greenlet.error was not raised!")
  209. done_event.set()
  210. thread.join()
  211. def test_exc_state(self):
  212. def f():
  213. try:
  214. raise ValueError('fun')
  215. except: # pylint:disable=bare-except
  216. exc_info = sys.exc_info()
  217. greenlet(h).switch()
  218. self.assertEqual(exc_info, sys.exc_info())
  219. def h():
  220. self.assertEqual(sys.exc_info(), (None, None, None))
  221. greenlet(f).switch()
  222. def test_instance_dict(self):
  223. def f():
  224. greenlet.getcurrent().test = 42
  225. def deldict(g):
  226. del g.__dict__
  227. def setdict(g, value):
  228. g.__dict__ = value
  229. g = greenlet(f)
  230. self.assertEqual(g.__dict__, {})
  231. g.switch()
  232. self.assertEqual(g.test, 42)
  233. self.assertEqual(g.__dict__, {'test': 42})
  234. g.__dict__ = g.__dict__
  235. self.assertEqual(g.__dict__, {'test': 42})
  236. self.assertRaises(TypeError, deldict, g)
  237. self.assertRaises(TypeError, setdict, g, 42)
  238. def test_threaded_reparent(self):
  239. data = {}
  240. created_event = threading.Event()
  241. done_event = threading.Event()
  242. def run():
  243. data['g'] = greenlet(lambda: None)
  244. created_event.set()
  245. done_event.wait()
  246. def blank():
  247. greenlet.getcurrent().parent.switch()
  248. def setparent(g, value):
  249. g.parent = value
  250. thread = threading.Thread(target=run)
  251. thread.start()
  252. created_event.wait()
  253. g = greenlet(blank)
  254. g.switch()
  255. self.assertRaises(ValueError, setparent, g, data['g'])
  256. done_event.set()
  257. thread.join()
  258. def test_deepcopy(self):
  259. import copy
  260. self.assertRaises(TypeError, copy.copy, greenlet())
  261. self.assertRaises(TypeError, copy.deepcopy, greenlet())
  262. def test_parent_restored_on_kill(self):
  263. hub = greenlet(lambda: None)
  264. main = greenlet.getcurrent()
  265. result = []
  266. def worker():
  267. try:
  268. # Wait to be killed
  269. main.switch()
  270. except greenlet.GreenletExit:
  271. # Resurrect and switch to parent
  272. result.append(greenlet.getcurrent().parent)
  273. result.append(greenlet.getcurrent())
  274. hub.switch()
  275. g = greenlet(worker, parent=hub)
  276. g.switch()
  277. del g
  278. self.assertTrue(result)
  279. self.assertEqual(result[0], main)
  280. self.assertEqual(result[1].parent, hub)
  281. def test_parent_return_failure(self):
  282. # No run causes AttributeError on switch
  283. g1 = greenlet()
  284. # Greenlet that implicitly switches to parent
  285. g2 = greenlet(lambda: None, parent=g1)
  286. # AttributeError should propagate to us, no fatal errors
  287. self.assertRaises(AttributeError, g2.switch)
  288. def test_throw_exception_not_lost(self):
  289. class mygreenlet(greenlet):
  290. def __getattribute__(self, name):
  291. try:
  292. raise Exception()
  293. except: # pylint:disable=bare-except
  294. pass
  295. return greenlet.__getattribute__(self, name)
  296. g = mygreenlet(lambda: None)
  297. self.assertRaises(SomeError, g.throw, SomeError())
  298. def test_throw_doesnt_crash(self):
  299. result = []
  300. def worker():
  301. greenlet.getcurrent().parent.switch()
  302. def creator():
  303. g = greenlet(worker)
  304. g.switch()
  305. result.append(g)
  306. t = threading.Thread(target=creator)
  307. t.start()
  308. t.join()
  309. self.assertRaises(greenlet.error, result[0].throw, SomeError())
  310. def test_recursive_startup(self):
  311. class convoluted(greenlet):
  312. def __init__(self):
  313. greenlet.__init__(self)
  314. self.count = 0
  315. def __getattribute__(self, name):
  316. if name == 'run' and self.count == 0:
  317. self.count = 1
  318. self.switch(43)
  319. return greenlet.__getattribute__(self, name)
  320. def run(self, value):
  321. while True:
  322. self.parent.switch(value)
  323. g = convoluted()
  324. self.assertEqual(g.switch(42), 43)
  325. def test_unexpected_reparenting(self):
  326. another = []
  327. def worker():
  328. g = greenlet(lambda: None)
  329. another.append(g)
  330. g.switch()
  331. t = threading.Thread(target=worker)
  332. t.start()
  333. t.join()
  334. class convoluted(greenlet):
  335. def __getattribute__(self, name):
  336. if name == 'run':
  337. self.parent = another[0] # pylint:disable=attribute-defined-outside-init
  338. return greenlet.__getattribute__(self, name)
  339. g = convoluted(lambda: None)
  340. self.assertRaises(greenlet.error, g.switch)
  341. def test_threaded_updatecurrent(self):
  342. # released when main thread should execute
  343. lock1 = threading.Lock()
  344. lock1.acquire()
  345. # released when another thread should execute
  346. lock2 = threading.Lock()
  347. lock2.acquire()
  348. class finalized(object):
  349. def __del__(self):
  350. # happens while in green_updatecurrent() in main greenlet
  351. # should be very careful not to accidentally call it again
  352. # at the same time we must make sure another thread executes
  353. lock2.release()
  354. lock1.acquire()
  355. # now ts_current belongs to another thread
  356. def deallocator():
  357. greenlet.getcurrent().parent.switch()
  358. def fthread():
  359. lock2.acquire()
  360. greenlet.getcurrent()
  361. del g[0]
  362. lock1.release()
  363. lock2.acquire()
  364. greenlet.getcurrent()
  365. lock1.release()
  366. main = greenlet.getcurrent()
  367. g = [greenlet(deallocator)]
  368. g[0].bomb = finalized()
  369. g[0].switch()
  370. t = threading.Thread(target=fthread)
  371. t.start()
  372. # let another thread grab ts_current and deallocate g[0]
  373. lock2.release()
  374. lock1.acquire()
  375. # this is the corner stone
  376. # getcurrent() will notice that ts_current belongs to another thread
  377. # and start the update process, which would notice that g[0] should
  378. # be deallocated, and that will execute an object's finalizer. Now,
  379. # that object will let another thread run so it can grab ts_current
  380. # again, which would likely crash the interpreter if there's no
  381. # check for this case at the end of green_updatecurrent(). This test
  382. # passes if getcurrent() returns correct result, but it's likely
  383. # to randomly crash if it's not anyway.
  384. self.assertEqual(greenlet.getcurrent(), main)
  385. # wait for another thread to complete, just in case
  386. t.join()
  387. def test_dealloc_switch_args_not_lost(self):
  388. seen = []
  389. def worker():
  390. # wait for the value
  391. value = greenlet.getcurrent().parent.switch()
  392. # delete all references to ourself
  393. del worker[0]
  394. initiator.parent = greenlet.getcurrent().parent
  395. # switch to main with the value, but because
  396. # ts_current is the last reference to us we
  397. # return immediately
  398. try:
  399. greenlet.getcurrent().parent.switch(value)
  400. finally:
  401. seen.append(greenlet.getcurrent())
  402. def initiator():
  403. return 42 # implicitly falls thru to parent
  404. worker = [greenlet(worker)]
  405. worker[0].switch() # prime worker
  406. initiator = greenlet(initiator, worker[0])
  407. value = initiator.switch()
  408. self.assertTrue(seen)
  409. self.assertEqual(value, 42)
  410. def test_tuple_subclass(self):
  411. if sys.version_info[0] > 2:
  412. # There's no apply in Python 3.x
  413. def _apply(func, a, k):
  414. func(*a, **k)
  415. else:
  416. _apply = apply # pylint:disable=undefined-variable
  417. class mytuple(tuple):
  418. def __len__(self):
  419. greenlet.getcurrent().switch()
  420. return tuple.__len__(self)
  421. args = mytuple()
  422. kwargs = dict(a=42)
  423. def switchapply():
  424. _apply(greenlet.getcurrent().parent.switch, args, kwargs)
  425. g = greenlet(switchapply)
  426. self.assertEqual(g.switch(), kwargs)
  427. def test_abstract_subclasses(self):
  428. AbstractSubclass = ABCMeta(
  429. 'AbstractSubclass',
  430. (greenlet,),
  431. {'run': abstractmethod(lambda self: None)})
  432. class BadSubclass(AbstractSubclass):
  433. pass
  434. class GoodSubclass(AbstractSubclass):
  435. def run(self):
  436. pass
  437. GoodSubclass() # should not raise
  438. self.assertRaises(TypeError, BadSubclass)
  439. def test_implicit_parent_with_threads(self):
  440. if not gc.isenabled():
  441. return # cannot test with disabled gc
  442. N = gc.get_threshold()[0]
  443. if N < 50:
  444. return # cannot test with such a small N
  445. def attempt():
  446. lock1 = threading.Lock()
  447. lock1.acquire()
  448. lock2 = threading.Lock()
  449. lock2.acquire()
  450. recycled = [False]
  451. def another_thread():
  452. lock1.acquire() # wait for gc
  453. greenlet.getcurrent() # update ts_current
  454. lock2.release() # release gc
  455. t = threading.Thread(target=another_thread)
  456. t.start()
  457. class gc_callback(object):
  458. def __del__(self):
  459. lock1.release()
  460. lock2.acquire()
  461. recycled[0] = True
  462. class garbage(object):
  463. def __init__(self):
  464. self.cycle = self
  465. self.callback = gc_callback()
  466. l = []
  467. x = range(N*2)
  468. current = greenlet.getcurrent()
  469. g = garbage()
  470. for _ in x:
  471. g = None # lose reference to garbage
  472. if recycled[0]:
  473. # gc callback called prematurely
  474. t.join()
  475. return False
  476. last = greenlet()
  477. if recycled[0]:
  478. break # yes! gc called in green_new
  479. l.append(last) # increase allocation counter
  480. else:
  481. # gc callback not called when expected
  482. gc.collect()
  483. if recycled[0]:
  484. t.join()
  485. return False
  486. self.assertEqual(last.parent, current)
  487. for g in l:
  488. self.assertEqual(g.parent, current)
  489. return True
  490. for _ in range(5):
  491. if attempt():
  492. break
  493. class TestRepr(unittest.TestCase):
  494. def assertEndsWith(self, got, suffix):
  495. self.assertTrue(got.endswith(suffix), (got, suffix))
  496. def test_main_while_running(self):
  497. r = repr(greenlet.getcurrent())
  498. self.assertEndsWith(r, " current active started main>")
  499. def test_main_in_background(self):
  500. main = greenlet.getcurrent()
  501. def run():
  502. return repr(main)
  503. g = greenlet(run)
  504. r = g.switch()
  505. self.assertEndsWith(r, ' suspended active started main>')
  506. def test_initial(self):
  507. r = repr(greenlet())
  508. self.assertEndsWith(r, ' pending>')
  509. def test_main_from_other_thread(self):
  510. main = greenlet.getcurrent()
  511. class T(threading.Thread):
  512. original_main = thread_main = None
  513. main_glet = None
  514. def run(self):
  515. self.original_main = repr(main)
  516. self.main_glet = greenlet.getcurrent()
  517. self.thread_main = repr(self.main_glet)
  518. t = T()
  519. t.start()
  520. t.join(10)
  521. self.assertEndsWith(t.original_main, ' suspended active started main>')
  522. self.assertEndsWith(t.thread_main, ' current active started main>')
  523. r = repr(t.main_glet)
  524. # main greenlets, even from dead threads, never really appear dead
  525. # TODO: Can we find a better way to differentiate that?
  526. assert not t.main_glet.dead
  527. self.assertEndsWith(r, ' suspended active started main>')
  528. def test_dead(self):
  529. g = greenlet(lambda: None)
  530. g.switch()
  531. self.assertEndsWith(repr(g), ' dead>')
  532. self.assertNotIn('suspended', repr(g))
  533. self.assertNotIn('started', repr(g))
  534. self.assertNotIn('active', repr(g))
  535. def test_formatting_produces_native_str(self):
  536. # https://github.com/python-greenlet/greenlet/issues/218
  537. # %s formatting on Python 2 was producing unicode, not str.
  538. g_dead = greenlet(lambda: None)
  539. g_not_started = greenlet(lambda: None)
  540. g_cur = greenlet.getcurrent()
  541. for g in g_dead, g_not_started, g_cur:
  542. self.assertIsInstance(
  543. '%s' % (g,),
  544. str
  545. )
  546. self.assertIsInstance(
  547. '%r' % (g,),
  548. str,
  549. )
  550. if __name__ == '__main__':
  551. unittest.main()