PageRenderTime 56ms CodeModel.GetById 27ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/rlib/rawrefcount.py

https://bitbucket.org/pypy/pypy/
Python | 283 lines | 255 code | 9 blank | 19 comment | 2 complexity | c21d8509d96174c2e7fe43d9eabee0b9 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. #
  2. # See documentation in pypy/doc/discussion/rawrefcount.rst
  3. #
  4. # This is meant for pypy's cpyext module, but is a generally
  5. # useful interface over our GC. XXX "pypy" should be removed here
  6. #
  7. import sys, weakref
  8. from rpython.rtyper.lltypesystem import lltype, llmemory
  9. from rpython.rlib.objectmodel import we_are_translated, specialize
  10. from rpython.rtyper.extregistry import ExtRegistryEntry
  11. from rpython.rlib import rgc
  12. REFCNT_FROM_PYPY = sys.maxint // 4 + 1
  13. REFCNT_FROM_PYPY_LIGHT = REFCNT_FROM_PYPY + (sys.maxint // 2 + 1)
  14. RAWREFCOUNT_DEALLOC_TRIGGER = lltype.Ptr(lltype.FuncType([], lltype.Void))
  15. def _build_pypy_link(p):
  16. res = len(_adr2pypy)
  17. _adr2pypy.append(p)
  18. return res
  19. def init(dealloc_trigger_callback=None):
  20. """NOT_RPYTHON: set up rawrefcount with the GC. This is only used
  21. for tests; it should not be called at all during translation.
  22. """
  23. global _p_list, _o_list, _adr2pypy, _pypy2ob, _pypy2ob_rev
  24. global _d_list, _dealloc_trigger_callback
  25. _p_list = []
  26. _o_list = []
  27. _adr2pypy = [None]
  28. _pypy2ob = {}
  29. _pypy2ob_rev = {}
  30. _d_list = []
  31. _dealloc_trigger_callback = dealloc_trigger_callback
  32. def create_link_pypy(p, ob):
  33. "NOT_RPYTHON: a link where the PyPy object contains some or all the data"
  34. #print 'create_link_pypy\n\t%s\n\t%s' % (p, ob)
  35. assert p not in _pypy2ob
  36. assert ob._obj not in _pypy2ob_rev
  37. assert not ob.c_ob_pypy_link
  38. ob.c_ob_pypy_link = _build_pypy_link(p)
  39. _pypy2ob[p] = ob
  40. _pypy2ob_rev[ob._obj] = p
  41. _p_list.append(ob)
  42. def create_link_pyobj(p, ob):
  43. """NOT_RPYTHON: a link where the PyObject contains all the data.
  44. from_obj() will not work on this 'p'."""
  45. #print 'create_link_pyobj\n\t%s\n\t%s' % (p, ob)
  46. assert p not in _pypy2ob
  47. assert ob._obj not in _pypy2ob_rev
  48. assert not ob.c_ob_pypy_link
  49. ob.c_ob_pypy_link = _build_pypy_link(p)
  50. _o_list.append(ob)
  51. def from_obj(OB_PTR_TYPE, p):
  52. "NOT_RPYTHON"
  53. ob = _pypy2ob.get(p)
  54. if ob is None:
  55. return lltype.nullptr(OB_PTR_TYPE.TO)
  56. assert lltype.typeOf(ob) == OB_PTR_TYPE
  57. assert _pypy2ob_rev[ob._obj] is p
  58. return ob
  59. def to_obj(Class, ob):
  60. "NOT_RPYTHON"
  61. link = ob.c_ob_pypy_link
  62. if link == 0:
  63. return None
  64. p = _adr2pypy[link]
  65. assert isinstance(p, Class)
  66. return p
  67. def next_dead(OB_PTR_TYPE):
  68. """NOT_RPYTHON. When the GC runs, it finds some pyobjs to be dead
  69. but cannot immediately dispose of them (it doesn't know how to call
  70. e.g. tp_dealloc(), and anyway calling it immediately would cause all
  71. sorts of bugs). So instead, it stores them in an internal list,
  72. initially with refcnt == 1. This pops the next item off this list.
  73. """
  74. if len(_d_list) == 0:
  75. return lltype.nullptr(OB_PTR_TYPE.TO)
  76. ob = _d_list.pop()
  77. assert lltype.typeOf(ob) == OB_PTR_TYPE
  78. return ob
  79. def _collect(track_allocation=True):
  80. """NOT_RPYTHON: for tests only. Emulates a GC collection.
  81. Will invoke dealloc_trigger_callback() once if there are objects
  82. whose _Py_Dealloc() should be called.
  83. """
  84. def detach(ob, wr_list):
  85. assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
  86. assert ob.c_ob_pypy_link
  87. p = _adr2pypy[ob.c_ob_pypy_link]
  88. assert p is not None
  89. _adr2pypy[ob.c_ob_pypy_link] = None
  90. wr_list.append((ob, weakref.ref(p)))
  91. return p
  92. global _p_list, _o_list
  93. wr_p_list = []
  94. new_p_list = []
  95. for ob in reversed(_p_list):
  96. if ob.c_ob_refcnt not in (REFCNT_FROM_PYPY, REFCNT_FROM_PYPY_LIGHT):
  97. new_p_list.append(ob)
  98. else:
  99. p = detach(ob, wr_p_list)
  100. ob_test = _pypy2ob.pop(p)
  101. p_test = _pypy2ob_rev.pop(ob_test._obj)
  102. assert p_test is p
  103. del p, p_test
  104. ob = None
  105. _p_list = Ellipsis
  106. wr_o_list = []
  107. for ob in reversed(_o_list):
  108. detach(ob, wr_o_list)
  109. _o_list = Ellipsis
  110. rgc.collect() # forces the cycles to be resolved and the weakrefs to die
  111. rgc.collect()
  112. rgc.collect()
  113. def attach(ob, wr, final_list):
  114. assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
  115. p = wr()
  116. if p is not None:
  117. assert ob.c_ob_pypy_link
  118. _adr2pypy[ob.c_ob_pypy_link] = p
  119. final_list.append(ob)
  120. return p
  121. else:
  122. ob.c_ob_pypy_link = 0
  123. if ob.c_ob_refcnt >= REFCNT_FROM_PYPY_LIGHT:
  124. ob.c_ob_refcnt -= REFCNT_FROM_PYPY_LIGHT
  125. ob.c_ob_pypy_link = 0
  126. if ob.c_ob_refcnt == 0:
  127. lltype.free(ob, flavor='raw',
  128. track_allocation=track_allocation)
  129. else:
  130. assert ob.c_ob_refcnt >= REFCNT_FROM_PYPY
  131. assert ob.c_ob_refcnt < int(REFCNT_FROM_PYPY_LIGHT * 0.99)
  132. ob.c_ob_refcnt -= REFCNT_FROM_PYPY
  133. ob.c_ob_pypy_link = 0
  134. if ob.c_ob_refcnt == 0:
  135. ob.c_ob_refcnt = 1
  136. _d_list.append(ob)
  137. return None
  138. _p_list = new_p_list
  139. for ob, wr in wr_p_list:
  140. p = attach(ob, wr, _p_list)
  141. if p is not None:
  142. _pypy2ob[p] = ob
  143. _pypy2ob_rev.clear() # rebuild this dict from scratch
  144. for p, ob in _pypy2ob.items():
  145. assert ob._obj not in _pypy2ob_rev
  146. _pypy2ob_rev[ob._obj] = p
  147. _o_list = []
  148. for ob, wr in wr_o_list:
  149. attach(ob, wr, _o_list)
  150. if _d_list:
  151. res = _dealloc_trigger_callback()
  152. if res == "RETRY":
  153. _collect(track_allocation=track_allocation)
  154. _keepalive_forever = set()
  155. def _dont_free_any_more():
  156. "Make sure that any object still referenced won't be freed any more."
  157. for ob in _p_list + _o_list:
  158. _keepalive_forever.add(to_obj(object, ob))
  159. del _d_list[:]
  160. # ____________________________________________________________
  161. def _unspec_p(hop, v_p):
  162. assert isinstance(v_p.concretetype, lltype.Ptr)
  163. assert v_p.concretetype.TO._gckind == 'gc'
  164. return hop.genop('cast_opaque_ptr', [v_p], resulttype=llmemory.GCREF)
  165. def _unspec_ob(hop, v_ob):
  166. assert isinstance(v_ob.concretetype, lltype.Ptr)
  167. assert v_ob.concretetype.TO._gckind == 'raw'
  168. return hop.genop('cast_ptr_to_adr', [v_ob], resulttype=llmemory.Address)
  169. def _spec_p(hop, v_p):
  170. assert v_p.concretetype == llmemory.GCREF
  171. return hop.genop('cast_opaque_ptr', [v_p],
  172. resulttype=hop.r_result.lowleveltype)
  173. def _spec_ob(hop, v_ob):
  174. assert v_ob.concretetype == llmemory.Address
  175. return hop.genop('cast_adr_to_ptr', [v_ob],
  176. resulttype=hop.r_result.lowleveltype)
  177. class Entry(ExtRegistryEntry):
  178. _about_ = init
  179. def compute_result_annotation(self, s_dealloc_callback):
  180. from rpython.rtyper.llannotation import SomePtr
  181. assert isinstance(s_dealloc_callback, SomePtr) # ll-ptr-to-function
  182. def specialize_call(self, hop):
  183. hop.exception_cannot_occur()
  184. [v_dealloc_callback] = hop.inputargs(hop.args_r[0])
  185. hop.genop('gc_rawrefcount_init', [v_dealloc_callback])
  186. class Entry(ExtRegistryEntry):
  187. _about_ = (create_link_pypy, create_link_pyobj)
  188. def compute_result_annotation(self, s_p, s_ob):
  189. pass
  190. def specialize_call(self, hop):
  191. if self.instance is create_link_pypy:
  192. name = 'gc_rawrefcount_create_link_pypy'
  193. elif self.instance is create_link_pyobj:
  194. name = 'gc_rawrefcount_create_link_pyobj'
  195. v_p, v_ob = hop.inputargs(*hop.args_r)
  196. hop.exception_cannot_occur()
  197. hop.genop(name, [_unspec_p(hop, v_p), _unspec_ob(hop, v_ob)])
  198. class Entry(ExtRegistryEntry):
  199. _about_ = from_obj
  200. def compute_result_annotation(self, s_OB_PTR_TYPE, s_p):
  201. from rpython.annotator import model as annmodel
  202. from rpython.rtyper.llannotation import lltype_to_annotation
  203. assert (isinstance(s_p, annmodel.SomeInstance) or
  204. annmodel.s_None.contains(s_p))
  205. assert s_OB_PTR_TYPE.is_constant()
  206. return lltype_to_annotation(s_OB_PTR_TYPE.const)
  207. def specialize_call(self, hop):
  208. hop.exception_cannot_occur()
  209. v_p = hop.inputarg(hop.args_r[1], arg=1)
  210. v_ob = hop.genop('gc_rawrefcount_from_obj', [_unspec_p(hop, v_p)],
  211. resulttype = llmemory.Address)
  212. return _spec_ob(hop, v_ob)
  213. class Entry(ExtRegistryEntry):
  214. _about_ = to_obj
  215. def compute_result_annotation(self, s_Class, s_ob):
  216. from rpython.annotator import model as annmodel
  217. from rpython.rtyper.llannotation import SomePtr
  218. assert isinstance(s_ob, SomePtr)
  219. assert s_Class.is_constant()
  220. classdef = self.bookkeeper.getuniqueclassdef(s_Class.const)
  221. return annmodel.SomeInstance(classdef, can_be_None=True)
  222. def specialize_call(self, hop):
  223. hop.exception_cannot_occur()
  224. v_ob = hop.inputarg(hop.args_r[1], arg=1)
  225. v_p = hop.genop('gc_rawrefcount_to_obj', [_unspec_ob(hop, v_ob)],
  226. resulttype = llmemory.GCREF)
  227. return _spec_p(hop, v_p)
  228. class Entry(ExtRegistryEntry):
  229. _about_ = next_dead
  230. def compute_result_annotation(self, s_OB_PTR_TYPE):
  231. from rpython.annotator import model as annmodel
  232. from rpython.rtyper.llannotation import lltype_to_annotation
  233. assert s_OB_PTR_TYPE.is_constant()
  234. return lltype_to_annotation(s_OB_PTR_TYPE.const)
  235. def specialize_call(self, hop):
  236. hop.exception_cannot_occur()
  237. v_ob = hop.genop('gc_rawrefcount_next_dead', [],
  238. resulttype = llmemory.Address)
  239. return _spec_ob(hop, v_ob)