PageRenderTime 38ms CodeModel.GetById 9ms RepoModel.GetById 0ms app.codeStats 0ms

/common/pycsp/net/channel.py

https://github.com/a4/a4
Python | 236 lines | 229 code | 0 blank | 7 comment | 0 complexity | b42e8c34b0ac0f6241b1c6bbed5673dd MD5 | raw file
  1. """
  2. Channel module
  3. Copyright (c) 2009 John Markus Bjoerndalen <jmb@cs.uit.no>,
  4. Brian Vinter <vinter@diku.dk>, Rune M. Friborg <runef@diku.dk>.
  5. Permission is hereby granted, free of charge, to any person obtaining
  6. a copy of this software and associated documentation files (the
  7. "Software"), to deal in the Software without restriction, including
  8. without limitation the rights to use, copy, modify, merge, publish,
  9. distribute, sublicense, and/or sell copies of the Software, and to
  10. permit persons to whom the Software is furnished to do so, subject to
  11. the following conditions:
  12. The above copyright notice and this permission notice shall be
  13. included in all copies or substantial portions of the Software. THE
  14. SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  16. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  17. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  18. LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  19. OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  20. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  21. """
  22. # Imports
  23. import threading, random, time
  24. from channelend import ChannelRetireException
  25. from pycsp.common.const import *
  26. # Exceptions
  27. class ChannelPoisonException(Exception):
  28. def __init__(self):
  29. pass
  30. # Classes
  31. class ReqStatus:
  32. def __init__(self, state=ACTIVE):
  33. self.state=state
  34. self.cond = threading.Condition()
  35. class ChannelReq:
  36. def __init__(self, status, msg=None, signal=None, name=None):
  37. self.status=status
  38. self.msg=msg
  39. self.signal=signal
  40. self.result=FAIL
  41. self.name=name
  42. def cancel(self):
  43. self.status.cond.acquire()
  44. self.status.state=CANCEL
  45. self.status.cond.notifyAll()
  46. self.status.cond.release()
  47. def poison(self):
  48. self.status.cond.acquire()
  49. if self.result == FAIL and self.status.state == ACTIVE:
  50. self.status.state=POISON
  51. self.result=POISON
  52. self.status.cond.notifyAll()
  53. self.status.cond.release()
  54. def retire(self):
  55. self.status.cond.acquire()
  56. if self.result == FAIL and self.status.state == ACTIVE:
  57. self.status.state=RETIRE
  58. self.result=RETIRE
  59. self.status.cond.notifyAll()
  60. self.status.cond.release()
  61. def wait(self):
  62. self.status.cond.acquire()
  63. while self.status.state==ACTIVE:
  64. self.status.cond.wait()
  65. self.status.cond.release()
  66. def offer(self, recipient):
  67. # Eliminate unnecessary locking, by adding an extra test
  68. if self.status.state == recipient.status.state == ACTIVE:
  69. s_cond = self.status.cond
  70. r_cond = recipient.status.cond
  71. # Ensuring to lock in the correct order.
  72. if s_cond < r_cond:
  73. s_cond.acquire()
  74. r_cond.acquire()
  75. else:
  76. r_cond.acquire()
  77. s_cond.acquire()
  78. if self.status.state == recipient.status.state == ACTIVE:
  79. recipient.msg=self.msg
  80. self.status.state=DONE
  81. self.result=SUCCESS
  82. recipient.status.state=DONE
  83. recipient.result=SUCCESS
  84. s_cond.notifyAll()
  85. r_cond.notifyAll()
  86. # Ensuring that we also release in the correct order. ( done in the opposite order of locking )
  87. if s_cond < r_cond:
  88. r_cond.release()
  89. s_cond.release()
  90. else:
  91. s_cond.release()
  92. r_cond.release()
  93. class RealChannel():
  94. """
  95. RealChannel is the Channel object that handles synchronization
  96. at the channel server daemon.
  97. """
  98. def __init__(self, name=None):
  99. self.readqueue=[]
  100. self.writequeue=[]
  101. self.ispoisoned=False
  102. self.isretired=False
  103. self.readers=0
  104. self.writers=0
  105. if name == None:
  106. self.name = str(random.random())+str(time.time())
  107. else:
  108. self.name=name
  109. # This lock is used to ensure atomic updates of the channelend
  110. # reference counting
  111. self.lock = threading.RLock()
  112. # We can avoid protecting the queue operations with this lock
  113. # , because of the Global Interpreter Lock preventing us from
  114. # updating Python lists simultaneously from multiple threads.
  115. def check_termination(self):
  116. if self.ispoisoned:
  117. raise ChannelPoisonException()
  118. if self.isretired:
  119. raise ChannelRetireException()
  120. def _read(self):
  121. self.check_termination()
  122. req=ChannelReq(ReqStatus(), name=self.name)
  123. self.post_read(req)
  124. req.wait()
  125. self.remove_read(req)
  126. if req.result==SUCCESS:
  127. return req.msg
  128. self.check_termination()
  129. print 'We should not get here in read!!!', req.status.state
  130. return None #Here we should handle that a read was cancled...
  131. def _write(self, msg):
  132. self.check_termination()
  133. req=ChannelReq(ReqStatus(), msg)
  134. self.post_write(req)
  135. req.wait()
  136. self.remove_write(req)
  137. if req.result==SUCCESS:
  138. return req.msg
  139. self.check_termination()
  140. print 'We should not get here in write!!!', req.status
  141. return None #Here we should handle that a read was cancled...
  142. def post_read(self, req):
  143. self.check_termination()
  144. self.readqueue.append(req) # ATOMIC
  145. self.match()
  146. def remove_read(self, req):
  147. self.readqueue.remove(req) # ATOMIC
  148. def post_write(self, req):
  149. self.check_termination()
  150. self.writequeue.append(req) # ATOMIC
  151. self.match()
  152. def remove_write(self, req):
  153. self.writequeue.remove(req) # ATOMIC
  154. def match(self):
  155. for w in self.writequeue[:]: # ATOMIC copy
  156. for r in self.readqueue[:]: # ATOMIC copy
  157. w.offer(r)
  158. def poison(self):
  159. self.ispoisoned=True
  160. for p in self.readqueue[:]: # ATOMIC copy
  161. p.poison()
  162. for p in self.writequeue[:]: # ATOMIC copy
  163. p.poison()
  164. def join_reader(self):
  165. self.lock.acquire()
  166. self.readers+=1
  167. self.lock.release()
  168. def join_writer(self):
  169. self.lock.acquire()
  170. self.writers+=1
  171. self.lock.release()
  172. def leave_reader(self):
  173. self.lock.acquire()
  174. if not self.isretired:
  175. self.readers-=1
  176. if self.readers==0:
  177. # Set channel retired
  178. self.isretired = True
  179. for p in self.writequeue[:]: # ATOMIC copy
  180. p.retire()
  181. self.lock.release()
  182. def leave_writer(self):
  183. self.lock.acquire()
  184. if not self.isretired:
  185. self.writers-=1
  186. if self.writers==0:
  187. # Set channel retired
  188. self.isretired = True
  189. for p in self.readqueue[:]: # ATOMIC copy
  190. p.retire()
  191. self.lock.release()
  192. # Run tests
  193. if __name__ == '__main__':
  194. import doctest
  195. doctest.testmod()