fix(devices): fail a running laser delay move when the device is destroyed
This commit is contained in:
1 parent
874845c5f9
commit
700134c495
2 files changed
+38
-5
No files matched your search
@@ -637,15 +637,23 @@ class LaserDelay(PSIDeviceBase, PositionerBase):
|
||||
|
||||
def on_stop(self) -> None:
|
||||
"""Interrupt a running phase ramp between two steps and fail its status."""
|
||||
self._interrupt_move(DeviceStoppedError(f"Device {self.name} has been stopped"))
|
||||
|
||||
def on_destroy(self) -> None:
|
||||
"""Interrupt a running move and fail its status before the base class kills the task.
|
||||
|
||||
The base class shuts the task handler down by raising an exception in the task thread. That
|
||||
can land anywhere in the task, including after the ramp but before the status is resolved,
|
||||
so the status is failed here first; whoever waits on it (a scan, ``umv``) is released.
|
||||
"""
|
||||
self._interrupt_move(DeviceStoppedError(f"Device {self.name} has been destroyed"))
|
||||
|
||||
def _interrupt_move(self, exc: Exception) -> None:
|
||||
with self._move_lock:
|
||||
self._move_stop.set()
|
||||
status = self._move_status
|
||||
if status is not None:
|
||||
self._finish_status(status, DeviceStoppedError(f"Device {self.name} has been stopped"))
|
||||
|
||||
def on_destroy(self) -> None:
|
||||
"""Interrupt a running move; the task handler is shut down by the base class."""
|
||||
self._move_stop.set()
|
||||
self._finish_status(status, exc)
|
||||
|
||||
# ----------------------------------------------------------------------------------------- #
|
||||
# Internals #
|
||||
|
||||
@@ -504,6 +504,31 @@ def test_destroy_ends_a_running_move():
|
||||
assert not status.success
|
||||
|
||||
|
||||
def test_destroy_fails_the_move_status_even_if_the_task_hangs():
|
||||
"""destroy() (config reload, device server shutdown) must release whoever waits on the move,
|
||||
also when the move task is stuck in a slow CA put and the thread kill does not reach it."""
|
||||
with patched_device(LaserDelay, name="laser_delay", step_sleep=0.0) as device:
|
||||
set_hardware(device)
|
||||
device.calibrate(SCOPE)
|
||||
blocker = threading.Event()
|
||||
original_put = device.phase_v1._write_pv.put
|
||||
|
||||
def hung_put(value, *args, **kwargs):
|
||||
blocker.wait(timeout=5)
|
||||
return original_put(value, *args, **kwargs)
|
||||
|
||||
device.phase_v1._write_pv.put = hung_put
|
||||
status = device.set(-4.9)
|
||||
assert wait_until(device._hw_lock.locked)
|
||||
with mock.patch.object(device.task_handler, "shutdown"):
|
||||
device.destroy()
|
||||
assert status.done and not status.success
|
||||
with pytest.raises(DeviceStoppedError, match="destroyed"):
|
||||
status.wait(timeout=0)
|
||||
blocker.set()
|
||||
assert wait_until(lambda: not device._hw_lock.locked())
|
||||
|
||||
|
||||
def test_stored_vs_readback_reference_when_voltages_were_touched(calibrated):
|
||||
calibrated.set(1.0).wait(timeout=10)
|
||||
# somebody turns the phase shifter by hand
|
||||
|
||||
Reference in new issue
Block a user