PageRenderTime 57ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 1ms

/rpython/memory/test/gc_test_base.py

https://bitbucket.org/pypy/pypy/
Python | 1120 lines | 1094 code | 13 blank | 13 comment | 6 complexity | 44fae820d124a57dc5a5cf01405013e0 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import py
  2. import sys
  3. from rpython.memory import gcwrapper
  4. from rpython.memory.test import snippet
  5. from rpython.rtyper import llinterp
  6. from rpython.rtyper.test.test_llinterp import get_interpreter
  7. from rpython.rtyper.lltypesystem import lltype
  8. from rpython.rtyper.lltypesystem.lloperation import llop
  9. from rpython.rlib.objectmodel import we_are_translated, keepalive_until_here
  10. from rpython.rlib.objectmodel import compute_unique_id
  11. from rpython.rlib import rgc
  12. from rpython.rlib.rstring import StringBuilder
  13. from rpython.rlib.rarithmetic import LONG_BIT
  14. WORD = LONG_BIT // 8
  15. ## def stdout_ignore_ll_functions(msg):
  16. ## strmsg = str(msg)
  17. ## if "evaluating" in strmsg and "ll_" in strmsg:
  18. ## return
  19. ## print >>sys.stdout, strmsg
  20. class GCTest(object):
  21. GC_PARAMS = {}
  22. GC_CAN_MOVE = False
  23. GC_CAN_SHRINK_ARRAY = False
  24. GC_CAN_SHRINK_BIG_ARRAY = False
  25. BUT_HOW_BIG_IS_A_BIG_STRING = 3*WORD
  26. WREF_IS_INVALID_BEFORE_DEL_IS_CALLED = False
  27. def setup_class(cls):
  28. # switch on logging of interp to show more info on failing tests
  29. llinterp.log.output_disabled = False
  30. def teardown_class(cls):
  31. llinterp.log.output_disabled = True
  32. def interpret(self, func, values, **kwds):
  33. interp, graph = get_interpreter(func, values, **kwds)
  34. gcwrapper.prepare_graphs_and_create_gc(interp, self.GCClass,
  35. self.GC_PARAMS)
  36. return interp.eval_graph(graph, values)
  37. def test_llinterp_lists(self):
  38. #curr = simulator.current_size
  39. def malloc_a_lot():
  40. i = 0
  41. while i < 10:
  42. i += 1
  43. a = [1] * 10
  44. j = 0
  45. while j < 20:
  46. j += 1
  47. a.append(j)
  48. self.interpret(malloc_a_lot, [])
  49. #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
  50. #print "size before: %s, size after %s" % (curr, simulator.current_size)
  51. def test_llinterp_tuples(self):
  52. #curr = simulator.current_size
  53. def malloc_a_lot():
  54. i = 0
  55. while i < 10:
  56. i += 1
  57. a = (1, 2, i)
  58. b = [a] * 10
  59. j = 0
  60. while j < 20:
  61. j += 1
  62. b.append((1, j, i))
  63. self.interpret(malloc_a_lot, [])
  64. #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
  65. #print "size before: %s, size after %s" % (curr, simulator.current_size)
  66. def test_global_list(self):
  67. lst = []
  68. def append_to_list(i, j):
  69. lst.append([i] * 50)
  70. return lst[j][0]
  71. res = self.interpret(append_to_list, [0, 0])
  72. assert res == 0
  73. for i in range(1, 15):
  74. res = self.interpret(append_to_list, [i, i - 1])
  75. assert res == i - 1 # crashes if constants are not considered roots
  76. def test_string_concatenation(self):
  77. #curr = simulator.current_size
  78. def concat(j):
  79. lst = []
  80. for i in range(j):
  81. lst.append(str(i))
  82. return len("".join(lst))
  83. res = self.interpret(concat, [100])
  84. assert res == concat(100)
  85. #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
  86. def test_collect(self):
  87. #curr = simulator.current_size
  88. def concat(j):
  89. lst = []
  90. for i in range(j):
  91. lst.append(str(i))
  92. result = len("".join(lst))
  93. if we_are_translated():
  94. # can't call llop.gc__collect directly
  95. llop.gc__collect(lltype.Void)
  96. return result
  97. res = self.interpret(concat, [100])
  98. assert res == concat(100)
  99. #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
  100. def test_collect_0(self):
  101. #curr = simulator.current_size
  102. def concat(j):
  103. lst = []
  104. for i in range(j):
  105. lst.append(str(i))
  106. result = len("".join(lst))
  107. if we_are_translated():
  108. # can't call llop.gc__collect directly
  109. llop.gc__collect(lltype.Void, 0)
  110. return result
  111. res = self.interpret(concat, [100])
  112. assert res == concat(100)
  113. #assert simulator.current_size - curr < 16000 * INT_SIZE / 4
  114. def test_destructor(self):
  115. class B(object):
  116. pass
  117. b = B()
  118. b.nextid = 0
  119. b.num_deleted = 0
  120. class A(object):
  121. def __init__(self):
  122. self.id = b.nextid
  123. b.nextid += 1
  124. def __del__(self):
  125. b.num_deleted += 1
  126. def f(x):
  127. a = A()
  128. i = 0
  129. while i < x:
  130. i += 1
  131. a = A()
  132. llop.gc__collect(lltype.Void)
  133. llop.gc__collect(lltype.Void)
  134. return b.num_deleted
  135. res = self.interpret(f, [5])
  136. assert res == 6
  137. def test_old_style_finalizer(self):
  138. class B(object):
  139. pass
  140. b = B()
  141. b.nextid = 0
  142. b.num_deleted = 0
  143. class A(object):
  144. def __init__(self):
  145. self.id = b.nextid
  146. b.nextid += 1
  147. def __del__(self):
  148. llop.gc__collect(lltype.Void)
  149. b.num_deleted += 1
  150. def f(x):
  151. a = A()
  152. i = 0
  153. while i < x:
  154. i += 1
  155. a = A()
  156. llop.gc__collect(lltype.Void)
  157. llop.gc__collect(lltype.Void)
  158. return b.num_deleted
  159. res = self.interpret(f, [5])
  160. assert res == 6
  161. def test_finalizer(self):
  162. class B(object):
  163. pass
  164. b = B()
  165. b.nextid = 0
  166. b.num_deleted = 0
  167. class A(object):
  168. def __init__(self):
  169. self.id = b.nextid
  170. b.nextid += 1
  171. fq.register_finalizer(self)
  172. class FQ(rgc.FinalizerQueue):
  173. Class = A
  174. def finalizer_trigger(self):
  175. while self.next_dead() is not None:
  176. b.num_deleted += 1
  177. fq = FQ()
  178. def f(x):
  179. a = A()
  180. i = 0
  181. while i < x:
  182. i += 1
  183. a = A()
  184. a = None
  185. llop.gc__collect(lltype.Void)
  186. llop.gc__collect(lltype.Void)
  187. return b.num_deleted
  188. res = self.interpret(f, [5])
  189. assert res == 6
  190. def test_finalizer_delaying_next_dead(self):
  191. class B(object):
  192. pass
  193. b = B()
  194. b.nextid = 0
  195. class A(object):
  196. def __init__(self):
  197. self.id = b.nextid
  198. b.nextid += 1
  199. fq.register_finalizer(self)
  200. class FQ(rgc.FinalizerQueue):
  201. Class = A
  202. def finalizer_trigger(self):
  203. b.triggered += 1
  204. fq = FQ()
  205. def g(): # indirection to avoid leaking the result for too long
  206. A()
  207. def f(x):
  208. b.triggered = 0
  209. g()
  210. i = 0
  211. while i < x:
  212. i += 1
  213. g()
  214. llop.gc__collect(lltype.Void)
  215. llop.gc__collect(lltype.Void)
  216. assert b.triggered > 0
  217. g(); g() # two more
  218. llop.gc__collect(lltype.Void)
  219. llop.gc__collect(lltype.Void)
  220. num_deleted = 0
  221. while fq.next_dead() is not None:
  222. num_deleted += 1
  223. return num_deleted + 1000 * b.triggered
  224. res = self.interpret(f, [5])
  225. assert res in (3008, 4008, 5008), "res == %d" % (res,)
  226. def test_finalizer_two_queues_in_sequence(self):
  227. class B(object):
  228. pass
  229. b = B()
  230. b.nextid = 0
  231. b.num_deleted_1 = 0
  232. b.num_deleted_2 = 0
  233. class A(object):
  234. def __init__(self):
  235. self.id = b.nextid
  236. b.nextid += 1
  237. fq1.register_finalizer(self)
  238. class FQ1(rgc.FinalizerQueue):
  239. Class = A
  240. def finalizer_trigger(self):
  241. while True:
  242. a = self.next_dead()
  243. if a is None:
  244. break
  245. b.num_deleted_1 += 1
  246. fq2.register_finalizer(a)
  247. class FQ2(rgc.FinalizerQueue):
  248. Class = A
  249. def finalizer_trigger(self):
  250. while self.next_dead() is not None:
  251. b.num_deleted_2 += 1
  252. fq1 = FQ1()
  253. fq2 = FQ2()
  254. def f(x):
  255. A()
  256. i = 0
  257. while i < x:
  258. i += 1
  259. A()
  260. llop.gc__collect(lltype.Void)
  261. llop.gc__collect(lltype.Void)
  262. llop.gc__collect(lltype.Void)
  263. llop.gc__collect(lltype.Void)
  264. return b.num_deleted_1 + b.num_deleted_2 * 1000
  265. res = self.interpret(f, [5])
  266. assert res == 6006
  267. def test_finalizer_calls_malloc(self):
  268. class B(object):
  269. pass
  270. b = B()
  271. b.nextid = 0
  272. b.num_deleted = 0
  273. class A(object):
  274. def __init__(self):
  275. self.id = b.nextid
  276. b.nextid += 1
  277. fq.register_finalizer(self)
  278. class C(A):
  279. pass
  280. class FQ(rgc.FinalizerQueue):
  281. Class = A
  282. def finalizer_trigger(self):
  283. while True:
  284. a = self.next_dead()
  285. if a is None:
  286. break
  287. b.num_deleted += 1
  288. if not isinstance(a, C):
  289. C()
  290. fq = FQ()
  291. def f(x):
  292. a = A()
  293. i = 0
  294. while i < x:
  295. i += 1
  296. a = A()
  297. a = None
  298. llop.gc__collect(lltype.Void)
  299. llop.gc__collect(lltype.Void)
  300. return b.num_deleted
  301. res = self.interpret(f, [5])
  302. assert res == 12
  303. def test_finalizer_calls_collect(self):
  304. class B(object):
  305. pass
  306. b = B()
  307. b.nextid = 0
  308. b.num_deleted = 0
  309. class A(object):
  310. def __init__(self):
  311. self.id = b.nextid
  312. b.nextid += 1
  313. fq.register_finalizer(self)
  314. class FQ(rgc.FinalizerQueue):
  315. Class = A
  316. def finalizer_trigger(self):
  317. while self.next_dead() is not None:
  318. b.num_deleted += 1
  319. llop.gc__collect(lltype.Void)
  320. fq = FQ()
  321. def f(x):
  322. a = A()
  323. i = 0
  324. while i < x:
  325. i += 1
  326. a = A()
  327. a = None
  328. llop.gc__collect(lltype.Void)
  329. llop.gc__collect(lltype.Void)
  330. return b.num_deleted
  331. res = self.interpret(f, [5])
  332. assert res == 6
  333. def test_finalizer_resurrects(self):
  334. class B(object):
  335. pass
  336. b = B()
  337. b.nextid = 0
  338. b.num_deleted = 0
  339. class A(object):
  340. def __init__(self):
  341. self.id = b.nextid
  342. b.nextid += 1
  343. fq.register_finalizer(self)
  344. class FQ(rgc.FinalizerQueue):
  345. Class = A
  346. def finalizer_trigger(self):
  347. while True:
  348. a = self.next_dead()
  349. if a is None:
  350. break
  351. b.num_deleted += 1
  352. b.a = a
  353. fq = FQ()
  354. def f(x):
  355. a = A()
  356. i = 0
  357. while i < x:
  358. i += 1
  359. a = A()
  360. a = None
  361. llop.gc__collect(lltype.Void)
  362. llop.gc__collect(lltype.Void)
  363. aid = b.a.id
  364. b.a = None
  365. # check that finalizer_trigger() is not called again
  366. llop.gc__collect(lltype.Void)
  367. llop.gc__collect(lltype.Void)
  368. return b.num_deleted * 10 + aid + 100 * (b.a is None)
  369. res = self.interpret(f, [5])
  370. assert 160 <= res <= 165
  371. def test_custom_trace(self):
  372. from rpython.rtyper.lltypesystem import llmemory
  373. from rpython.rtyper.lltypesystem.llarena import ArenaError
  374. #
  375. S = lltype.GcStruct('S', ('x', llmemory.Address),
  376. ('y', llmemory.Address))
  377. T = lltype.GcStruct('T', ('z', lltype.Signed))
  378. offset_of_x = llmemory.offsetof(S, 'x')
  379. def customtrace(gc, obj, callback, arg):
  380. gc._trace_callback(callback, arg, obj + offset_of_x)
  381. lambda_customtrace = lambda: customtrace
  382. #
  383. for attrname in ['x', 'y']:
  384. def setup():
  385. rgc.register_custom_trace_hook(S, lambda_customtrace)
  386. s1 = lltype.malloc(S)
  387. tx = lltype.malloc(T)
  388. tx.z = 42
  389. ty = lltype.malloc(T)
  390. s1.x = llmemory.cast_ptr_to_adr(tx)
  391. s1.y = llmemory.cast_ptr_to_adr(ty)
  392. return s1
  393. def f():
  394. s1 = setup()
  395. llop.gc__collect(lltype.Void)
  396. return llmemory.cast_adr_to_ptr(getattr(s1, attrname),
  397. lltype.Ptr(T))
  398. if attrname == 'x':
  399. res = self.interpret(f, [])
  400. assert res.z == 42
  401. else:
  402. py.test.raises((RuntimeError, ArenaError),
  403. self.interpret, f, [])
  404. def test_weakref(self):
  405. import weakref
  406. class A(object):
  407. pass
  408. def g():
  409. a = A()
  410. return weakref.ref(a)
  411. def f():
  412. a = A()
  413. ref = weakref.ref(a)
  414. result = ref() is a
  415. ref = g()
  416. llop.gc__collect(lltype.Void)
  417. result = result and (ref() is None)
  418. # check that a further collection is fine
  419. llop.gc__collect(lltype.Void)
  420. result = result and (ref() is None)
  421. return result
  422. res = self.interpret(f, [])
  423. assert res
  424. def test_weakref_to_object_with_destructor(self):
  425. import weakref
  426. class A(object):
  427. count = 0
  428. a = A()
  429. class B(object):
  430. def __del__(self):
  431. a.count += 1
  432. def g():
  433. b = B()
  434. return weakref.ref(b)
  435. def f():
  436. ref = g()
  437. llop.gc__collect(lltype.Void)
  438. llop.gc__collect(lltype.Void)
  439. result = a.count == 1 and (ref() is None)
  440. return result
  441. res = self.interpret(f, [])
  442. assert res
  443. def test_weakref_to_object_with_finalizer(self):
  444. import weakref
  445. class A(object):
  446. count = 0
  447. a = A()
  448. class B(object):
  449. pass
  450. class FQ(rgc.FinalizerQueue):
  451. Class = B
  452. def finalizer_trigger(self):
  453. while self.next_dead() is not None:
  454. a.count += 1
  455. fq = FQ()
  456. def g():
  457. b = B()
  458. fq.register_finalizer(b)
  459. return weakref.ref(b)
  460. def f():
  461. ref = g()
  462. llop.gc__collect(lltype.Void)
  463. llop.gc__collect(lltype.Void)
  464. result = a.count == 1 and (ref() is None)
  465. return result
  466. res = self.interpret(f, [])
  467. assert res
  468. def test_bug_1(self):
  469. import weakref
  470. class B(object):
  471. pass
  472. def g():
  473. b = B()
  474. llop.gc__collect(lltype.Void) # force 'b' to be old
  475. ref = weakref.ref(B())
  476. b.ref = ref
  477. return ref
  478. def f():
  479. ref = g()
  480. llop.gc__collect(lltype.Void)
  481. llop.gc__collect(lltype.Void)
  482. result = (ref() is None)
  483. return result
  484. res = self.interpret(f, [])
  485. assert res
  486. def test_cycle_with_weakref_and_finalizer(self):
  487. import weakref
  488. class A(object):
  489. count = 0
  490. a = A()
  491. class B(object):
  492. pass
  493. class FQ(rgc.FinalizerQueue):
  494. Class = B
  495. def finalizer_trigger(self):
  496. while True:
  497. b = self.next_dead()
  498. if b is None:
  499. break
  500. # when we are here, the weakref to c should be dead
  501. if b.ref() is None:
  502. a.count += 10 # ok
  503. else:
  504. a.count = 666 # not ok
  505. fq = FQ()
  506. class C(object):
  507. pass
  508. def g():
  509. c = C()
  510. c.b = B()
  511. fq.register_finalizer(c.b)
  512. ref = weakref.ref(c)
  513. c.b.ref = ref
  514. return ref
  515. def f():
  516. ref = g()
  517. llop.gc__collect(lltype.Void)
  518. llop.gc__collect(lltype.Void)
  519. result = a.count + (ref() is None)
  520. return result
  521. res = self.interpret(f, [])
  522. assert res == 11
  523. def test_weakref_to_object_with_finalizer_ordering(self):
  524. import weakref
  525. class A(object):
  526. count = 0
  527. a = A()
  528. expected_invalid = self.WREF_IS_INVALID_BEFORE_DEL_IS_CALLED
  529. class B(object):
  530. pass
  531. class FQ(rgc.FinalizerQueue):
  532. Class = B
  533. def finalizer_trigger(self):
  534. # when we are here, the weakref to myself is still valid
  535. # in RPython with most GCs. However, this can lead to strange
  536. # bugs with incminimark. https://bugs.pypy.org/issue1687
  537. # So with incminimark, we expect the opposite.
  538. while True:
  539. b = self.next_dead()
  540. if b is None:
  541. break
  542. if expected_invalid:
  543. if b.ref() is None:
  544. a.count += 10 # ok
  545. else:
  546. a.count = 666 # not ok
  547. else:
  548. if b.ref() is b:
  549. a.count += 10 # ok
  550. else:
  551. a.count = 666 # not ok
  552. fq = FQ()
  553. def g():
  554. b = B()
  555. fq.register_finalizer(b)
  556. ref = weakref.ref(b)
  557. b.ref = ref
  558. return ref
  559. def f():
  560. ref = g()
  561. llop.gc__collect(lltype.Void)
  562. llop.gc__collect(lltype.Void)
  563. result = a.count + (ref() is None)
  564. return result
  565. res = self.interpret(f, [])
  566. assert res == 11
  567. def test_weakref_bug_1(self):
  568. import weakref
  569. class A(object):
  570. pass
  571. class B(object):
  572. pass
  573. class FQ(rgc.FinalizerQueue):
  574. Class = B
  575. def finalizer_trigger(self):
  576. while True:
  577. b = self.next_dead()
  578. if b is None:
  579. break
  580. b.wref().x += 1
  581. fq = FQ()
  582. def g(a):
  583. b = B()
  584. fq.register_finalizer(b)
  585. b.wref = weakref.ref(a)
  586. # the only way to reach this weakref is via B, which is an
  587. # object with finalizer (but the weakref itself points to
  588. # a, which does not go away but will move during the next
  589. # gc.collect)
  590. def f():
  591. a = A()
  592. a.x = 10
  593. g(a)
  594. llop.gc__collect(lltype.Void)
  595. return a.x
  596. res = self.interpret(f, [])
  597. assert res == 11
  598. def test_id(self):
  599. class A(object):
  600. pass
  601. a1 = A()
  602. def f():
  603. a2 = A()
  604. a3 = A()
  605. id1 = compute_unique_id(a1)
  606. id2 = compute_unique_id(a2)
  607. id3 = compute_unique_id(a3)
  608. llop.gc__collect(lltype.Void)
  609. error = 0
  610. if id1 != compute_unique_id(a1): error += 1
  611. if id2 != compute_unique_id(a2): error += 2
  612. if id3 != compute_unique_id(a3): error += 4
  613. return error
  614. res = self.interpret(f, [])
  615. assert res == 0
  616. def test_finalizer_calls_malloc_during_minor_collect(self):
  617. # originally a GenerationGC test, this has also found bugs in other GCs
  618. class B(object):
  619. pass
  620. b = B()
  621. b.nextid = 0
  622. b.num_deleted = 0
  623. b.all = []
  624. class A(object):
  625. def __init__(self):
  626. self.id = b.nextid
  627. b.nextid += 1
  628. fq.register_finalizer(self)
  629. class FQ(rgc.FinalizerQueue):
  630. Class = A
  631. def finalizer_trigger(self):
  632. while self.next_dead() is not None:
  633. b.num_deleted += 1
  634. b.all.append(D(b.num_deleted))
  635. fq = FQ()
  636. class D(object):
  637. # make a big object that does not use malloc_varsize
  638. def __init__(self, x):
  639. self.x00 = self.x01 = self.x02 = self.x03 = self.x04 = x
  640. self.x10 = self.x11 = self.x12 = self.x13 = self.x14 = x
  641. self.x20 = self.x21 = self.x22 = self.x23 = self.x24 = x
  642. def f(x):
  643. i = 0
  644. all = [None] * x
  645. a = A()
  646. del a
  647. while i < x:
  648. d = D(i)
  649. all[i] = d
  650. i += 1
  651. return b.num_deleted + len(all)
  652. res = self.interpret(f, [500])
  653. assert res == 1 + 500
  654. def test_collect_during_collect(self):
  655. class B(object):
  656. pass
  657. b = B()
  658. b.nextid = 1
  659. b.num_deleted = 0
  660. b.num_deleted_c = 0
  661. class A(object):
  662. def __init__(self):
  663. self.id = b.nextid
  664. b.nextid += 1
  665. fq.register_finalizer(self)
  666. class C(A):
  667. pass
  668. class FQ(rgc.FinalizerQueue):
  669. Class = A
  670. def finalizer_trigger(self):
  671. while True:
  672. a = self.next_dead()
  673. if a is None:
  674. break
  675. llop.gc__collect(lltype.Void)
  676. b.num_deleted += 1
  677. if isinstance(a, C):
  678. b.num_deleted_c += 1
  679. else:
  680. C()
  681. C()
  682. fq = FQ()
  683. def f(x, y):
  684. persistent_a1 = A()
  685. persistent_a2 = A()
  686. i = 0
  687. while i < x:
  688. i += 1
  689. a = A()
  690. persistent_a3 = A()
  691. persistent_a4 = A()
  692. llop.gc__collect(lltype.Void)
  693. llop.gc__collect(lltype.Void)
  694. b.bla = persistent_a1.id + persistent_a2.id + persistent_a3.id + persistent_a4.id
  695. print b.num_deleted_c
  696. return b.num_deleted
  697. res = self.interpret(f, [4, 42])
  698. assert res == 12
  699. def test_print_leak(self):
  700. def f(n):
  701. for i in range(n):
  702. print i
  703. return 42
  704. res = self.interpret(f, [10])
  705. assert res == 42
  706. def test_weakref_across_minor_collection(self):
  707. import weakref
  708. class A:
  709. pass
  710. def f(x):
  711. a = A()
  712. a.foo = x
  713. ref = weakref.ref(a)
  714. all = [None] * x
  715. i = 0
  716. while i < x:
  717. all[i] = [i] * i
  718. i += 1
  719. assert ref() is a
  720. llop.gc__collect(lltype.Void)
  721. assert ref() is a
  722. return a.foo + len(all)
  723. res = self.interpret(f, [20]) # for GenerationGC, enough for a minor collection
  724. assert res == 20 + 20
  725. def test_young_weakref_to_old_object(self):
  726. import weakref
  727. class A:
  728. pass
  729. def f(x):
  730. a = A()
  731. llop.gc__collect(lltype.Void)
  732. # 'a' is old, 'ref' is young
  733. ref = weakref.ref(a)
  734. # now trigger a minor collection
  735. all = [None] * x
  736. i = 0
  737. while i < x:
  738. all[i] = [i] * i
  739. i += 1
  740. # now 'a' is old, but 'ref' did not move
  741. assert ref() is a
  742. llop.gc__collect(lltype.Void)
  743. # now both 'a' and 'ref' have moved
  744. return ref() is a
  745. res = self.interpret(f, [20]) # for GenerationGC, enough for a minor collection
  746. assert res == True
  747. def test_weakref_to_prebuilt(self):
  748. import weakref
  749. class A:
  750. pass
  751. a = A()
  752. def f(x):
  753. ref = weakref.ref(a)
  754. assert ref() is a
  755. llop.gc__collect(lltype.Void)
  756. return ref() is a
  757. res = self.interpret(f, [20]) # for GenerationGC, enough for a minor collection
  758. assert res == True
  759. def test_many_weakrefs(self):
  760. # test for the case where allocating the weakref itself triggers
  761. # a collection
  762. import weakref
  763. class A:
  764. pass
  765. def f(x):
  766. a = A()
  767. i = 0
  768. while i < x:
  769. ref = weakref.ref(a)
  770. assert ref() is a
  771. i += 1
  772. self.interpret(f, [1100])
  773. def test_nongc_static_root(self):
  774. from rpython.rtyper.lltypesystem import lltype
  775. T1 = lltype.GcStruct("C", ('x', lltype.Signed))
  776. T2 = lltype.Struct("C", ('p', lltype.Ptr(T1)))
  777. static = lltype.malloc(T2, immortal=True)
  778. def f():
  779. t1 = lltype.malloc(T1)
  780. t1.x = 42
  781. static.p = t1
  782. llop.gc__collect(lltype.Void)
  783. return static.p.x
  784. res = self.interpret(f, [])
  785. assert res == 42
  786. def test_can_move(self):
  787. TP = lltype.GcArray(lltype.Float)
  788. def func():
  789. return rgc.can_move(lltype.malloc(TP, 1))
  790. assert self.interpret(func, []) == self.GC_CAN_MOVE
  791. def test_trace_array_of_structs(self):
  792. R = lltype.GcStruct('R', ('i', lltype.Signed))
  793. S1 = lltype.GcArray(('p1', lltype.Ptr(R)))
  794. S2 = lltype.GcArray(('p1', lltype.Ptr(R)),
  795. ('p2', lltype.Ptr(R)))
  796. S3 = lltype.GcArray(('p1', lltype.Ptr(R)),
  797. ('p2', lltype.Ptr(R)),
  798. ('p3', lltype.Ptr(R)))
  799. def func():
  800. s1 = lltype.malloc(S1, 2)
  801. s1[0].p1 = lltype.malloc(R)
  802. s1[1].p1 = lltype.malloc(R)
  803. s2 = lltype.malloc(S2, 2)
  804. s2[0].p1 = lltype.malloc(R)
  805. s2[0].p2 = lltype.malloc(R)
  806. s2[1].p1 = lltype.malloc(R)
  807. s2[1].p2 = lltype.malloc(R)
  808. s3 = lltype.malloc(S3, 2)
  809. s3[0].p1 = lltype.malloc(R)
  810. s3[0].p2 = lltype.malloc(R)
  811. s3[0].p3 = lltype.malloc(R)
  812. s3[1].p1 = lltype.malloc(R)
  813. s3[1].p2 = lltype.malloc(R)
  814. s3[1].p3 = lltype.malloc(R)
  815. s1[0].p1.i = 100
  816. s1[1].p1.i = 101
  817. s2[0].p1.i = 102
  818. s2[0].p2.i = 103
  819. s2[1].p1.i = 104
  820. s2[1].p2.i = 105
  821. s3[0].p1.i = 106
  822. s3[0].p2.i = 107
  823. s3[0].p3.i = 108
  824. s3[1].p1.i = 109
  825. s3[1].p2.i = 110
  826. s3[1].p3.i = 111
  827. rgc.collect()
  828. return ((s1[0].p1.i == 100) +
  829. (s1[1].p1.i == 101) +
  830. (s2[0].p1.i == 102) +
  831. (s2[0].p2.i == 103) +
  832. (s2[1].p1.i == 104) +
  833. (s2[1].p2.i == 105) +
  834. (s3[0].p1.i == 106) +
  835. (s3[0].p2.i == 107) +
  836. (s3[0].p3.i == 108) +
  837. (s3[1].p1.i == 109) +
  838. (s3[1].p2.i == 110) +
  839. (s3[1].p3.i == 111))
  840. res = self.interpret(func, [])
  841. assert res == 12
  842. def test_shrink_array(self):
  843. from rpython.rtyper.lltypesystem.rstr import STR
  844. def f(n, m, gc_can_shrink_array):
  845. ptr = lltype.malloc(STR, n)
  846. ptr.hash = 0x62
  847. ptr.chars[0] = 'A'
  848. ptr.chars[1] = 'B'
  849. ptr.chars[2] = 'C'
  850. ptr2 = rgc.ll_shrink_array(ptr, 2)
  851. assert (ptr == ptr2) == gc_can_shrink_array
  852. rgc.collect()
  853. return ( ord(ptr2.chars[0]) +
  854. (ord(ptr2.chars[1]) << 8) +
  855. (len(ptr2.chars) << 16) +
  856. (ptr2.hash << 24))
  857. flag = self.GC_CAN_SHRINK_ARRAY
  858. assert self.interpret(f, [3, 0, flag]) == 0x62024241
  859. # with larger numbers, it gets allocated outside the semispace
  860. # with some GCs.
  861. flag = self.GC_CAN_SHRINK_BIG_ARRAY
  862. bigsize = self.BUT_HOW_BIG_IS_A_BIG_STRING
  863. assert self.interpret(f, [bigsize, 0, flag]) == 0x62024241
  864. def test_tagged_simple(self):
  865. class Unrelated(object):
  866. pass
  867. u = Unrelated()
  868. u.x = UnboxedObject(47)
  869. def fn(n):
  870. rgc.collect() # check that a prebuilt tagged pointer doesn't explode
  871. if n > 0:
  872. x = BoxedObject(n)
  873. else:
  874. x = UnboxedObject(n)
  875. u.x = x # invoke write barrier
  876. rgc.collect()
  877. return x.meth(100)
  878. res = self.interpret(fn, [1000], taggedpointers=True)
  879. assert res == 1102
  880. res = self.interpret(fn, [-1000], taggedpointers=True)
  881. assert res == -897
  882. def test_tagged_prebuilt(self):
  883. class F:
  884. pass
  885. f = F()
  886. f.l = [UnboxedObject(10)]
  887. def fn(n):
  888. if n > 0:
  889. x = BoxedObject(n)
  890. else:
  891. x = UnboxedObject(n)
  892. f.l.append(x)
  893. rgc.collect()
  894. return f.l[-1].meth(100)
  895. res = self.interpret(fn, [1000], taggedpointers=True)
  896. assert res == 1102
  897. res = self.interpret(fn, [-1000], taggedpointers=True)
  898. assert res == -897
  899. def test_tagged_id(self):
  900. class Unrelated(object):
  901. pass
  902. u = Unrelated()
  903. u.x = UnboxedObject(0)
  904. def fn(n):
  905. id_prebuilt1 = compute_unique_id(u.x)
  906. if n > 0:
  907. x = BoxedObject(n)
  908. else:
  909. x = UnboxedObject(n)
  910. id_x1 = compute_unique_id(x)
  911. rgc.collect() # check that a prebuilt tagged pointer doesn't explode
  912. id_prebuilt2 = compute_unique_id(u.x)
  913. id_x2 = compute_unique_id(x)
  914. print u.x, id_prebuilt1, id_prebuilt2
  915. print x, id_x1, id_x2
  916. return ((id_x1 == id_x2) * 1 +
  917. (id_prebuilt1 == id_prebuilt2) * 10 +
  918. (id_x1 != id_prebuilt1) * 100)
  919. res = self.interpret(fn, [1000], taggedpointers=True)
  920. assert res == 111
  921. res = self.interpret(fn, [-1000], taggedpointers=True)
  922. assert res == 111
  923. def test_writebarrier_before_copy(self):
  924. S = lltype.GcStruct('S', ('x', lltype.Char))
  925. TP = lltype.GcArray(lltype.Ptr(S))
  926. def fn():
  927. l = lltype.malloc(TP, 100)
  928. l2 = lltype.malloc(TP, 100)
  929. for i in range(100):
  930. l[i] = lltype.malloc(S)
  931. rgc.ll_arraycopy(l, l2, 50, 0, 50)
  932. x = []
  933. # force minor collect
  934. t = (1, lltype.malloc(S))
  935. for i in range(20):
  936. x.append(t)
  937. for i in range(50):
  938. assert l2[i] == l[50 + i]
  939. return 0
  940. self.interpret(fn, [])
  941. def test_stringbuilder(self):
  942. def fn():
  943. s = StringBuilder(4)
  944. s.append("abcd")
  945. s.append("defg")
  946. s.append("rty")
  947. s.append_multiple_char('y', 1000)
  948. rgc.collect()
  949. s.append_multiple_char('y', 1000)
  950. res = s.build()[1000]
  951. rgc.collect()
  952. return ord(res)
  953. res = self.interpret(fn, [])
  954. assert res == ord('y')
  955. def test_gcflag_extra(self):
  956. class A:
  957. pass
  958. a1 = A()
  959. def fn():
  960. a2 = A()
  961. if not rgc.has_gcflag_extra():
  962. return # cannot test it then
  963. assert rgc.get_gcflag_extra(a1) == False
  964. assert rgc.get_gcflag_extra(a2) == False
  965. rgc.toggle_gcflag_extra(a1)
  966. assert rgc.get_gcflag_extra(a1) == True
  967. assert rgc.get_gcflag_extra(a2) == False
  968. rgc.toggle_gcflag_extra(a2)
  969. assert rgc.get_gcflag_extra(a1) == True
  970. assert rgc.get_gcflag_extra(a2) == True
  971. rgc.toggle_gcflag_extra(a1)
  972. assert rgc.get_gcflag_extra(a1) == False
  973. assert rgc.get_gcflag_extra(a2) == True
  974. rgc.toggle_gcflag_extra(a2)
  975. assert rgc.get_gcflag_extra(a1) == False
  976. assert rgc.get_gcflag_extra(a2) == False
  977. self.interpret(fn, [])
  978. def test_register_custom_trace_hook(self):
  979. S = lltype.GcStruct('S', ('x', lltype.Signed))
  980. called = []
  981. def trace_hook(gc, obj, callback, arg):
  982. called.append("called")
  983. lambda_trace_hook = lambda: trace_hook
  984. def f():
  985. rgc.register_custom_trace_hook(S, lambda_trace_hook)
  986. s = lltype.malloc(S)
  987. rgc.collect()
  988. keepalive_until_here(s)
  989. self.interpret(f, [])
  990. assert called # not empty, can contain more than one item
  991. def test_pinning(self):
  992. def fn(n):
  993. s = str(n)
  994. if not rgc.can_move(s):
  995. return 13
  996. res = int(rgc.pin(s))
  997. if res:
  998. rgc.unpin(s)
  999. return res
  1000. res = self.interpret(fn, [10])
  1001. if not self.GCClass.moving_gc:
  1002. assert res == 13
  1003. elif self.GCClass.can_usually_pin_objects:
  1004. assert res == 1
  1005. else:
  1006. assert res == 0 or res == 13
  1007. def test__is_pinned(self):
  1008. def fn(n):
  1009. from rpython.rlib.debug import debug_print
  1010. s = str(n)
  1011. if not rgc.can_move(s):
  1012. return 13
  1013. res = int(rgc.pin(s))
  1014. if res:
  1015. res += int(rgc._is_pinned(s))
  1016. rgc.unpin(s)
  1017. return res
  1018. res = self.interpret(fn, [10])
  1019. if not self.GCClass.moving_gc:
  1020. assert res == 13
  1021. elif self.GCClass.can_usually_pin_objects:
  1022. assert res == 2
  1023. else:
  1024. assert res == 0 or res == 13
  1025. def test_gettypeid(self):
  1026. class A(object):
  1027. pass
  1028. def fn():
  1029. a = A()
  1030. return rgc.get_typeid(a)
  1031. self.interpret(fn, [])
  1032. from rpython.rlib.objectmodel import UnboxedValue
  1033. class TaggedBase(object):
  1034. __slots__ = ()
  1035. def meth(self, x):
  1036. raise NotImplementedError
  1037. class BoxedObject(TaggedBase):
  1038. attrvalue = 66
  1039. def __init__(self, normalint):
  1040. self.normalint = normalint
  1041. def meth(self, x):
  1042. return self.normalint + x + 2
  1043. class UnboxedObject(TaggedBase, UnboxedValue):
  1044. __slots__ = 'smallint'
  1045. def meth(self, x):
  1046. return self.smallint + x + 3