Skip to content

fix(server): macOS UDP search port sharing + init handlers without data#5

Merged
MatInGit merged 1 commit intoISISNeutronMuon:mainfrom
physwkim:fix/udp-reuseport-and-init-no-data
Apr 16, 2026
Merged

fix(server): macOS UDP search port sharing + init handlers without data#5
MatInGit merged 1 commit intoISISNeutronMuon:mainfrom
physwkim:fix/udp-reuseport-and-init-no-data

Conversation

@physwkim
Copy link
Copy Markdown
Contributor

Context

When running a spvirit-server IOC alongside other PVAccess consumers on the same macOS host (e.g. p4p-based PyDM image viewers, pvmonitor), two issues prevent the stack from working end-to-end:

  1. UDP port 5076 cannot be shared. Only the first process to bind wins; all subsequent clients fail with EADDRINUSE and the PVA plugin never loads.
  2. MONITOR on a PV that has no data yet is rejected. Strict clients like p4p tear the subscription down on the initial "PV not found" error and never recover, even after data eventually arrives. The most common trigger is an NTNDArray PV before the camera starts acquiring.

Root cause:

  1. handler.rs::run_udp_search called UdpSocket::bind(addr) directly. No SO_REUSEADDR / SO_REUSEPORT. On macOS/BSD (and Linux ≥ 3.9) live multi-bind on the same UDP port requires SO_REUSEPORT on every participant — if the server doesn't set it, later clients cannot join. The CLI binaries spserver and spdodeca had the same duplicated code path.
  2. The init branches for MONITOR (command 13), PUT (11), and PUT_GET (12) all went through get_nt_snapshot and returned encode_op_error("PV not found", ...) when the snapshot was None. PVA semantics for init only require the type descriptor, not a live value — lenient clients (pvmonitor-rs) tolerate the error and keep waiting, but p4p treats the init error as fatal. Upstream already fixed this for GET init in 3918bb1 but did not propagate the same pattern to the other init paths.

Changes

1. UDP search port: shareable bind

  • Add pub fn bind_udp_search_socket(addr) in spvirit-server/src/handler.rs. Uses socket2 to set SO_REUSEADDR + SO_REUSEPORT (Unix) and converts back to a tokio::net::UdpSocket.
  • run_udp_search (library and both CLI binaries) now goes through this helper.
  • beacon.rs not touched — it binds :0 (ephemeral, send-only), so there is no port to share.

2. Init handlers: tolerate data-less PVs

  • MONITOR / PUT / PUT_GET init now try PvStore::get_descriptor first, fall back to deriving the descriptor from get_nt_snapshot, and only return "PV not found" when both are None. Mirrors the existing GET init logic from 3918bb1.
  • Behaviour for GET data (non-init), PUT data, and monitor start/destroy paths is unchanged — those genuinely need a value.

Two related server fixes for running an IOC co-located with other PVA
clients (notably p4p) on macOS:

1. UDP search port: add bind_udp_search_socket() helper that uses
   socket2 to set SO_REUSEADDR + SO_REUSEPORT (Unix) before binding.
   Without SO_REUSEPORT, macOS rejects a second process trying to bind
   the well-known search port (5076), preventing p4p / pvmonitor from
   starting on the same host as the spvirit IOC. Linux has the same
   requirement for live multi-bind (>= 3.9). handler.rs and the
   spserver / spdodeca CLI binaries now share the same helper.

2. MONITOR / PUT / PUT_GET init: previously these init paths returned
   "PV not found" whenever get_nt_snapshot returned None, which strict
   clients like p4p treat as fatal for the whole subscription /
   transaction. Following the pattern already applied to GET init,
   these handlers now try PvStore::get_descriptor first so clients can
   initialise against PVs that have no data yet (e.g. NTNDArray before
   the first acquire). "PV not found" is only returned when neither a
   descriptor nor a snapshot is available.
@MatInGit MatInGit merged commit 5af368e into ISISNeutronMuon:main Apr 16, 2026
0 of 2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants