Skip to content

macOS: interface enumeration blocks on a CoreWLAN XPC round-trip (get_wifi_transmit_rate) — make Wi-Fi metadata opt-in? #175

Description

@jgeluk

Summary

On macOS, get_interfaces() calls get_wifi_transmit_rate() for any Wireless80211 interface (src/os/macos/interface.rs). That function's CWWiFiClient::interfaceWithName is a synchronous XPC round-trip to airportd, and on busy hosts we measure it at ~0.3–0.5 s per process — it dominates enumeration cost. It also fires when Wi-Fi is powered off (the interface still enumerates as Wireless80211).

Caught via sample(1) on a process stuck in enumeration:

Thread ... DispatchQueue: com.apple.wifi.xpcclient.mutex  (serial)
  netdev::os::macos::wifi::get_wifi_transmit_rate
    objc2_core_wlan::CWWiFiClient::interfaceWithName
      -[CWWiFiClient interfaceWithName:]  (CoreWLAN)
        -[CWInterface(Private) initWithInterfaceName:xpcClient:legacy:]

Why it hurts

Downstream consumers treat get_interfaces() as cheap. netwatch (the iroh project's interface monitor) enumerates at endpoint bind and re-enumerates on every network event — on a host with virtual bridges (Docker/VM) that's continuous, so long-lived daemons pay the XPC repeatedly and short-lived processes (e.g. one process per test) pay it at every startup. In our case the value was never even consumed: transmit_speed was discarded by the entire downstream stack.

Measured impact when we no-op the call in a fork: a process that binds a QUIC endpoint at startup went from ~0.6 s to ~0.2 s; a 154-test suite (process-per-test) from 13–17 s to 5.7 s.

Proposal

Make Wi-Fi metadata collection opt-in, any of:

  1. A cargo feature (like the existing gateway feature) gating the CoreWLAN query, default off — enumeration stays pure syscalls.
  2. A runtime API: get_interfaces_with(Options { wifi_metadata: bool, .. }), with get_interfaces() defaulting to off.
  3. At minimum: skip the query when the interface is down/powered off, and document that get_interfaces() performs blocking IPC on macOS.

Happy to send a PR for whichever direction you prefer — we currently carry a small fork that sets transmit_speed = None on macOS (branch) and would rather converge upstream.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions