Fix exceptions during shutdown

This commit is contained in:
Kovid Goyal 2016-11-29 23:31:56 +05:30
parent 6e003b0901
commit 7cc39404c8
3 changed files with 45 additions and 21 deletions

View File

@ -6,6 +6,7 @@ import argparse
import tempfile import tempfile
import os import os
import sys import sys
from queue import Empty
from gettext import gettext as _ from gettext import gettext as _
@ -73,6 +74,16 @@ def run_app(opts, args):
tabs.render() tabs.render()
window.swap_buffers() window.swap_buffers()
glfw_wait_events() glfw_wait_events()
try:
func, args = tabs.pending_ui_thread_calls.get_nowait()
except Empty:
pass
else:
try:
func(*args)
except Exception:
import traceback
traceback.print_exc()
finally: finally:
tabs.destroy() tabs.destroy()
del window del window

View File

@ -9,11 +9,11 @@ import signal
import struct import struct
from collections import deque from collections import deque
from functools import partial from functools import partial
from threading import Thread from threading import Thread, current_thread
from queue import Queue, Empty from queue import Queue, Empty
from .child import Child from .child import Child
from .constants import viewport_size, shell_path, appname, set_tab_manager, tab_manager, wakeup, cell_size, MODIFIER_KEYS from .constants import viewport_size, shell_path, appname, set_tab_manager, tab_manager, wakeup, cell_size, MODIFIER_KEYS, main_thread
from .fast_data_types import ( from .fast_data_types import (
glViewport, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GLFW_PRESS, glViewport, glBlendFunc, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GLFW_PRESS,
GLFW_REPEAT, GLFW_MOUSE_BUTTON_1, glfw_post_empty_event GLFW_REPEAT, GLFW_MOUSE_BUTTON_1, glfw_post_empty_event
@ -113,6 +113,7 @@ class TabManager(Thread):
self.read_dispatch_map = {self.signal_fd: self.signal_received, self.read_wakeup_fd: self.on_wakeup} self.read_dispatch_map = {self.signal_fd: self.signal_received, self.read_wakeup_fd: self.on_wakeup}
self.all_writers = [] self.all_writers = []
self.timers = Timers() self.timers = Timers()
self.pending_ui_thread_calls = Queue()
self.write_dispatch_map = {} self.write_dispatch_map = {}
set_tab_manager(self) set_tab_manager(self)
cell_size.width, cell_size.height = set_font_family(opts.font_family, opts.font_size) cell_size.width, cell_size.height = set_font_family(opts.font_family, opts.font_size)
@ -144,11 +145,7 @@ class TabManager(Thread):
if data: if data:
signals = struct.unpack('%uB' % len(data), data) signals = struct.unpack('%uB' % len(data), data)
if signal.SIGINT in signals or signal.SIGTERM in signals: if signal.SIGINT in signals or signal.SIGTERM in signals:
self.shutdown()
def shutdown(self):
if not self.shutting_down: if not self.shutting_down:
self.shutting_down = True
self.glfw_window.set_should_close(True) self.glfw_window.set_should_close(True)
glfw_post_empty_event() glfw_post_empty_event()
@ -192,20 +189,18 @@ class TabManager(Thread):
except Exception: except Exception:
pass pass
def queue_ui_action(self, func, *args):
self.pending_ui_thread_calls.put((func, args))
glfw_post_empty_event()
def close_window(self, window): def close_window(self, window):
self.remove_child_fd(window.child_fd) ' Can be called in either thread, will first kill the child (with SIGHUP), then remove the window from the gui '
for tab in self.tabs: if current_thread() is main_thread:
if window in tab: self.queue_action(self.close_window, window)
break
else: else:
return self.remove_child_fd(window.child_fd)
tab.remove_window(window)
window.destroy() window.destroy()
if len(tab) == 0: self.queue_ui_action(self.gui_close_window, window)
self.tabs.remove(tab)
tab.destroy()
if len(self.tabs) == 0:
self.shutdown()
def run(self): def run(self):
if self.args.profile: if self.args.profile:
@ -338,6 +333,21 @@ class TabManager(Thread):
with self.cursor_program: with self.cursor_program:
active.char_grid.render_cursor(rd, self.cursor_program) active.char_grid.render_cursor(rd, self.cursor_program)
def gui_close_window(self, window):
for tab in self.tabs:
if window in tab:
break
else:
return
tab.remove_window(window)
if len(tab) == 0:
self.tabs.remove(tab)
tab.destroy()
if len(self.tabs) == 0:
if not self.shutting_down:
self.glfw_window.set_should_close(True)
glfw_post_empty_event()
def destroy(self): def destroy(self):
# Must be called in the main thread as it manipulates signal handlers # Must be called in the main thread as it manipulates signal handlers
signal.signal(signal.SIGINT, signal.SIG_DFL) signal.signal(signal.SIGINT, signal.SIG_DFL)

View File

@ -61,6 +61,9 @@ class Window:
def destroy(self): def destroy(self):
self.child.hangup() self.child.hangup()
self.child.get_child_status() # Ensure child does not become zombie self.child.get_child_status() # Ensure child does not become zombie
# At this point this window can still render to screen using its
# existing buffers in char_grid. The rest of the cleanup must be
# performed in the GUI thread.
def read_ready(self): def read_ready(self):
if self.read_bytes(self.screen, self.child_fd) is False: if self.read_bytes(self.screen, self.child_fd) is False: