serve_mixin.py 3.3 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. # Copyright (C) 2010 Internet Systems Consortium.
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. import threading
  16. import socket
  17. import select
  18. SOCK_DATA = b'somedata'
  19. class ServeMixIn:
  20. '''Mix-In class to override the function serve_forever()
  21. and shutdown() in class socketserver::TCPServer.
  22. serve_forever() in socketserver::TCPServer use polling
  23. for checking request, which reduces the responsiveness to
  24. the shutdown request and wastes cpu at all other times.
  25. ServeMixIn should be used together with socketserver.TCPServer
  26. or some derived classes of it, and ServeMixIn must be the first
  27. base class in multiple inheritance, eg. MyClass(ServeMixIn,
  28. socketserver.TCPServer). ServeMixIn.__init__() should be called
  29. explicitely in derived class.
  30. '''
  31. def __init__(self):
  32. self.__serving = False
  33. self.__read_sock, self.__write_sock = socket.socketpair()
  34. self.__serve_thread = None
  35. def serve_forever(self, poll_interval=None):
  36. ''' Override the serve_forever([poll_interval]) in class
  37. socketserver.TCPServer by using the socketpair to wake up
  38. instead of polling.
  39. Note, parameter 'poll_interval' is just used to keep the
  40. interface, it's never used in this function.
  41. '''
  42. self.__serving = True
  43. started_event = threading.Event()
  44. self.__serve_thread = threading.Thread(target=self.__serve_forever, \
  45. args=(started_event,))
  46. self.__serve_thread.start()
  47. started_event.wait() # wait until the thread has started
  48. return self.__serve_thread
  49. def __serve_forever(self, syn_event):
  50. '''Use one socket pair to wake up the select when shutdown()
  51. is called in anther thread.
  52. '''
  53. self.__serving = True
  54. syn_event.set()
  55. while self.__serving:
  56. # block until the self.socket or self.__read_sock is readable
  57. try:
  58. r, w, e = select.select([self, self.__read_sock], [], [])
  59. except select.error:
  60. continue
  61. if self.__read_sock in r:
  62. break
  63. else:
  64. self._handle_request_noblock()
  65. def shutdown(self):
  66. '''Stops the self.__serve_thread( self.__serve_forever loop).
  67. when self.__serve_thread is running, it will block until the
  68. self.__serve_thread terminate.
  69. '''
  70. self.__serving = False
  71. self.__write_sock.send(SOCK_DATA) # make self.__read_sock readable.
  72. if self.__serve_thread:
  73. self.__serve_thread.join() # wait until the serve thread terminate