Skip to content

Commit

Permalink
bot: keep track of trigger processing threads
Browse files Browse the repository at this point in the history
  • Loading branch information
Exirel committed Nov 20, 2019
1 parent 55d5835 commit e3135b7
Showing 1 changed file with 44 additions and 5 deletions.
49 changes: 44 additions & 5 deletions sopel/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ def __init__(self, config, daemon=False):
super(Sopel, self).__init__(config)
self._daemon = daemon # Used for iPython. TODO something saner here
self.wantsrestart = False
self._running_triggers = []
self._running_triggers_lock = threading.Lock()

# `re.compile('.*') is re.compile('.*')` because of caching, so we need
# to associate a list with each regex, since they are unexpectedly
Expand Down Expand Up @@ -650,6 +652,8 @@ def dispatch(self, pretrigger):
:meth:`~get_triggered_callables`. This method is also responsible
for telling ``dispatch`` if the function is blocked or not.
"""
# list of commands running in separate threads for this dispatch
running_triggers = []
# nickname/hostname blocking
nick_blocked = host_blocked = self._is_pretrigger_blocked(pretrigger)
blocked = bool(nick_blocked or host_blocked)
Expand All @@ -659,38 +663,73 @@ def dispatch(self, pretrigger):
for priority in ('high', 'medium', 'low'):
items = self.get_triggered_callables(priority, pretrigger, blocked)
for func, trigger, is_blocked in items:
function_name = "%s.%s" % (func.__module__, func.__name__)
# skip running blocked functions, but track them for logging
if is_blocked:
function_name = "%s.%s" % (
func.__module__, func.__name__
)
list_of_blocked_functions.append(function_name)
continue

# call triggered function
wrapper = SopelWrapper(
self, trigger, output_prefix=func.output_prefix)
if func.thread:
# run in a separate thread
targs = (func, wrapper, trigger)
t = threading.Thread(target=self.call, args=targs)
t.name = '%s-%s' % (t.name, function_name)
t.start()
running_triggers.append(t)
else:
# direct call
self.call(func, wrapper, trigger)

# log blocked functions
if list_of_blocked_functions:
if nick_blocked and host_blocked:
block_type = 'both'
elif nick_blocked:
block_type = 'nick'
else:
block_type = 'host'
LOGGER.info(
"[%s]%s prevented from using %s.",
LOGGER.debug(
"[%s] %s prevented from using %s.",
block_type,
pretrigger.nick,
', '.join(list_of_blocked_functions)
)

# update currently running triggers
self._update_running_triggers(running_triggers)

@property
def running_triggers(self):
"""Current active threads for triggers.
This read-only list contains the currently running thread processing
a trigger. This is for testing and debugging purpose only.
"""
with self._running_triggers_lock:
return [t for t in self._running_triggers if t.is_alive()]

def _update_running_triggers(self, running_triggers):
"""Update list of running triggers.
:param list running_triggers: new started threads
We want to keep track of running triggers, mostly for testing and
debugging purpose. For instance, it'll help make sure, in test, that
a bot's plugin finished processing of a trigger, by manually joining
all running threads.
This is kept private, as this is pure internal machinery and isn't made
to be manipulated by an outside code.
"""
# update bot's global running triggers
with self._running_triggers_lock:
running_triggers = running_triggers + self._running_triggers
self._running_triggers = [
t for t in running_triggers if t.is_alive()]

def on_scheduler_error(self, scheduler, exc):
"""Called when the Job Scheduler fails.
Expand Down

0 comments on commit e3135b7

Please sign in to comment.