@@ -124,20 +124,21 @@ class Bot(object): | |||
if self.watcher: | |||
self.watcher.stop(msg) | |||
def _stop_task_threads(self): | |||
"""Notify the user of which task threads are going to be killed. | |||
Unfortunately, there is no method right now of stopping task threads | |||
safely. This is because there is no way to tell them to stop like the | |||
IRC components can be told; furthermore, they are run as daemons, and | |||
daemon threads automatically stop without calling any __exit__ or | |||
try/finally code when all non-daemon threads stop. They were originally | |||
implemented as regular non-daemon threads, but this meant there was no | |||
way to completely stop the bot if tasks were running, because all other | |||
threads would exit and threading would absorb KeyboardInterrupts. | |||
def _stop_daemon_threads(self): | |||
"""Notify the user of which threads are going to be killed. | |||
Unfortunately, there is no method right now of stopping command and | |||
task threads safely. This is because there is no way to tell them to | |||
stop like the IRC components can be told; furthermore, they are run as | |||
daemons, and daemon threads automatically stop without calling any | |||
__exit__ or try/finally code when all non-daemon threads stop. They | |||
were originally implemented as regular non-daemon threads, but this | |||
meant there was no way to completely stop the bot if tasks were | |||
running, because all other threads would exit and threading would | |||
absorb KeyboardInterrupts. | |||
The advantage of this is that stopping the bot is truly guarenteed to | |||
*stop* the bot, while the disadvantage is that the tasks are given no | |||
*stop* the bot, while the disadvantage is that the threads are given no | |||
advance warning of their forced shutdown. | |||
""" | |||
tasks = [] | |||
@@ -146,7 +147,7 @@ class Bot(object): | |||
if thread.name not in non_tasks and thread.is_alive(): | |||
tasks.append(thread.name) | |||
if tasks: | |||
log = "The following tasks will be killed: {0}" | |||
log = "The following commands or tasks will be killed: {0}" | |||
self.logger.warn(log.format(" ".join(tasks))) | |||
def run(self): | |||
@@ -200,4 +201,4 @@ class Bot(object): | |||
with self.component_lock: | |||
self._stop_irc_components(msg) | |||
self._keep_looping = False | |||
self._stop_task_threads() | |||
self._stop_daemon_threads() |
@@ -67,7 +67,7 @@ class Threads(Command): | |||
threads = threading.enumerate() | |||
normal_threads = [] | |||
task_threads = [] | |||
daemon_threads = [] | |||
for thread in threads: | |||
tname = thread.name | |||
@@ -84,14 +84,15 @@ class Threads(Command): | |||
else: | |||
tname, start_time = re.findall("^(.*?) \((.*?)\)$", tname)[0] | |||
t = "\x0302{0}\x0301 (id {1}, since {2})" | |||
task_threads.append(t.format(tname, thread.ident, start_time)) | |||
daemon_threads.append(t.format(tname, thread.ident, | |||
start_time)) | |||
if task_threads: | |||
msg = "\x02{0}\x0F threads active: {1}, and \x02{2}\x0F task threads: {3}." | |||
if daemon_threads: | |||
msg = "\x02{0}\x0F threads active: {1}, and \x02{2}\x0F command/task threads: {3}." | |||
msg = msg.format(len(threads), ', '.join(normal_threads), | |||
len(task_threads), ', '.join(task_threads)) | |||
len(daemon_threads), ', '.join(daemon_threads)) | |||
else: | |||
msg = "\x02{0}\x0F threads active: {1}, and \x020\x0F task threads." | |||
msg = "\x02{0}\x0F threads active: {1}, and \x020\x0F command/task threads." | |||
msg = msg.format(len(threads), ', '.join(normal_threads)) | |||
self.reply(self.data, msg) | |||
@@ -93,7 +93,7 @@ class IRCConnection(object): | |||
"""Default process hooks for lines received on IRC.""" | |||
self._last_recv = time() | |||
if line[0] == "PING": # If we are pinged, pong back | |||
self.pong(line[1]) | |||
self.pong(line[1][1:]) | |||
@property | |||
def host(self): | |||
@@ -195,9 +195,9 @@ class IRCConnection(object): | |||
def keep_alive(self): | |||
"""Ensure that we stay connected, stopping if the connection breaks.""" | |||
now = time() | |||
if now - self._last_recv > 60: | |||
if now - self._last_recv > 120: | |||
if self._last_ping < self._last_recv: | |||
log = "Last message was received over 60 seconds ago. Pinging." | |||
log = "Last message was received over 120 seconds ago. Pinging." | |||
self.logger.debug(log) | |||
self.ping(self.host) | |||
self._last_ping = now | |||
@@ -170,7 +170,12 @@ class CommandManager(_ResourceManager): | |||
"""Respond to a hook type and a :py:class:`Data` object.""" | |||
for command in self: | |||
if hook in command.hooks and self._wrap_check(command, data): | |||
self._wrap_process(command, data) | |||
thread = Thread(target=self._wrap_process, | |||
args=(command, data)) | |||
start_time = strftime("%b %d %H:%M:%S") | |||
thread.name = "irc:{0} ({1})".format(command.name, start_time) | |||
thread.daemon = True | |||
thread.start() | |||
return | |||