3
0
mirror of https://github.com/jlu5/PyLink.git synced 2024-11-01 01:09:22 +01:00

Split Irc.reply() into _reply() to make 'networks.remote' actually thread-safe

Previously, the Irc.reply_lock check was in the reply() function itself: replacing it with another function checking for the same lock would delay execution,
but then run the wrong reply() code if another module used irc.reply() while 'remote' was executing.
This commit is contained in:
James Lu 2017-03-31 16:25:28 -07:00
parent 40fa4f71bc
commit 9d9b01839c
2 changed files with 33 additions and 22 deletions

View File

@ -62,7 +62,7 @@ class Irc(utils.DeprecatedAttributesObject):
self.connected = threading.Event()
self.aborted = threading.Event()
self.reply_lock = threading.Lock()
self.reply_lock = threading.RLock()
self.pingTimer = None
@ -559,11 +559,12 @@ class Irc(utils.DeprecatedAttributesObject):
# replies across relay.
self.callHooks([source, cmd, {'target': target, 'text': text}])
def reply(self, text, notice=None, source=None, private=None, force_privmsg_in_private=False,
def _reply(self, text, notice=None, source=None, private=None, force_privmsg_in_private=False,
loopback=True):
"""Replies to the last caller in the right context (channel or PM)."""
with self.reply_lock:
"""
Core of the reply() function - replies to the last caller in the right context
(channel or PM).
"""
if private is None:
# Allow using private replies as the default, if no explicit setting was given.
private = conf.conf['bot'].get("prefer_private_replies")
@ -581,6 +582,16 @@ class Irc(utils.DeprecatedAttributesObject):
self.msg(target, text, notice=notice, source=source, loopback=loopback)
def reply(self, *args, **kwargs):
"""
Replies to the last caller in the right context (channel or PM).
This function wraps around _reply() and can be monkey-patched in a thread-safe manner
to temporarily redirect plugin output to another target.
"""
with self.reply_lock:
self._reply(*args, **kwargs)
def error(self, text, **kwargs):
"""Replies with an error to the last caller in the right context (channel or PM)."""
# This is a stub to alias error to reply

View File

@ -103,19 +103,19 @@ def remote(irc, source, args):
del kwargs['source']
irc.reply(text, source=irc.pseudoclient.uid, **kwargs)
old_reply = remoteirc.reply
old_reply = remoteirc._reply
with remoteirc.reply_lock:
try: # Remotely call the command (use the PyLink client as a dummy user).
# Override the remote irc.reply() to send replies HERE.
log.debug('(%s) networks.remote: overriding reply() of IRC object %s', irc.name, netname)
remoteirc.reply = types.MethodType(_remote_reply, remoteirc)
remoteirc._reply = types.MethodType(_remote_reply, remoteirc)
world.services[args.service].call_cmd(remoteirc, remoteirc.pseudoclient.uid,
' '.join(args.command))
finally:
# Restore the original remoteirc.reply()
log.debug('(%s) networks.remote: restoring reply() of IRC object %s', irc.name, netname)
remoteirc.reply = old_reply
remoteirc._reply = old_reply
# Remove the identification override after we finish.
remoteirc.pseudoclient.account = ''