PageRenderTime 52ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/rpython/translator/c/test/test_boehm.py

https://bitbucket.org/pypy/pypy/
Python | 432 lines | 374 code | 37 blank | 21 comment | 32 complexity | e7833a40076ce561a841a2775178e8d9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import weakref
  2. import py
  3. from rpython.rlib import rgc, debug
  4. from rpython.rlib.objectmodel import (keepalive_until_here, compute_unique_id,
  5. compute_hash, current_object_addr_as_int)
  6. from rpython.rtyper.lltypesystem import lltype, llmemory
  7. from rpython.rtyper.lltypesystem.lloperation import llop
  8. from rpython.rtyper.lltypesystem.rstr import STR
  9. from rpython.translator.c.test.test_genc import compile
  10. def setup_module(mod):
  11. from rpython.rtyper.tool.rffi_platform import configure_boehm
  12. from rpython.translator.platform import CompilationError
  13. try:
  14. configure_boehm()
  15. except CompilationError:
  16. py.test.skip("Boehm GC not present")
  17. class AbstractGCTestClass(object):
  18. gcpolicy = "boehm"
  19. use_threads = False
  20. extra_options = {}
  21. # deal with cleanups
  22. def setup_method(self, meth):
  23. self._cleanups = []
  24. def teardown_method(self, meth):
  25. while self._cleanups:
  26. #print "CLEANUP"
  27. self._cleanups.pop()()
  28. def getcompiled(self, func, argstypelist=[], annotatorpolicy=None,
  29. extra_options={}):
  30. return compile(func, argstypelist, gcpolicy=self.gcpolicy,
  31. thread=self.use_threads, **extra_options)
  32. class TestUsingBoehm(AbstractGCTestClass):
  33. gcpolicy = "boehm"
  34. def test_malloc_a_lot(self):
  35. def malloc_a_lot():
  36. i = 0
  37. while i < 10:
  38. i += 1
  39. a = [1] * 10
  40. j = 0
  41. while j < 20:
  42. j += 1
  43. a.append(j)
  44. fn = self.getcompiled(malloc_a_lot)
  45. fn()
  46. def test__del__(self):
  47. class State:
  48. pass
  49. s = State()
  50. class A(object):
  51. def __del__(self):
  52. s.a_dels += 1
  53. class B(A):
  54. def __del__(self):
  55. s.b_dels += 1
  56. class C(A):
  57. pass
  58. def f():
  59. s.a_dels = 0
  60. s.b_dels = 0
  61. A()
  62. B()
  63. C()
  64. A()
  65. B()
  66. C()
  67. llop.gc__collect(lltype.Void)
  68. return s.a_dels * 10 + s.b_dels
  69. fn = self.getcompiled(f)
  70. # we can't demand that boehm has collected all of the objects,
  71. # even with the gc__collect call. calling the compiled
  72. # function twice seems to help, though.
  73. res = 0
  74. res += fn()
  75. res += fn()
  76. # if res is still 0, then we haven't tested anything so fail.
  77. # it might be the test's fault though.
  78. assert 0 < res <= 84
  79. def test_id_is_weak(self):
  80. # test that compute_unique_id(obj) does not keep obj alive
  81. class State:
  82. pass
  83. s = State()
  84. class A(object):
  85. def __del__(self):
  86. s.a_dels += 1
  87. class B(A):
  88. def __del__(self):
  89. s.b_dels += 1
  90. class C(A):
  91. pass
  92. def run_once():
  93. a = A()
  94. ida = compute_unique_id(a)
  95. b = B()
  96. idb = compute_unique_id(b)
  97. c = C()
  98. idc = compute_unique_id(c)
  99. llop.gc__collect(lltype.Void)
  100. llop.gc__collect(lltype.Void)
  101. llop.gc__collect(lltype.Void)
  102. return ida, idb, idc
  103. def f(n):
  104. s.a_dels = 0
  105. s.b_dels = 0
  106. a1, b1, c1 = run_once()
  107. a2, b2, c2 = run_once()
  108. a3, b3, c3 = run_once()
  109. a4, b4, c4 = run_once()
  110. a5, b5, c5 = run_once()
  111. return str((s.a_dels, s.b_dels,
  112. a1, b1, c1,
  113. a2, b2, c2,
  114. a3, b3, c3,
  115. a4, b4, c4,
  116. a5, b5, c5))
  117. fn = self.getcompiled(f, [int])
  118. # we can't demand that boehm has collected all of the objects,
  119. # even with the gc__collect call.
  120. res = fn(50)
  121. res1, res2 = eval(res)[:2]
  122. # if res1 or res2 is still 0, then we haven't tested anything so fail.
  123. # it might be the test's fault though.
  124. print res1, res2
  125. assert 0 < res1 <= 10
  126. assert 0 < res2 <= 5
  127. def test_del_raises(self):
  128. class A(object):
  129. def __del__(self):
  130. s.dels += 1
  131. raise Exception
  132. class State:
  133. pass
  134. s = State()
  135. s.dels = 0
  136. def g():
  137. a = A()
  138. def f():
  139. s.dels = 0
  140. for i in range(10):
  141. g()
  142. llop.gc__collect(lltype.Void)
  143. return s.dels
  144. fn = self.getcompiled(f)
  145. # we can't demand that boehm has collected all of the objects,
  146. # even with the gc__collect call. calling the compiled
  147. # function twice seems to help, though.
  148. res = 0
  149. res += fn()
  150. res += fn()
  151. # if res is still 0, then we haven't tested anything so fail.
  152. # it might be the test's fault though.
  153. assert res > 0
  154. def test_memory_error_varsize(self):
  155. N = int(2**31-1)
  156. A = lltype.GcArray(lltype.Char)
  157. def alloc(n):
  158. return lltype.malloc(A, n)
  159. def f():
  160. try:
  161. x = alloc(N)
  162. except MemoryError:
  163. y = alloc(10)
  164. return len(y)
  165. y = alloc(10)
  166. return len(y) # allocation may work on 64 bits machines
  167. fn = self.getcompiled(f)
  168. res = fn()
  169. assert res == 10
  170. def test_zero_malloc(self):
  171. T = lltype.GcStruct("C", ('x', lltype.Signed))
  172. def fixed_size():
  173. t = lltype.malloc(T, zero=True)
  174. return t.x
  175. c_fixed_size = self.getcompiled(fixed_size, [])
  176. res = c_fixed_size()
  177. assert res == 0
  178. A = lltype.GcArray(lltype.Signed)
  179. def var_size():
  180. a = lltype.malloc(A, 1, zero=True)
  181. return a[0]
  182. c_var_size = self.getcompiled(var_size, [])
  183. res = c_var_size()
  184. assert res == 0
  185. def test_gc_set_max_heap_size(self):
  186. def g(n):
  187. return 'x' * n
  188. def fn():
  189. from rpython.rlib import rgc
  190. rgc.set_max_heap_size(500000)
  191. s1 = s2 = s3 = None
  192. try:
  193. s1 = g(10000)
  194. s2 = g(100000)
  195. s3 = g(1000000)
  196. except MemoryError:
  197. pass
  198. return (s1 is not None) + (s2 is not None) + (s3 is not None)
  199. c_fn = self.getcompiled(fn, [])
  200. res = c_fn()
  201. assert res == 2
  202. def test_weakref(self):
  203. class A:
  204. pass
  205. def fn(n):
  206. keepalive = []
  207. weakrefs = []
  208. a = None
  209. for i in range(n):
  210. if i & 1 == 0:
  211. a = A()
  212. a.index = i
  213. assert a is not None
  214. weakrefs.append(weakref.ref(a))
  215. if i % 7 == 6:
  216. keepalive.append(a)
  217. rgc.collect()
  218. count_free = 0
  219. for i in range(n):
  220. a = weakrefs[i]()
  221. if i % 7 == 6:
  222. assert a is not None
  223. if a is not None:
  224. assert a.index == i & ~1
  225. else:
  226. count_free += 1
  227. keepalive_until_here(keepalive)
  228. return count_free
  229. c_fn = self.getcompiled(fn, [int])
  230. res = c_fn(7000)
  231. # more than half of them should have been freed, ideally up to 6000
  232. assert 3500 <= res <= 6000
  233. def test_prebuilt_weakref(self):
  234. class A:
  235. pass
  236. a = A()
  237. a.hello = 42
  238. r1 = weakref.ref(a)
  239. r2 = weakref.ref(A())
  240. rgc.collect()
  241. assert r2() is None
  242. def fn(n):
  243. if n:
  244. r = r1
  245. else:
  246. r = r2
  247. a = r()
  248. rgc.collect()
  249. if a is None:
  250. return -5
  251. else:
  252. return a.hello
  253. c_fn = self.getcompiled(fn, [int])
  254. res = c_fn(1)
  255. assert res == 42
  256. res = c_fn(0)
  257. assert res == -5
  258. def test_weakref_to_prebuilt(self):
  259. class A:
  260. pass
  261. a = A()
  262. a.hello = 42
  263. def fn(n):
  264. lst = [weakref.ref(a) for i in range(n)]
  265. rgc.collect()
  266. for r in lst:
  267. assert r() is a
  268. c_fn = self.getcompiled(fn, [int])
  269. c_fn(100)
  270. def test_nested_finalizers(self):
  271. class State:
  272. pass
  273. state = State()
  274. def g():
  275. n = state.counter
  276. if n > 0:
  277. for i in range(5):
  278. state.a = A(n)
  279. state.a = None
  280. rgc.collect()
  281. return n
  282. fun = g
  283. for i in range(200):
  284. def fun(next=fun):
  285. return next() + 1 # prevents tail-call optimization
  286. class A:
  287. def __init__(self, level):
  288. self.level = level
  289. def __del__(self):
  290. if state.counter == self.level:
  291. state.counter -= 1
  292. fun()
  293. def fn(n):
  294. state.counter = n
  295. fun()
  296. return state.counter
  297. c_fn = self.getcompiled(fn, [int])
  298. res = c_fn(10000)
  299. assert res == 0
  300. def test_can_move(self):
  301. class A:
  302. pass
  303. def fn():
  304. return rgc.can_move(A())
  305. c_fn = self.getcompiled(fn, [])
  306. assert c_fn() == False
  307. def test_heap_stats(self):
  308. def fn():
  309. return bool(rgc._heap_stats())
  310. c_fn = self.getcompiled(fn, [])
  311. assert not c_fn()
  312. def test_shrink_array(self):
  313. def f():
  314. ptr = lltype.malloc(STR, 3)
  315. ptr.hash = 0x62
  316. ptr.chars[0] = '0'
  317. ptr.chars[1] = 'B'
  318. ptr.chars[2] = 'C'
  319. ptr2 = rgc.ll_shrink_array(ptr, 2)
  320. return ((ptr == ptr2) +
  321. ord(ptr2.chars[0]) +
  322. (ord(ptr2.chars[1]) << 8) +
  323. (len(ptr2.chars) << 16) +
  324. (ptr2.hash << 24))
  325. run = self.getcompiled(f)
  326. assert run() == 0x62024230
  327. def test_write_barrier_nop(self):
  328. S = lltype.GcStruct('S', ('x', lltype.Signed))
  329. s = lltype.malloc(S)
  330. s.x = 0
  331. def f():
  332. llop.gc_writebarrier(lltype.Void, llmemory.cast_ptr_to_adr(s))
  333. return True
  334. run = self.getcompiled(f)
  335. assert run() == True
  336. def test_hash_preservation(self):
  337. class C:
  338. pass
  339. class D(C):
  340. pass
  341. c = C()
  342. d = D()
  343. compute_hash(d) # force to be cached on 'd', but not on 'c'
  344. #
  345. def fn():
  346. d2 = D()
  347. return str((compute_hash(d2),
  348. current_object_addr_as_int(d2),
  349. compute_hash(c),
  350. compute_hash(d),
  351. compute_hash(("Hi", None, (7.5, 2, d)))))
  352. f = self.getcompiled(fn)
  353. res = f()
  354. res = eval(res)
  355. # xxx the next line is too precise, checking the exact implementation
  356. assert res[0] == ~res[1]
  357. assert res[2] != compute_hash(c) # likely
  358. assert res[3] == compute_hash(d)
  359. assert res[4] == compute_hash(("Hi", None, (7.5, 2, d)))
  360. def test_finalizer_queue(self):
  361. class A(object):
  362. def __init__(self, i):
  363. self.i = i
  364. class Glob:
  365. triggered = 0
  366. glob = Glob()
  367. class FQ(rgc.FinalizerQueue):
  368. Class = A
  369. triggered = 0
  370. def finalizer_trigger(self):
  371. glob.triggered += 1
  372. fq = FQ()
  373. #
  374. def fn():
  375. for i in range(1000):
  376. fq.register_finalizer(A(i))
  377. rgc.collect()
  378. rgc.collect()
  379. if glob.triggered == 0:
  380. print "not triggered!"
  381. return 50
  382. seen = {}
  383. while True:
  384. a = fq.next_dead()
  385. if a is None:
  386. break
  387. assert a.i not in seen
  388. seen[a.i] = True
  389. if len(seen) < 500:
  390. print "seen only %d!" % len(seen)
  391. return 51
  392. return 42
  393. f = self.getcompiled(fn)
  394. res = f()
  395. assert res == 42