PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/pypy/module/_rawffi/callback.py

https://bitbucket.org/pypy/pypy/
Python | 143 lines | 122 code | 13 blank | 8 comment | 15 complexity | 1b8ce0866595ca0bd80728fa94a3fe16 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. import sys
  2. from pypy.interpreter.gateway import interp2app, unwrap_spec
  3. from pypy.interpreter.typedef import TypeDef, GetSetProperty
  4. from rpython.rtyper.lltypesystem import lltype, rffi
  5. from pypy.module._rawffi.interp_rawffi import write_ptr
  6. from pypy.module._rawffi.structure import W_Structure
  7. from pypy.module._rawffi.interp_rawffi import (W_DataInstance, letter2tp,
  8. unwrap_value, unpack_argshapes, got_libffi_error, is_narrow_integer_type,
  9. LL_TYPEMAP, NARROW_INTEGER_TYPES)
  10. from rpython.rlib.clibffi import USERDATA_P, CallbackFuncPtr, FUNCFLAG_CDECL
  11. from rpython.rlib.clibffi import ffi_type_void, LibFFIError
  12. from rpython.rlib import rweakref
  13. from pypy.module._rawffi.tracker import tracker
  14. from pypy.interpreter.error import OperationError
  15. from pypy.interpreter import gateway
  16. from rpython.rlib.unroll import unrolling_iterable
  17. BIGENDIAN = sys.byteorder == 'big'
  18. unroll_narrow_integer_types = unrolling_iterable(NARROW_INTEGER_TYPES)
  19. app = gateway.applevel('''
  20. def tbprint(tb, err):
  21. import traceback, sys
  22. traceback.print_tb(tb)
  23. print >>sys.stderr, err
  24. ''', filename=__file__)
  25. tbprint = app.interphook("tbprint")
  26. def callback(ll_args, ll_res, ll_userdata):
  27. userdata = rffi.cast(USERDATA_P, ll_userdata)
  28. callback_ptr = global_counter.get(userdata.addarg)
  29. w_callable = callback_ptr.w_callable
  30. argtypes = callback_ptr.argtypes
  31. must_leave = False
  32. space = callback_ptr.space
  33. try:
  34. must_leave = space.threadlocals.try_enter_thread(space)
  35. args_w = [None] * len(argtypes)
  36. for i in range(len(argtypes)):
  37. argtype = argtypes[i]
  38. if isinstance(argtype, W_Structure):
  39. args_w[i] = argtype.fromaddress(
  40. space, rffi.cast(rffi.SIZE_T, ll_args[i]))
  41. else:
  42. # XXX other types?
  43. args_w[i] = space.wrap(rffi.cast(rffi.ULONG, ll_args[i]))
  44. w_res = space.call(w_callable, space.newtuple(args_w))
  45. if callback_ptr.result is not None: # don't return void
  46. ptr = ll_res
  47. letter = callback_ptr.result
  48. if BIGENDIAN:
  49. # take care of narrow integers!
  50. for int_type in unroll_narrow_integer_types:
  51. if int_type == letter:
  52. T = LL_TYPEMAP[int_type]
  53. n = rffi.sizeof(lltype.Signed) - rffi.sizeof(T)
  54. ptr = rffi.ptradd(ptr, n)
  55. break
  56. unwrap_value(space, write_ptr, ptr, 0, letter, w_res)
  57. except OperationError as e:
  58. tbprint(space, space.wrap(e.get_traceback()),
  59. space.wrap(e.errorstr(space)))
  60. # force the result to be zero
  61. if callback_ptr.result is not None:
  62. resshape = letter2tp(space, callback_ptr.result)
  63. for i in range(resshape.size):
  64. ll_res[i] = '\x00'
  65. if must_leave:
  66. space.threadlocals.leave_thread(space)
  67. class W_CallbackPtr(W_DataInstance):
  68. def __init__(self, space, w_callable, w_args, w_result,
  69. flags=FUNCFLAG_CDECL):
  70. self.space = space
  71. self.w_callable = w_callable
  72. self.argtypes = unpack_argshapes(space, w_args)
  73. ffiargs = [tp.get_basic_ffi_type() for tp in self.argtypes]
  74. if not space.is_w(w_result, space.w_None):
  75. self.result = space.str_w(w_result)
  76. ffiresult = letter2tp(space, self.result).get_basic_ffi_type()
  77. else:
  78. self.result = None
  79. ffiresult = ffi_type_void
  80. self.number = global_counter.add(self)
  81. try:
  82. self.ll_callback = CallbackFuncPtr(ffiargs, ffiresult,
  83. callback, self.number, flags)
  84. except LibFFIError:
  85. raise got_libffi_error(space)
  86. self.ll_buffer = rffi.cast(rffi.VOIDP, self.ll_callback.ll_closure)
  87. if tracker.DO_TRACING:
  88. addr = rffi.cast(lltype.Signed, self.ll_callback.ll_closure)
  89. tracker.trace_allocation(addr, self)
  90. #
  91. # We must setup the GIL here, in case the callback is invoked in
  92. # some other non-Pythonic thread. This is the same as ctypes on
  93. # CPython (but only when creating a callback; on CPython it occurs
  94. # as soon as we import _ctypes)
  95. if space.config.translation.thread:
  96. from pypy.module.thread.os_thread import setup_threads
  97. setup_threads(space)
  98. def free(self):
  99. if tracker.DO_TRACING:
  100. addr = rffi.cast(lltype.Signed, self.ll_callback.ll_closure)
  101. tracker.trace_free(addr)
  102. global_counter.remove(self.number)
  103. # A global storage to be able to recover W_CallbackPtr object out of number
  104. class GlobalCounter:
  105. def __init__(self):
  106. self.callback_id = 0
  107. self.callbacks = rweakref.RWeakValueDictionary(int, W_CallbackPtr)
  108. def add(self, w_callback):
  109. self.callback_id += 1
  110. id = self.callback_id
  111. self.callbacks.set(id, w_callback)
  112. return id
  113. def remove(self, id):
  114. self.callbacks.set(id, None)
  115. def get(self, id):
  116. return self.callbacks.get(id)
  117. global_counter = GlobalCounter()
  118. @unwrap_spec(flags=int)
  119. def descr_new_callbackptr(space, w_type, w_callable, w_args, w_result,
  120. flags=FUNCFLAG_CDECL):
  121. return W_CallbackPtr(space, w_callable, w_args, w_result, flags)
  122. W_CallbackPtr.typedef = TypeDef(
  123. 'CallbackPtr',
  124. __new__ = interp2app(descr_new_callbackptr),
  125. byptr = interp2app(W_CallbackPtr.byptr),
  126. buffer = GetSetProperty(W_CallbackPtr.getbuffer),
  127. free = interp2app(W_CallbackPtr.free),
  128. )