kqueue.py 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. #
  2. # Copyright 2012 Facebook
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License"); you may
  5. # not use this file except in compliance with the License. You may obtain
  6. # a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. # License for the specific language governing permissions and limitations
  14. # under the License.
  15. """KQueue-based IOLoop implementation for BSD/Mac systems."""
  16. from __future__ import absolute_import, division, print_function
  17. import select
  18. from tornado.ioloop import IOLoop, PollIOLoop
  19. assert hasattr(select, 'kqueue'), 'kqueue not supported'
  20. class _KQueue(object):
  21. """A kqueue-based event loop for BSD/Mac systems."""
  22. def __init__(self):
  23. self._kqueue = select.kqueue()
  24. self._active = {}
  25. def fileno(self):
  26. return self._kqueue.fileno()
  27. def close(self):
  28. self._kqueue.close()
  29. def register(self, fd, events):
  30. if fd in self._active:
  31. raise IOError("fd %s already registered" % fd)
  32. self._control(fd, events, select.KQ_EV_ADD)
  33. self._active[fd] = events
  34. def modify(self, fd, events):
  35. self.unregister(fd)
  36. self.register(fd, events)
  37. def unregister(self, fd):
  38. events = self._active.pop(fd)
  39. self._control(fd, events, select.KQ_EV_DELETE)
  40. def _control(self, fd, events, flags):
  41. kevents = []
  42. if events & IOLoop.WRITE:
  43. kevents.append(select.kevent(
  44. fd, filter=select.KQ_FILTER_WRITE, flags=flags))
  45. if events & IOLoop.READ:
  46. kevents.append(select.kevent(
  47. fd, filter=select.KQ_FILTER_READ, flags=flags))
  48. # Even though control() takes a list, it seems to return EINVAL
  49. # on Mac OS X (10.6) when there is more than one event in the list.
  50. for kevent in kevents:
  51. self._kqueue.control([kevent], 0)
  52. def poll(self, timeout):
  53. kevents = self._kqueue.control(None, 1000, timeout)
  54. events = {}
  55. for kevent in kevents:
  56. fd = kevent.ident
  57. if kevent.filter == select.KQ_FILTER_READ:
  58. events[fd] = events.get(fd, 0) | IOLoop.READ
  59. if kevent.filter == select.KQ_FILTER_WRITE:
  60. if kevent.flags & select.KQ_EV_EOF:
  61. # If an asynchronous connection is refused, kqueue
  62. # returns a write event with the EOF flag set.
  63. # Turn this into an error for consistency with the
  64. # other IOLoop implementations.
  65. # Note that for read events, EOF may be returned before
  66. # all data has been consumed from the socket buffer,
  67. # so we only check for EOF on write events.
  68. events[fd] = IOLoop.ERROR
  69. else:
  70. events[fd] = events.get(fd, 0) | IOLoop.WRITE
  71. if kevent.flags & select.KQ_EV_ERROR:
  72. events[fd] = events.get(fd, 0) | IOLoop.ERROR
  73. return events.items()
  74. class KQueueIOLoop(PollIOLoop):
  75. def initialize(self, **kwargs):
  76. super(KQueueIOLoop, self).initialize(impl=_KQueue(), **kwargs)