microscope-cockpit / cockpit

Cockpit is a microscope graphical user interface. It is a flexible and easy to extend platform aimed at life scientists using bespoke microscopes.
https://microscope-cockpit.org
GNU General Public License v3.0
37 stars 27 forks source link

Occasional crash on exit in _updater function from LightPower handler. #865

Closed iandobbie closed 1 year ago

iandobbie commented 1 year ago

The LightPower handler (cockpit/handlers/lightPower.py) starts a new thread with the _updater function which updates light power levels with an infinite while loop and a future.ThreadPoolExecutor. Some discussion of this is in issue #715. I think the issue is that the executor calls a getPower callback after the light device is deleted, causing an exception.

iandobbie commented 1 year ago

On the Mac the error produced is

Exception in thread _updater: Traceback (most recent call last): File "/opt/homebrew/Cellar/python@3.11/3.11.4/Frameworks/Python.framework/Versions/3.11/lib/python3.11/threading.py", line 1038, in _bootstrap_inner

And it happend once out of 100 shutdowns, so is pretty rare.

iandobbie commented 1 year ago

This is a duplicate of #726. There is a bnit more info about the exact cause there so leaving this issue open

iandobbie commented 1 year ago

This code is a bit of a nightmare to prevent causing crashes. I suggest either:

1) use the addWatcher in the handler code setup in the light panel that will have no active component but assumes that the power is only changed from within cockpit. Trap the destroy event and remove the watch on close

2) use a wx.Timer on the main interface panel which can be a timer like this a query the device on a regular basis. We will need to trap the destroy event and remove the timer.

Both of these approaches are used elsewhere, thie appraoch currently used here makes shutting it down at exit very tricky.

iandobbie commented 1 year ago

Turns out 1 is already happening, in gui/mainPanels, about line 72 we have

        self.powCtrl.SetValue(lightPower.powerSetPoint *100.0)
        lightPower.addWatch('powerSetPoint', self._SetPowerPercent)

So we are already using addWatch code to trap cockpit generated events so I guess we just need the timer option to get external events.

iandobbie commented 1 year ago

A fix using a wx.timer pushed into master and the _updater thread removed.