PageRenderTime 51ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rlib/test/test_rthread.py

https://bitbucket.org/timfel/pypy
Python | 338 lines | 279 code | 43 blank | 16 comment | 25 complexity | 573f0758a5f72e533c8c3c6209a8d825 MD5 | raw file
Possible License(s): Apache-2.0, AGPL-3.0, BSD-3-Clause
  1. import gc, time
  2. from rpython.rlib.rthread import *
  3. from rpython.rlib.rarithmetic import r_longlong
  4. from rpython.rlib import objectmodel
  5. from rpython.translator.c.test.test_boehm import AbstractGCTestClass
  6. from rpython.rtyper.lltypesystem import lltype, rffi
  7. import py
  8. import platform
  9. def test_lock():
  10. l = allocate_lock()
  11. ok1 = l.acquire(True)
  12. ok2 = l.acquire(False)
  13. l.release()
  14. ok3 = l.acquire(False)
  15. res = ok1 and not ok2 and ok3
  16. assert res == 1
  17. def test_lock_is_aquired():
  18. l = allocate_lock()
  19. ok1 = l.acquire(True)
  20. assert l.is_acquired() == True
  21. assert l.is_acquired() == True
  22. l.release()
  23. assert l.is_acquired() == False
  24. def test_thread_error():
  25. l = allocate_lock()
  26. try:
  27. l.release()
  28. except error:
  29. pass
  30. else:
  31. py.test.fail("Did not raise")
  32. def test_tlref_untranslated():
  33. import thread
  34. class FooBar(object):
  35. pass
  36. t = ThreadLocalReference(FooBar)
  37. results = []
  38. def subthread():
  39. x = FooBar()
  40. results.append(t.get() is None)
  41. t.set(x)
  42. results.append(t.get() is x)
  43. time.sleep(0.2)
  44. results.append(t.get() is x)
  45. for i in range(5):
  46. thread.start_new_thread(subthread, ())
  47. time.sleep(0.5)
  48. assert results == [True] * 15
  49. def test_get_ident():
  50. import thread
  51. assert get_ident() == thread.get_ident()
  52. def test_threadlocalref_on_llinterp():
  53. from rpython.rtyper.test.test_llinterp import interpret
  54. tlfield = ThreadLocalField(lltype.Signed, "rthread_test_")
  55. #
  56. def f():
  57. x = tlfield.setraw(42)
  58. return tlfield.getraw()
  59. #
  60. res = interpret(f, [])
  61. assert res == 42
  62. class AbstractThreadTests(AbstractGCTestClass):
  63. use_threads = True
  64. def test_start_new_thread(self):
  65. import time
  66. class State:
  67. pass
  68. state = State()
  69. def bootstrap1():
  70. state.my_thread_ident1 = get_ident()
  71. def bootstrap2():
  72. state.my_thread_ident2 = get_ident()
  73. def f():
  74. state.my_thread_ident1 = get_ident()
  75. state.my_thread_ident2 = get_ident()
  76. start_new_thread(bootstrap1, ())
  77. start_new_thread(bootstrap2, ())
  78. willing_to_wait_more = 1000
  79. while (state.my_thread_ident1 == get_ident() or
  80. state.my_thread_ident2 == get_ident()):
  81. willing_to_wait_more -= 1
  82. if not willing_to_wait_more:
  83. raise Exception("thread didn't start?")
  84. time.sleep(0.01)
  85. return 42
  86. fn = self.getcompiled(f, [])
  87. res = fn()
  88. assert res == 42
  89. @py.test.mark.xfail(platform.machine() == 's390x',
  90. reason='may fail this test under heavy load')
  91. def test_gc_locking(self):
  92. import time
  93. from rpython.rlib.debug import ll_assert
  94. class State:
  95. pass
  96. state = State()
  97. class Z:
  98. def __init__(self, i, j):
  99. self.i = i
  100. self.j = j
  101. def run(self):
  102. j = self.j
  103. if self.i > 1:
  104. g(self.i-1, self.j * 2)
  105. ll_assert(j == self.j, "1: bad j")
  106. g(self.i-2, self.j * 2 + 1)
  107. else:
  108. if len(state.answers) % 7 == 5:
  109. gc.collect()
  110. state.answers.append(self.j)
  111. ll_assert(j == self.j, "2: bad j")
  112. run._dont_inline_ = True
  113. def bootstrap():
  114. # after_extcall() is called before we arrive here.
  115. # We can't just acquire and release the GIL manually here,
  116. # because it is unsafe: bootstrap() is called from a rffi
  117. # callback which checks for and reports exceptions after
  118. # bootstrap() returns. The exception checking code must be
  119. # protected by the GIL too.
  120. z = state.z
  121. state.z = None
  122. state.bootstrapping.release()
  123. z.run()
  124. gc_thread_die()
  125. # before_extcall() is called after we leave here
  126. def g(i, j):
  127. state.bootstrapping.acquire(True)
  128. state.z = Z(i, j)
  129. start_new_thread(bootstrap, ())
  130. def f():
  131. state.bootstrapping = allocate_lock()
  132. state.answers = []
  133. state.finished = 0
  134. g(10, 1)
  135. done = False
  136. willing_to_wait_more = 2000
  137. while not done:
  138. if not willing_to_wait_more:
  139. break
  140. willing_to_wait_more -= 1
  141. done = len(state.answers) == expected
  142. print "waitting %d more iterations" % willing_to_wait_more
  143. time.sleep(0.01)
  144. time.sleep(0.1)
  145. return len(state.answers)
  146. expected = 89
  147. fn = self.getcompiled(f, [])
  148. answers = fn()
  149. assert answers == expected
  150. def test_acquire_timed(self):
  151. import time
  152. def f():
  153. l = allocate_lock()
  154. l.acquire(True)
  155. t1 = time.time()
  156. ok = l.acquire_timed(1000001)
  157. t2 = time.time()
  158. delay = t2 - t1
  159. if ok == 0: # RPY_LOCK_FAILURE
  160. return -delay
  161. elif ok == 2: # RPY_LOCK_INTR
  162. return delay
  163. else: # RPY_LOCK_ACQUIRED
  164. return 0.0
  165. fn = self.getcompiled(f, [])
  166. res = fn()
  167. assert res < -1.0
  168. def test_acquire_timed_huge_timeout(self):
  169. t = r_longlong(2 ** 61)
  170. def f():
  171. l = allocate_lock()
  172. return l.acquire_timed(t)
  173. fn = self.getcompiled(f, [])
  174. res = fn()
  175. assert res == 1 # RPY_LOCK_ACQUIRED
  176. def test_acquire_timed_alarm(self):
  177. import sys
  178. if not sys.platform.startswith('linux'):
  179. py.test.skip("skipped on non-linux")
  180. import time
  181. from rpython.rlib import rsignal
  182. def f():
  183. l = allocate_lock()
  184. l.acquire(True)
  185. #
  186. rsignal.pypysig_setflag(rsignal.SIGALRM)
  187. rsignal.c_alarm(1)
  188. #
  189. t1 = time.time()
  190. ok = l.acquire_timed(2500000)
  191. t2 = time.time()
  192. delay = t2 - t1
  193. if ok == 0: # RPY_LOCK_FAILURE
  194. return -delay
  195. elif ok == 2: # RPY_LOCK_INTR
  196. return delay
  197. else: # RPY_LOCK_ACQUIRED
  198. return 0.0
  199. fn = self.getcompiled(f, [])
  200. res = fn()
  201. assert res >= 0.95
  202. def test_tlref(self):
  203. class FooBar(object):
  204. pass
  205. t = ThreadLocalReference(FooBar)
  206. def f():
  207. x1 = FooBar()
  208. t.set(x1)
  209. import gc; gc.collect()
  210. assert t.get() is x1
  211. return 42
  212. fn = self.getcompiled(f, [])
  213. res = fn()
  214. assert res == 42
  215. #class TestRunDirectly(AbstractThreadTests):
  216. # def getcompiled(self, f, argtypes):
  217. # return f
  218. # These are disabled because they crash occasionally for bad reasons
  219. # related to the fact that ll2ctypes is not at all thread-safe
  220. class TestUsingBoehm(AbstractThreadTests):
  221. gcpolicy = 'boehm'
  222. class TestUsingFramework(AbstractThreadTests):
  223. gcpolicy = 'minimark'
  224. def test_tlref_keepalive(self, no__thread=True):
  225. import weakref
  226. from rpython.config.translationoption import SUPPORT__THREAD
  227. if not (SUPPORT__THREAD or no__thread):
  228. py.test.skip("no __thread support here")
  229. class FooBar(object):
  230. def __init__(self, a, b):
  231. self.lst = [a, b]
  232. t = ThreadLocalReference(FooBar)
  233. t2 = ThreadLocalReference(FooBar)
  234. def tset():
  235. x1 = FooBar(40, 2)
  236. t.set(x1)
  237. return weakref.ref(x1)
  238. tset._dont_inline_ = True
  239. def t2set():
  240. x1 = FooBar(50, 3)
  241. t2.set(x1)
  242. return weakref.ref(x1)
  243. t2set._dont_inline_ = True
  244. class WrFromThread:
  245. pass
  246. wr_from_thread = WrFromThread()
  247. def f():
  248. config = objectmodel.fetch_translated_config()
  249. assert t.automatic_keepalive(config) is True
  250. wr = tset()
  251. wr2 = t2set()
  252. import gc; gc.collect() # the two 'x1' should not be collected
  253. x1 = t.get()
  254. assert x1 is not None
  255. assert wr() is not None
  256. assert wr() is x1
  257. assert x1.lst == [40, 2]
  258. x2 = t2.get()
  259. assert x2 is not None
  260. assert wr2() is not None
  261. assert wr2() is x2
  262. assert x2.lst == [50, 3]
  263. return wr, wr2
  264. def thread_entry_point():
  265. wr, wr2 = f()
  266. wr_from_thread.wr = wr
  267. wr_from_thread.wr2 = wr2
  268. wr_from_thread.seen = True
  269. def main():
  270. wr_from_thread.seen = False
  271. start_new_thread(thread_entry_point, ())
  272. wr1, wr2 = f()
  273. count = 0
  274. while True:
  275. time.sleep(0.5)
  276. if wr_from_thread.seen or count >= 50:
  277. break
  278. count += 1
  279. assert wr_from_thread.seen is True
  280. wr_other_1 = wr_from_thread.wr
  281. wr_other_2 = wr_from_thread.wr2
  282. import gc; gc.collect() # wr_other_*() should be collected here
  283. assert wr1() is not None # this thread, still running
  284. assert wr2() is not None # this thread, still running
  285. assert wr_other_1() is None # other thread, not running any more
  286. assert wr_other_2() is None # other thread, not running any more
  287. assert wr1().lst == [40, 2]
  288. assert wr2().lst == [50, 3]
  289. return 42
  290. extra_options = {'no__thread': no__thread, 'shared': True}
  291. fn = self.getcompiled(main, [], extra_options=extra_options)
  292. res = fn()
  293. assert res == 42
  294. def test_tlref_keepalive__thread(self):
  295. self.test_tlref_keepalive(no__thread=False)