socketserver_mixin.py 3.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  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 NoPollMixIn:
  20. '''This is a mix-in class to override the function serve_forever()
  21. and shutdown() in class socketserver.BaseServer.
  22. As commented in the module source code, serve_forever() in
  23. socketserver.BaseServer uses polling for a shutdown request, which
  24. "reduces the responsiveness to a shutdown request and wastes cpu at
  25. all other times."
  26. This class fixes this problem by introducing internal message
  27. passing via a separate socket. Note, however, that according to
  28. the module documentation serve_forever() and shutdown() are not
  29. categorized as functions that can be overridden via mix-ins. So
  30. this mix-in class may not be compatible with future versions of
  31. socketserver. It should be considered a short term workaround
  32. until the original implementation is fixed.
  33. The NoPollMixIn class should be used together with
  34. socketserver.BaseServer or some derived classes of it, and it must
  35. be placed before the corresponding socketserver class. In
  36. addition, the constructor of this mix-in must be called
  37. explicitly in the derived class. For example, a basic TCP server
  38. without the problem of polling is created as follows:
  39. class MyServer(NoPollMixIn, socketserver.TCPServer):
  40. def __init__(...):
  41. ...
  42. NoPollMixIn.__init__(self)
  43. ...
  44. To shutdown the server correctly, the serve_forever() method must
  45. be run in a separate thread, and shutdown() must be called from
  46. some other thread.
  47. '''
  48. def __init__(self):
  49. self.__read_sock, self.__write_sock = socket.socketpair()
  50. self._is_shut_down = threading.Event()
  51. def serve_forever(self, poll_interval=None):
  52. ''' Overrides the serve_forever([poll_interval]) in class
  53. socketserver.BaseServer.
  54. It uses a socketpair to wake up the select when shutdown() is
  55. called in anther thread. Note, parameter 'poll_interval' is
  56. just used for interface compatibility; it's never used in this
  57. function.
  58. '''
  59. while True:
  60. # block until the self.socket or self.__read_sock is readable
  61. try:
  62. r, w, e = select.select([self, self.__read_sock], [], [])
  63. except select.error as err:
  64. if err.args[0] == EINTR:
  65. continue
  66. else:
  67. break
  68. if self.__read_sock in r:
  69. break
  70. else:
  71. self._handle_request_noblock();
  72. self._is_shut_down.set()
  73. def shutdown(self):
  74. '''Stops the serve_forever loop.
  75. Blocks until the loop has finished, the function should be called
  76. in another thread when serve_forever is running, or it will block.
  77. '''
  78. self.__write_sock.send(SOCK_DATA) # make self.__read_sock readable.
  79. self._is_shut_down.wait() # wait until the serve thread terminate