Commit cb8a5a88 authored by Julien Muchembled's avatar Julien Muchembled

client: replace Event by a pipe as a way to stop the poll loop

This is a prerequisite for tickless poll loops.
parent 4a328ade
......@@ -15,18 +15,19 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from logging import DEBUG, ERROR
from threading import Thread, Event, enumerate as thread_enum
from threading import Thread, enumerate as thread_enum
from neo.lib import logging
from neo.lib.locking import Lock
class _ThreadedPoll(Thread):
"""Polling thread."""
stopping = False
def __init__(self, em, **kw):
Thread.__init__(self, **kw)
self.em = em
self.daemon = True
self._stop = Event()
def run(self):
_log = logging.log
......@@ -34,25 +35,24 @@ class _ThreadedPoll(Thread):
# Ignore errors due to garbage collection on exit
try:
_log(*args, **kw)
except:
if not self.stopping():
except Exception:
if not self.stopping:
raise
log(DEBUG, 'Started %s', self)
while not self.stopping():
try:
# XXX: Delay cannot be infinite here, because we need
# to check connection timeout and thread shutdown.
self.em.poll(1)
except:
log(ERROR, 'poll raised, retrying', exc_info=1)
log(DEBUG, 'Threaded poll stopped')
self._stop.clear()
try:
while 1:
try:
# XXX: Delay can't be infinite here, because we need
# to check connection timeouts.
self.em.poll(1)
except Exception:
log(ERROR, 'poll raised, retrying', exc_info=1)
finally:
log(DEBUG, 'Threaded poll stopped')
def stop(self):
self._stop.set()
def stopping(self):
return self._stop.isSet()
self.stopping = True
self.em.wakeup(True)
class ThreadedPoll(object):
"""
......@@ -81,7 +81,7 @@ class ThreadedPoll(object):
self._status_lock_acquire()
try:
thread = self._thread
if thread.stopping():
if thread.stopping:
# XXX: ideally, we should wake thread up here, to be sure not
# to wait forever.
thread.join()
......
......@@ -14,22 +14,30 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os, thread
from time import time
from select import epoll, EPOLLIN, EPOLLOUT, EPOLLERR, EPOLLHUP
from errno import EAGAIN, EINTR, ENOENT
from errno import EAGAIN, EEXIST, EINTR, ENOENT
from . import logging
from .locking import Lock
class EpollEventManager(object):
"""This class manages connections and events based on epoll(5)."""
_trigger_exit = False
def __init__(self):
self.connection_dict = {}
self.reader_set = set()
self.writer_set = set()
self.epoll = epoll()
self._pending_processing = []
self._trigger_fd, w = os.pipe()
os.close(w)
self._trigger_lock = Lock()
def close(self):
os.close(self._trigger_fd)
for c in self.connection_dict.values():
c.close()
del self.__dict__
......@@ -150,6 +158,12 @@ class EpollEventManager(object):
try:
conn = self.connection_dict[fd]
except KeyError:
if fd == self._trigger_fd:
with self._trigger_lock:
self.epoll.unregister(fd)
if self._trigger_exit:
del self._trigger_exit
thread.exit()
continue
if conn.readable():
self._addPendingConnection(conn)
......@@ -158,6 +172,16 @@ class EpollEventManager(object):
for conn in self.connection_dict.values():
conn.checkTimeout(t)
def wakeup(self, exit=False):
with self._trigger_lock:
self._trigger_exit |= exit
try:
self.epoll.register(self._trigger_fd)
except IOError, e:
# Ignore if 'wakeup' is called several times in a row.
if e.errno != EEXIST:
raise
def addReader(self, conn):
connector = conn.getConnector()
assert connector is not None, conn.whoSetConnector()
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment