Serialize root operations with operation lock#1259
Conversation
… and ZMQ requests
…ations in operation lock context
…oot-operation-lock
There was a problem hiding this comment.
Pull request overview
This PR introduces a new reentrant Root.operationLock() and applies it across coordinated root-level operations to prevent interleaving between YAML import/export, root read/write, lifecycle reset operations, and ZMQ request handling. This aims to make multi-step tree operations deterministic under concurrency (multiple local threads and/or multiple ZMQ clients).
Changes:
- Added a reentrant root operation lock (
Root.operationLock) and used it to serialize root read/write, lifecycle operations, YAML load/set paths, and remote variable dump. - Centralized YAML helper locking via
Node._operationLock()so device/node YAML operations serialize against the same root lock and fail explicitly if called while detached. - Serialized ZMQ request/reply execution paths (binary and JSON) against the same root lock and added concurrency-focused tests and documentation updates.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
tests/interfaces/test_interfaces_zmq_server.py |
Adds operation-lock aware fakes and tests to ensure ZMQ request paths serialize through a shared lock. |
tests/core/test_core_root_device_io_edges.py |
Adds multi-threading tests verifying reentrancy, cross-thread blocking, lifecycle serialization, and rooted-YAML requirements. |
python/pyrogue/interfaces/_ZmqServer.py |
Wraps request/reply handlers with root.operationLock() to prevent interleaved remote operations. |
python/pyrogue/_Root.py |
Implements operationLock() and applies it to lifecycle ops, root read/write, YAML load/set, and remote variable dump. |
python/pyrogue/_Node.py |
Adds _operationLock() and uses it to serialize YAML export and enforce “must be attached to a Root”. |
python/pyrogue/_Device.py |
Serializes device YAML save/load/set using the root operation lock via _operationLock(). |
docs/src/pyrogue_tree/core/yaml_configuration.rst |
Documents YAML serialization behavior and rooted-tree requirement. |
docs/src/pyrogue_tree/core/root.rst |
Documents the root operation-lock model, scope, and app-author guidance. |
docs/src/pyrogue_tree/core/device.rst |
Documents that device-level YAML helpers are live-tree ops serialized by the root operation lock. |
docs/src/pyrogue_tree/client_interfaces/zmq_server.rst |
Documents ZMQ request serialization behavior and operational tradeoffs. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
This appears to only create exclusivity between bulk read and write commands and the zmq interface. This would not block epics accesses, or other entry points that are not zmq. I can't seem to in my head figure out a clean way to do this that does not impact performance. the only way to do this is an attempted lock at each variable entry point which checks for a root level lock on a "exclusive operation" that includes the bulk configure and status. This lock would be re-entrant. |
Description
Add a reentrant Root operation lock and use it to serialize coordinated root-level operations. This prevents YAML configuration loads, root read/write operations, lifecycle reset operations, and ZMQ request handling from interleaving with each other.
Details
Root.operationLock()and applied it to root read/write, YAML load/set/export paths, remote variable dumps, and root lifecycle operations.NodeErrorbehavior for YAML helpers called before a node is attached to a Root.Validation performed:
pytest tests/interfaces/test_interfaces_zmq_server.py tests/core/test_core_root_device_io_edges.py -qpytest tests/core/test_core_structure_edge_cases.py tests/core/test_core_tree_root.py tests/core/test_device_yaml_configuration.py tests/core/test_yaml_configuration_semantics.py -qgit diff --checkJIRA
https://jira.slac.stanford.edu/browse/ESROGUE-745
Related
Related discussion: #1256