Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 23 additions & 4 deletions src/emc/usr_intf/gmoccapy/gmoccapy.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,15 @@
# construction: a shutdown SIGTERM landing there would otherwise kill
# the process mid-startup with no cleanup. Exit through SystemExit so
# atexit handlers run. The full handler below replaces this one.
# The SystemExit surfaces at whatever statement the constructor is on,
# and a try/except around that statement can swallow it. The flag
# remembers the request, and main() honours it once construction
# returns, so the exit is still the ordinary one.
_terminate_requested = False

def _early_sigterm(signum, frame):
global _terminate_requested
_terminate_requested = True
sys.exit(0)

signal.signal(signal.SIGTERM, _early_sigterm)
Expand Down Expand Up @@ -62,9 +70,8 @@ def _early_sigterm(signum, frame):

# Throws up a dialog with debug info when an error is encountered
def excepthook(exc_type, exc_obj, exc_tb):
# A KeyboardInterrupt reaching the excepthook is a termination request:
# either SIGINT, or SIGTERM surfaced into the main loop by PyGObject's
# signal bridge. Quit cleanly instead of popping a modal error dialog,
# A KeyboardInterrupt reaching the excepthook is a termination request
# (SIGINT). Quit cleanly instead of popping a modal error dialog,
# which would block in a nested loop and leave gmoccapy running until the
# caller escalates to SIGKILL.
if issubclass(exc_type, KeyboardInterrupt):
Expand All @@ -76,6 +83,12 @@ def excepthook(exc_type, exc_obj, exc_tb):
# C boundary, so returning resumes startup. Force the exit.
os._exit(0)
return
# A SystemExit that a try/except in the constructor swallowed can leave
# a half-built object behind, and the error from that must not park
# the process in the modal dialog: the exit was already requested.
if _terminate_requested:
LOG.info("gmoccapy received SIGTERM during startup, shutting down")
sys.exit(0)
try:
w = app.widgets.window1
except Exception:
Expand Down Expand Up @@ -5223,7 +5236,7 @@ def _set_icon_theme(self, name):
pixbuf = icon_theme_helper.load_symbolic_from_icon_theme(self.icon_theme, icon_name, size, default_style)
image.set_from_pixbuf(pixbuf)
image.set_size_request(size, size)
except BaseException as err:
except Exception as err:
LOG.warning(f"Failed to change icon for <{widget_name}> to '{icon_name}': {str(err)}")
failed_icons += 1

Expand Down Expand Up @@ -6587,6 +6600,12 @@ def _make_hal_pins(self):
# instantiate gmoccapy
app = gmoccapy(sys.argv)

# A SIGTERM during construction that a try/except in there swallowed
# would otherwise leave the GUI running.
if _terminate_requested:
LOG.info("gmoccapy received SIGTERM during startup, shutting down")
sys.exit(0)

# get the INI path
inifile = sys.argv[2]

Expand Down
4 changes: 4 additions & 0 deletions src/hal/halmodule.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2391,8 +2391,12 @@ PyMODINIT_FUNC PyInit__hal(void)
return NULL;
}

// A halcmd unload arrives as SIGTERM; deliver it to a component as
// a KeyboardInterrupt. A program that has already set a handler of
// its own keeps it.
PyRun_SimpleString(
"(lambda s=__import__('signal'):"
"s.getsignal(s.SIGTERM) is s.SIG_DFL and "
"s.signal(s.SIGTERM, s.default_int_handler))()");
return m;
}
Loading