From 634fe767a9d620c8b83daa4bef436659a8511288 Mon Sep 17 00:00:00 2001 From: Ivo Hanak Date: Tue, 17 Oct 2023 17:09:57 +0200 Subject: [PATCH] Prevented unsubscribing events from a cleared channel --- .gitignore | 1 + epics/ca.py | 14 ++++++++++---- epics/pv.py | 5 +++++ tests/test_ca_clearcache.py | 22 ++++++++++++++++++---- 4 files changed, 34 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index cec08d3f..c8185830 100644 --- a/.gitignore +++ b/.gitignore @@ -12,5 +12,6 @@ dist NonGit venv .vscode +.coverage pyepics.egg-info epics.egg-info diff --git a/epics/ca.py b/epics/ca.py index 7c458879..64bff9cb 100755 --- a/epics/ca.py +++ b/epics/ca.py @@ -494,6 +494,16 @@ def clear_cache(): multiprocessing (and is done internally by CAProcess), but can be useful to fully reset a Channel Access session. """ + # Clear the cache of PVs used by epics.caget()-like functions + # by disconnecting PVs so that all subscriptions are released + # prior clearing channels. + from . import pv + pv_cache = pv._PVcache_ + pv._PVcache_ = {} + for pv in pv_cache.values(): + pv.disconnect() + pv_cache.clear() + # Unregister callbacks (if any) for chid, entry in list(_chid_cache.items()): try: @@ -505,10 +515,6 @@ def clear_cache(): _cache.clear() _chid_cache.clear() - # Clear the cache of PVs used by epics.caget()-like functions - from . import pv - pv._PVcache_ = {} - # The old context is copied directly from the old process # in systems with proper fork() implementations detach_context() diff --git a/epics/pv.py b/epics/pv.py index 129ab5b9..cbc1a3c2 100755 --- a/epics/pv.py +++ b/epics/pv.py @@ -1120,6 +1120,11 @@ def disconnect(self, deepclean=True): it from _PVcache_, so that subsequent connection to this PV will almost always make a completely new connection. + However, this method keeps the EPICS channel ID alive + so that it can be re-used later. This may block some resources. + Use `ca.clear_channel()` to clear the channel ID if needed + only after disconnecting the PV. + Arguments ----------- deepclean, bool removal all cache connection and access-rights callbacks [True] diff --git a/tests/test_ca_clearcache.py b/tests/test_ca_clearcache.py index 02ee0f67..3e38b2a1 100644 --- a/tests/test_ca_clearcache.py +++ b/tests/test_ca_clearcache.py @@ -1,9 +1,8 @@ import epics -from epics import ca +import epics.ca +import epics.pv -import os import time -import sys import subprocess import pvnames enabledpv = pvnames.clear_cache_enabled @@ -24,7 +23,22 @@ def run(): assert value is not None, \ "PV {} is offline".format(beaconpv) time.sleep(0.2) - ca.clear_cache() + epics.ca.clear_cache() + + +def test_use_monitor(): + # This test verifies that clear_cache clears subscriptions created by + # use_monitor=True prior clearing channels. + # Otherwise, EPICS will crash on an attempt to clear subscription + # on a cleared channel. + assert epics.caget(enabledpv, use_monitor=True) is not None, \ + "PV {} is offline".format(enabledpv) + + pv = epics.pv.get_pv(enabledpv) + + epics.ca.clear_cache() + + pv.disconnect() def test_subscribe():