mirror of
https://github.com/jlu5/PyLink.git
synced 2024-11-01 01:09:22 +01:00
parent
57f77c676d
commit
f7ab2564fe
49
classes.py
49
classes.py
@ -24,7 +24,7 @@ try:
|
||||
except ImportError:
|
||||
raise ImportError("PyLink requires ircmatch to function; please install it and try again.")
|
||||
|
||||
from . import world, utils, structures, conf, __version__
|
||||
from . import world, utils, structures, conf, __version__, selectdriver
|
||||
from .log import *
|
||||
from .utils import ProtocolError # Compatibility with PyLink 1.x
|
||||
|
||||
@ -1300,10 +1300,10 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self._connection_thread = None
|
||||
self._queue = None
|
||||
self._ping_timer = None
|
||||
self._socket = None
|
||||
self._selector_key = None
|
||||
|
||||
def _init_vars(self, *args, **kwargs):
|
||||
super()._init_vars(*args, **kwargs)
|
||||
@ -1335,12 +1335,10 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
else:
|
||||
log.error(*args, **kwargs)
|
||||
|
||||
def _connect(self):
|
||||
def connect(self):
|
||||
"""
|
||||
Runs the connect loop for the IRC object. This is usually called by
|
||||
__init__ in a separate thread to allow multiple concurrent connections.
|
||||
Connects to the network.
|
||||
"""
|
||||
while True:
|
||||
self._pre_connect()
|
||||
|
||||
ip = self.serverdata["ip"]
|
||||
@ -1352,7 +1350,6 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
|
||||
# Creat the socket.
|
||||
self._socket = socket.socket(stype)
|
||||
self._socket.setblocking(0)
|
||||
|
||||
# Set the socket bind if applicable.
|
||||
if 'bindhost' in self.serverdata:
|
||||
@ -1394,6 +1391,7 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
|
||||
self._socket = context.wrap_socket(self._socket)
|
||||
|
||||
self._selector_key = selectdriver.register(self)
|
||||
log.info("Connecting to network %r on %s:%s", self.name, ip, port)
|
||||
self._socket.connect((ip, port))
|
||||
self._socket.settimeout(self.pingtimeout)
|
||||
@ -1461,7 +1459,6 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
self._schedule_ping()
|
||||
log.info('(%s) Server ready; listening for data.', self.name)
|
||||
self.autoconnect_active_multiplier = 1 # Reset any extra autoconnect delays
|
||||
self._run_irc()
|
||||
else: # Configuration error :(
|
||||
log.error('(%s) A configuration error was encountered '
|
||||
'trying to set up this connection. Please check'
|
||||
@ -1474,32 +1471,18 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
# IRC connection from freezing instead.
|
||||
except (OSError, RuntimeError, SystemExit) as e:
|
||||
self._log_connection_error('(%s) Disconnected from IRC:', self.name, exc_info=True)
|
||||
|
||||
if not self._aborted.is_set():
|
||||
self.disconnect()
|
||||
|
||||
if not self._run_autoconnect():
|
||||
return
|
||||
|
||||
def connect(self):
|
||||
log.debug('(%s) calling _connect() (world.testing=%s)', self.name, world.testing)
|
||||
if world.testing:
|
||||
# HACK: Don't thread if we're running tests.
|
||||
self._connect()
|
||||
else:
|
||||
if self._connection_thread and self._connection_thread.is_alive():
|
||||
raise RuntimeError("Refusing to start multiple connection threads for network %r!" % self.name)
|
||||
|
||||
self._connection_thread = threading.Thread(target=self._connect,
|
||||
name="Listener for %s" %
|
||||
self.name)
|
||||
self._connection_thread.start()
|
||||
|
||||
def disconnect(self):
|
||||
"""Handle disconnects from the remote server."""
|
||||
self._pre_disconnect()
|
||||
|
||||
if self._socket is not None:
|
||||
selectdriver.unregister(self)
|
||||
try:
|
||||
log.debug('(%s) disconnect: Shutting down socket.', self.name)
|
||||
self._socket.shutdown(socket.SHUT_RDWR)
|
||||
@ -1523,6 +1506,9 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
self._ping_timer.cancel()
|
||||
self._post_disconnect()
|
||||
|
||||
if self._run_autoconnect():
|
||||
self.connect()
|
||||
|
||||
def handle_events(self, line):
|
||||
raise NotImplementedError
|
||||
|
||||
@ -1547,18 +1533,13 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
return hook_args
|
||||
|
||||
def _run_irc(self):
|
||||
"""Main IRC loop which listens for messages."""
|
||||
buf = b""
|
||||
data = b""
|
||||
while (not self._aborted.is_set()) and not world.shutting_down.is_set():
|
||||
|
||||
"""
|
||||
Message handler, called when select() has data to read.
|
||||
"""
|
||||
buf = b''
|
||||
data = b''
|
||||
try:
|
||||
data = self._socket.recv(2048)
|
||||
except BlockingIOError:
|
||||
log.debug('(%s) No data to read, trying again later...', self.name)
|
||||
if self._aborted.wait(self.SOCKET_REPOLL_WAIT):
|
||||
break
|
||||
continue
|
||||
except OSError:
|
||||
# Suppress socket read warnings from lingering recv() calls if
|
||||
# we've been told to shutdown.
|
||||
@ -1569,9 +1550,11 @@ class IRCNetwork(PyLinkNetworkCoreWithUtils):
|
||||
buf += data
|
||||
if not data:
|
||||
self._log_connection_error('(%s) Connection lost, disconnecting.', self.name)
|
||||
self.disconnect()
|
||||
return
|
||||
elif (time.time() - self.lastping) > self.pingtimeout:
|
||||
self._log_connection_error('(%s) Connection timed out.', self.name)
|
||||
self.disconnect()
|
||||
return
|
||||
|
||||
while b'\n' in buf:
|
||||
|
@ -44,7 +44,7 @@ def main():
|
||||
conf.load_conf(args.config)
|
||||
|
||||
from pylinkirc.log import log
|
||||
from pylinkirc import classes, utils, coremods
|
||||
from pylinkirc import classes, utils, coremods, selectdriver
|
||||
|
||||
world.daemon = args.daemonize
|
||||
if args.daemonize:
|
||||
@ -177,3 +177,4 @@ def main():
|
||||
|
||||
world.started.set()
|
||||
log.info("Loaded plugins: %s", ', '.join(sorted(world.plugins.keys())))
|
||||
selectdriver.start()
|
||||
|
44
selectdriver.py
Normal file
44
selectdriver.py
Normal file
@ -0,0 +1,44 @@
|
||||
"""
|
||||
Socket handling driver using the selectors module. epoll, kqueue, and devpoll
|
||||
are used internally when available.
|
||||
"""
|
||||
|
||||
import selectors
|
||||
import threading
|
||||
|
||||
from pylinkirc import world
|
||||
from pylinkirc.log import log
|
||||
|
||||
SELECT_TIMEOUT = 0.5
|
||||
|
||||
selector = selectors.DefaultSelector()
|
||||
|
||||
def _process_conns():
|
||||
"""Main loop which processes connected sockets."""
|
||||
|
||||
while not world.shutting_down.is_set():
|
||||
for socketkey, mask in selector.select(timeout=SELECT_TIMEOUT):
|
||||
irc = socketkey.data
|
||||
if mask & selectors.EVENT_READ:
|
||||
irc._run_irc()
|
||||
|
||||
def register(irc):
|
||||
"""
|
||||
Registers a network to the global selectors instance.
|
||||
"""
|
||||
log.debug('selectdriver: registering %s for network %s', irc._socket, irc.name)
|
||||
selector.register(irc._socket, selectors.EVENT_READ, data=irc)
|
||||
|
||||
def unregister(irc):
|
||||
"""
|
||||
Removes a network from the global selectors instance.
|
||||
"""
|
||||
log.debug('selectdriver: de-registering %s for network %s', irc._socket, irc.name)
|
||||
selector.unregister(irc._socket)
|
||||
|
||||
def start():
|
||||
"""
|
||||
Starts a thread to process connections.
|
||||
"""
|
||||
t = threading.Thread(target=_process_conns, name="Selector driver loop")
|
||||
t.start()
|
Loading…
Reference in New Issue
Block a user