/Lib/multiprocessing/sharedctypes.py

http://unladen-swallow.googlecode.com/ · Python · 238 lines · 165 code · 39 blank · 34 comment · 32 complexity · c28d2040adb0b3d5dad521b656d0c00d MD5 · raw file

  1. #
  2. # Module which supports allocation of ctypes objects from shared memory
  3. #
  4. # multiprocessing/sharedctypes.py
  5. #
  6. # Copyright (c) 2007-2008, R Oudkerk --- see COPYING.txt
  7. #
  8. import sys
  9. import ctypes
  10. import weakref
  11. from multiprocessing import heap, RLock
  12. from multiprocessing.forking import assert_spawning, ForkingPickler
  13. __all__ = ['RawValue', 'RawArray', 'Value', 'Array', 'copy', 'synchronized']
  14. #
  15. #
  16. #
  17. typecode_to_type = {
  18. 'c': ctypes.c_char, 'u': ctypes.c_wchar,
  19. 'b': ctypes.c_byte, 'B': ctypes.c_ubyte,
  20. 'h': ctypes.c_short, 'H': ctypes.c_ushort,
  21. 'i': ctypes.c_int, 'I': ctypes.c_uint,
  22. 'l': ctypes.c_long, 'L': ctypes.c_ulong,
  23. 'f': ctypes.c_float, 'd': ctypes.c_double
  24. }
  25. #
  26. #
  27. #
  28. def _new_value(type_):
  29. size = ctypes.sizeof(type_)
  30. wrapper = heap.BufferWrapper(size)
  31. return rebuild_ctype(type_, wrapper, None)
  32. def RawValue(typecode_or_type, *args):
  33. '''
  34. Returns a ctypes object allocated from shared memory
  35. '''
  36. type_ = typecode_to_type.get(typecode_or_type, typecode_or_type)
  37. obj = _new_value(type_)
  38. ctypes.memset(ctypes.addressof(obj), 0, ctypes.sizeof(obj))
  39. obj.__init__(*args)
  40. return obj
  41. def RawArray(typecode_or_type, size_or_initializer):
  42. '''
  43. Returns a ctypes array allocated from shared memory
  44. '''
  45. type_ = typecode_to_type.get(typecode_or_type, typecode_or_type)
  46. if isinstance(size_or_initializer, int):
  47. type_ = type_ * size_or_initializer
  48. return _new_value(type_)
  49. else:
  50. type_ = type_ * len(size_or_initializer)
  51. result = _new_value(type_)
  52. result.__init__(*size_or_initializer)
  53. return result
  54. def Value(typecode_or_type, *args, **kwds):
  55. '''
  56. Return a synchronization wrapper for a Value
  57. '''
  58. lock = kwds.pop('lock', None)
  59. if kwds:
  60. raise ValueError('unrecognized keyword argument(s): %s' % kwds.keys())
  61. obj = RawValue(typecode_or_type, *args)
  62. if lock is False:
  63. return obj
  64. if lock in (True, None):
  65. lock = RLock()
  66. if not hasattr(lock, 'acquire'):
  67. raise AttributeError("'%r' has no method 'acquire'" % lock)
  68. return synchronized(obj, lock)
  69. def Array(typecode_or_type, size_or_initializer, **kwds):
  70. '''
  71. Return a synchronization wrapper for a RawArray
  72. '''
  73. lock = kwds.pop('lock', None)
  74. if kwds:
  75. raise ValueError('unrecognized keyword argument(s): %s' % kwds.keys())
  76. obj = RawArray(typecode_or_type, size_or_initializer)
  77. if lock is False:
  78. return obj
  79. if lock in (True, None):
  80. lock = RLock()
  81. if not hasattr(lock, 'acquire'):
  82. raise AttributeError("'%r' has no method 'acquire'" % lock)
  83. return synchronized(obj, lock)
  84. def copy(obj):
  85. new_obj = _new_value(type(obj))
  86. ctypes.pointer(new_obj)[0] = obj
  87. return new_obj
  88. def synchronized(obj, lock=None):
  89. assert not isinstance(obj, SynchronizedBase), 'object already synchronized'
  90. if isinstance(obj, ctypes._SimpleCData):
  91. return Synchronized(obj, lock)
  92. elif isinstance(obj, ctypes.Array):
  93. if obj._type_ is ctypes.c_char:
  94. return SynchronizedString(obj, lock)
  95. return SynchronizedArray(obj, lock)
  96. else:
  97. cls = type(obj)
  98. try:
  99. scls = class_cache[cls]
  100. except KeyError:
  101. names = [field[0] for field in cls._fields_]
  102. d = dict((name, make_property(name)) for name in names)
  103. classname = 'Synchronized' + cls.__name__
  104. scls = class_cache[cls] = type(classname, (SynchronizedBase,), d)
  105. return scls(obj, lock)
  106. #
  107. # Functions for pickling/unpickling
  108. #
  109. def reduce_ctype(obj):
  110. assert_spawning(obj)
  111. if isinstance(obj, ctypes.Array):
  112. return rebuild_ctype, (obj._type_, obj._wrapper, obj._length_)
  113. else:
  114. return rebuild_ctype, (type(obj), obj._wrapper, None)
  115. def rebuild_ctype(type_, wrapper, length):
  116. if length is not None:
  117. type_ = type_ * length
  118. ForkingPickler.register(type_, reduce_ctype)
  119. obj = type_.from_address(wrapper.get_address())
  120. obj._wrapper = wrapper
  121. return obj
  122. #
  123. # Function to create properties
  124. #
  125. def make_property(name):
  126. try:
  127. return prop_cache[name]
  128. except KeyError:
  129. d = {}
  130. exec template % ((name,)*7) in d
  131. prop_cache[name] = d[name]
  132. return d[name]
  133. template = '''
  134. def get%s(self):
  135. self.acquire()
  136. try:
  137. return self._obj.%s
  138. finally:
  139. self.release()
  140. def set%s(self, value):
  141. self.acquire()
  142. try:
  143. self._obj.%s = value
  144. finally:
  145. self.release()
  146. %s = property(get%s, set%s)
  147. '''
  148. prop_cache = {}
  149. class_cache = weakref.WeakKeyDictionary()
  150. #
  151. # Synchronized wrappers
  152. #
  153. class SynchronizedBase(object):
  154. def __init__(self, obj, lock=None):
  155. self._obj = obj
  156. self._lock = lock or RLock()
  157. self.acquire = self._lock.acquire
  158. self.release = self._lock.release
  159. def __reduce__(self):
  160. assert_spawning(self)
  161. return synchronized, (self._obj, self._lock)
  162. def get_obj(self):
  163. return self._obj
  164. def get_lock(self):
  165. return self._lock
  166. def __repr__(self):
  167. return '<%s wrapper for %s>' % (type(self).__name__, self._obj)
  168. class Synchronized(SynchronizedBase):
  169. value = make_property('value')
  170. class SynchronizedArray(SynchronizedBase):
  171. def __len__(self):
  172. return len(self._obj)
  173. def __getitem__(self, i):
  174. self.acquire()
  175. try:
  176. return self._obj[i]
  177. finally:
  178. self.release()
  179. def __setitem__(self, i, value):
  180. self.acquire()
  181. try:
  182. self._obj[i] = value
  183. finally:
  184. self.release()
  185. def __getslice__(self, start, stop):
  186. self.acquire()
  187. try:
  188. return self._obj[start:stop]
  189. finally:
  190. self.release()
  191. def __setslice__(self, start, stop, values):
  192. self.acquire()
  193. try:
  194. self._obj[start:stop] = values
  195. finally:
  196. self.release()
  197. class SynchronizedString(SynchronizedArray):
  198. value = make_property('value')
  199. raw = make_property('raw')