Mini Shell
import select
import errno
class BasePoller:
def __init__(self, options):
self.options = options
self.initialize()
def initialize(self):
pass
def register_readable(self, fd):
raise NotImplementedError
def register_writable(self, fd):
raise NotImplementedError
def unregister_readable(self, fd):
raise NotImplementedError
def unregister_writable(self, fd):
raise NotImplementedError
def poll(self, timeout):
raise NotImplementedError
def before_daemonize(self):
pass
def after_daemonize(self):
pass
def close(self):
pass
class SelectPoller(BasePoller):
def initialize(self):
self._select = select
self._init_fdsets()
def register_readable(self, fd):
self.readables.add(fd)
def register_writable(self, fd):
self.writables.add(fd)
def unregister_readable(self, fd):
self.readables.discard(fd)
def unregister_writable(self, fd):
self.writables.discard(fd)
def unregister_all(self):
self._init_fdsets()
def poll(self, timeout):
try:
r, w, x = self._select.select(
self.readables,
self.writables,
[], timeout
)
except select.error as err:
if err.args[0] == errno.EINTR:
self.options.logger.blather('EINTR encountered in poll')
return [], []
if err.args[0] == errno.EBADF:
self.options.logger.blather('EBADF encountered in poll')
self.unregister_all()
return [], []
raise
return r, w
def _init_fdsets(self):
self.readables = set()
self.writables = set()
class PollPoller(BasePoller):
def initialize(self):
self._poller = select.poll()
self.READ = select.POLLIN | select.POLLPRI | select.POLLHUP
self.WRITE = select.POLLOUT
self.readables = set()
self.writables = set()
def register_readable(self, fd):
self._poller.register(fd, self.READ)
self.readables.add(fd)
def register_writable(self, fd):
self._poller.register(fd, self.WRITE)
self.writables.add(fd)
def unregister_readable(self, fd):
self.readables.discard(fd)
self._poller.unregister(fd)
if fd in self.writables:
self._poller.register(fd, self.WRITE)
def unregister_writable(self, fd):
self.writables.discard(fd)
self._poller.unregister(fd)
if fd in self.readables:
self._poller.register(fd, self.READ)
def poll(self, timeout):
fds = self._poll_fds(timeout)
readables, writables = [], []
for fd, eventmask in fds:
if self._ignore_invalid(fd, eventmask):
continue
if eventmask & self.READ:
readables.append(fd)
if eventmask & self.WRITE:
writables.append(fd)
return readables, writables
def _poll_fds(self, timeout):
try:
return self._poller.poll(timeout * 1000)
except select.error as err:
if err.args[0] == errno.EINTR:
self.options.logger.blather('EINTR encountered in poll')
return []
raise
def _ignore_invalid(self, fd, eventmask):
if eventmask & select.POLLNVAL:
# POLLNVAL means `fd` value is invalid, not open.
# When a process quits it's `fd`s are closed so there
# is no more reason to keep this `fd` registered
# If the process restarts it's `fd`s are registered again
self._poller.unregister(fd)
self.readables.discard(fd)
self.writables.discard(fd)
return True
return False
class KQueuePoller(BasePoller):
'''
Wrapper for select.kqueue()/kevent()
'''
max_events = 1000
def initialize(self):
self._kqueue = select.kqueue()
self.readables = set()
self.writables = set()
def register_readable(self, fd):
self.readables.add(fd)
kevent = select.kevent(fd, filter=select.KQ_FILTER_READ,
flags=select.KQ_EV_ADD)
self._kqueue_control(fd, kevent)
def register_writable(self, fd):
self.writables.add(fd)
kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE,
flags=select.KQ_EV_ADD)
self._kqueue_control(fd, kevent)
def unregister_readable(self, fd):
kevent = select.kevent(fd, filter=select.KQ_FILTER_READ,
flags=select.KQ_EV_DELETE)
self.readables.discard(fd)
self._kqueue_control(fd, kevent)
def unregister_writable(self, fd):
kevent = select.kevent(fd, filter=select.KQ_FILTER_WRITE,
flags=select.KQ_EV_DELETE)
self.writables.discard(fd)
self._kqueue_control(fd, kevent)
def _kqueue_control(self, fd, kevent):
try:
self._kqueue.control([kevent], 0)
except OSError as error:
if error.errno == errno.EBADF:
self.options.logger.blather('EBADF encountered in kqueue. '
'Invalid file descriptor %s' % fd)
else:
raise
def poll(self, timeout):
readables, writables = [], []
try:
kevents = self._kqueue.control(None, self.max_events, timeout)
except OSError as error:
if error.errno == errno.EINTR:
self.options.logger.blather('EINTR encountered in poll')
return readables, writables
raise
for kevent in kevents:
if kevent.filter == select.KQ_FILTER_READ:
readables.append(kevent.ident)
if kevent.filter == select.KQ_FILTER_WRITE:
writables.append(kevent.ident)
return readables, writables
def before_daemonize(self):
self.close()
def after_daemonize(self):
self._kqueue = select.kqueue()
for fd in self.readables:
self.register_readable(fd)
for fd in self.writables:
self.register_writable(fd)
def close(self):
self._kqueue.close()
self._kqueue = None
def implements_poll():
return hasattr(select, 'poll')
def implements_kqueue():
return hasattr(select, 'kqueue')
if implements_kqueue():
Poller = KQueuePoller
elif implements_poll():
Poller = PollPoller
else:
Poller = SelectPoller
Zerion Mini Shell 1.0