macos dev compat#1
Draft
jonathan-reichardt wants to merge 13 commits into
Draft
Conversation
Replace inline sched_setscheduler/sched_setaffinity/clock_nanosleep calls with calls into the new oals::rt namespace from OpenAudioNetwork/netutils/platform/rt.h. Delete the local set_thread_realtime/set_running_cpu helpers and the #include <linux/sched.h> in both files. No behavior change on Linux: same SCHED_FIFO priorities (engine 25/20/80, io_sim 50), same SCHED_RR (99, io_sim main), same CPU affinities (engine 1/1/2/3), same CLOCK_MONOTONIC relative sleeps (100 ns + 10000 ns in engine, ~667 µs in io_sim). Plus bumped OpenAudioNetwork submodule pointer to the matching M1 commit that introduces the shim and the NOT EMBEDDED_BUILD gate. After M1+M2 the engine and io_sim will compile on macOS; linking still depends on M3's transport seam (the four extern "C" hooks that LowLatSocket's #else arm currently references but doesn't define on non-Linux hosts). See Docs/dev-tooling-plan.md M1.
CMake plumbing + source hygiene so the OALS tree configures and
compiles on macOS up to the M3-scope link gap. Linking still depends
on M3's transport seam (__send_data / __recv_data / __fetch_iface_meta
and NetworkMapper::get_mac_by_uid remain undefined on non-Linux hosts).
Top-level CMakeLists.txt:
- Gate the aarch64 / -ftree-vectorize block with AND NOT APPLE so it
no longer fires on Apple Silicon (which reports as aarch64 on some
toolchains, arm64 on others; either way Apple Clang auto-vectorizes
at -O2+, so the flag is redundant). Linux ARM (Pi, embedded) still
picks it up.
- Add option(OAN_HOST_BACKENDS "Build host dev transports + RT shim"
OFF), force ON via CACHE BOOL ... FORCE when APPLE so subdirectory
scopes see the override (a plain set() shadows but does not update
the cache), propagate as add_compile_definitions. No code consumes
the macro yet — it is plumbing for M3 transport sources.
io_sim ALSA dead-code purge:
The dev-tooling plan claimed io_sim's ALSA use was "already entirely
commented out." That was wrong — only the call and the playback_thread
consuming it were commented; the snd_pcm_t* alsa_setup() function
body and #include <alsa/asoundlib.h> were live. Decision was to
delete outright rather than wrap in #ifdef __linux__: io_sim is
marked deprecated in CLAUDE.md, the VSC track supersedes its playback
path, and ALSA cannot exist on macOS. Removes -107 lines from
io_sim/main.cpp plus the ALSA pkg_check_modules and link in
io_sim/CMakeLists.txt. SNDFILE stays REQUIRED (used live by
gen_packet_strm_from_file for stem playback; available on macOS via
brew install libsndfile).
SNDFILE include-dir propagation:
io_sim and debugger now pass ${SNDFILE_INCLUDE_DIRS} into
target_include_directories. Linux happened to work because
/usr/include is searched by default; Homebrew installs to
/opt/homebrew/include which is not.
Qt include de-Debianisation in plugins/loader/PipeDesc.h:
#include <qt6/QtGui/QPainter> etc. was Debian/Ubuntu-specific
(their Qt6 headers live under /usr/include/qt6/). Standard Qt6
CMake config — used by Homebrew, Arch, Fedora, Alpine, and upstream
Qt — adds the include dir directly and expects the canonical
<QtGui/QPainter> form. Linux Debian/Ubuntu users keep working because
Qt6::Widgets is already linked everywhere these headers are used.
Two missing #include <iostream>:
engine/NetMan.cpp and debugger/DebuggerWindow.cpp use std::cerr /
std::cout but did not include <iostream>. Previously transitive via
other headers; the Mac build path no longer pulls it in. Direct
include is correct hygiene.
Plus .gitignore for build/ dirs.
Verified on macOS: cmake configure + cmake --build produces 72 .cpp.o
files with 0 compile errors. Only failures are the documented M3-scope
undefined symbols at the oannetutils.dylib link step.
See Docs/dev-tooling-plan.md M2.
Bump OpenAudioNetwork submodule to the M3 commit that introduces the
ITransport seam, so OALSEngine, io_simulator, OALSCoreUI, debugger,
and the builtin plugins all link cleanly on macOS for the first time
(M2 only got them to compile; oannetutils.dylib was failing to link
with four undefined symbols — __send_data, __recv_data,
__fetch_iface_meta, NetworkMapper::get_mac_by_uid). After M3 the
SimTransport stub is reachable: ./OALSEngine sim:default routes
through it and exits cleanly with a "not yet implemented (M4)"
message. Plain ifname on Mac fails loudly with a clear "this host
requires a transport prefix" message.
Plus a CMake fix the M3 link surfaced: target_link_directories with
${SNDFILE_LIBRARY_DIRS} on both io_simulator and debugger. M2 added
${SNDFILE_INCLUDE_DIRS} for the compile step but missed the parallel
${SNDFILE_LIBRARY_DIRS} for the link step. On Linux this happened to
work because /usr/lib is searched by default; on Mac, Homebrew's
/opt/homebrew/lib is not. Without this, `ld: library 'sndfile' not
found` blocks io_simulator and debugger from linking. M2 didn't
surface this because the M2 link gap stopped earlier in the chain.
See OpenAudioLiveSystem/Docs/dev-tooling-plan.md M3 and the OAN
submodule's M3 commit (61ed291) for the transport seam details.
New tools/sim_switch/ — single-threaded poll() user-space switch daemon emulating a switched L2 segment over AF_UNIX. Pure POSIX, builds on Linux + macOS. Live TUI with traffic rates, device discovery decode, per-conn drop counters; --headless mode for CI. 7-scenario GTest smoke suite (hello, broadcast, unicast, unknown-unicast, bad magic, slow client, SimTransport interop). Submodule bump pulls in OAN's real SimTransport implementation. Wire framing (sim_proto.h) is owned here, not in OAN — OAN is vendored into firmware repos and must stay free of host-dev-only contracts. SimTransport keeps a byte-identical local copy. Also in this commit (M4 verification follow-ups discovered on macOS): - ENGINE_PLUGIN_SYSLOCATION is now $HOME/.osst/core_plugins on APPLE; the Linux /core_plugins path can't be created on macOS's sealed root volume. Linux target unchanged. - io_simulator now respects argv[1] as the interface string, matching OALSEngine. Previously hardcoded to 'virbr0'. - coreui/surface_config/surface.json uses sim:default as the default eth_interface so the UI works out of the box on macOS. Linux dev loops can override per-host. - Top-level enable_testing() so ctest discovers sim_switch_smoke reliably across CMake versions.
io_sim's wav paths were hardcoded to a developer's home dir, so it
exited on every other machine. Replace with a JSON config (argv[2],
default ./io_sim.json) that lists tracks as either {channel, path} or
{channel, tone: {freq, gain}}.
- nlohmann/json pulled in via FetchContent (header-only, dev-only tool
so the dep is acceptable; coreui still uses QJsonDocument).
- Tilde-expand paths so ~/Music/... works directly.
- Reject non-96k wavs with the exact ffmpeg command needed to convert.
- Tone-gen tracks for "is the wire alive?" testing without wavs.
- Loops normalized to shortest stream so the cursor wraps cleanly.
- io_sim.example.json: 4-channel tone demo (440/880/1k/1.5k Hz).
Submodule bump pulls in the NetworkMapper disco/age-loop throttle on
host backends.
sim_switch:
- SimFrame v2: switch populates src_uid, so observers see who sent.
- SimHello v2: flags field with SIM_HELLO_PROMISCUOUS for inspectors
that want every fanout regardless of ethertype / dst_uid.
- Per-peer tx/rx stat counters per ethertype, attributed during
consume_frame and try_write. Pruned alongside disco entries.
- TUI rates smoothed via EWMA (α=0.15, ~1s window) so disco's bursty
5-sec interval no longer makes the dashboard oscillate between
20/s and 0/s.
- New Peers table sorted by uid with A/D/C/S tx/rx columns — actual
data-flow visibility for installations with more than 2 nodes.
- "Inspectors: N attached" line.
oaninspect (new tool):
- Connects as a promiscuous client, ANSI-coloured per-EtherType
pretty-print of every frame.
- Filter expression with key=value[,value,...] grammar (ethertype/
src/dst/peer/uid). Default suppresses audio.
- Audio fast-path: when filter excludes audio, skip at ingest
(4000 frames/s) so the 100k ring holds plenty of disco/sync/
control history — audio still counted for the suppression hint.
- Pause stops ring ingest (with pending counter), so the frozen
view stays frozen even at high wire rates. Filter changes
re-filter the existing ring so they work while paused.
- Diff-render: only redraws rows whose content changed, no
full-screen clear flicker.
- Interactive: Space pause, j/k scroll, Ctrl-D/U half-page,
g/G top/end, / re-filter, h hex, q quit. Cheat-sheet footer.
- Non-TTY fallback: prints plain decoded lines to stdout for CI.
- --pcap record / --replay (own binary format).
Tests:
- +7 switch scenarios (promiscuous receives broadcasts +
unicasts to others, src_uid populated by switch, multi-
inspector, no loopback, promiscuous not in route table,
v1 hello rejected).
- 6 Filter parser unit tests.
- 3 EWMA unit tests.
- 23/23 passing.
OAN submodule bump pulls in the matching SimFrame v2 / SimHello v2
on the engine + io_sim + UI side.
OALSEngine idle on macOS was burning ~150 % CPU (and ~350 % with audio
flowing) because three loops weren't actually paced:
- main thread: while(true) NetMan::update_netman() — the function
body is empty. Pure busy spin, one full core.
- pipe_updater: precise_sleep_ns(100) between update_processes()
walks of all 64 disabled pipes. nanosleep floor on
macOS is ~3 µs, so it ticked at ~300 kHz.
- clock_syncer: ClockMaster::sync_process() does an async recv that
returns 0 on EAGAIN; the 10 µs precise_sleep_ns
between calls couldn't keep up.
io_sim's clock_thread had the same async-recv spin with no sleep at all.
The four real RT recv threads (audiopoll, controlpoll, packet_receiver)
were already cold — they call receive_data(async=false) which blocks in
the kernel correctly via SimTransport::recv's existing poll(-1).
All four fixes are gated by OAN_HOST_BACKENDS so the Linux RT path is
byte-identical. AudioEngine gains a CV pair (notify_block_ready /
wait_for_block); feed_pipe signals it from the audio recv callback so
pipe_updater can block instead of ticking. NetMan gains
clock_wait_or_tick(timeout_ms) that drains the sync poll-spin via
ClockMaster::wait_sync_readable before running the 1 s heartbeat.
Main thread parks on pause(). io_sim's clock_thread does the same
wait_sync_readable wrap.
Measured on macOS, OALSEngine:
- idle (no io_sim): 151 % → 0.9 %
- with io_sim audio flowing: ~350 % → 6.0 %
Audio throughput preserved (~3960 audio frames/s on the switch). All
23 sim_switch tests still green.
OALSEngine and io_simulator now print a real usage block on --help instead of trying to parse the flag as an interface name. Both explain the sim:<name> transport spec inline so contributors don't need to dig in CLAUDE.md. scripts/dev-up.sh launches the 3-process stack (sim_switch + OALSEngine + io_simulator, optionally + oaninspect) in a tmux session. Idempotent: --down kills the session and clears the socket; re-running cleans up stale processes first. Per-pane logs tee'd to /tmp/osst-dev-<role>.log. OAN bump pulls in the M7 Mac RT shim (THREAD_TIME_CONSTRAINT_POLICY + mlockall). No engine call site change — set_thread_realtime now actually applies a budget on Mac instead of being a no-op.
CI: GitHub Actions runs on every push and PR to any branch, on ubuntu-latest + macos-latest. Installs cmake, ninja, qt6, libsndfile, gtest from package managers, then configures with OAN_HOST_BACKENDS=ON, builds, and runs ctest. clang-format: LLVM-based, K&R braces, 100-col limit, left-aligned references (auto& is the codebase majority). Editor-on-save only — no mass reformat. pre-commit: clang-format + trailing-whitespace + EOF fixer + merge- conflict + large-file guard. Excludes the OAN submodule (it has its own config). Install via 'pip install pre-commit && pre-commit install'.
Linux ld is strict about undefined references in shared libs; oannetutils' LowLatSocket pulls in NetworkMapper::get_mac_by_uid which lives in oancommon. macOS' -undefined,dynamic_lookup link option hid this until CI tried a real Linux build.
Real root cause of the Linux link failure: GNU ld defaults to --as-needed since binutils 2.31. sim_switch_test's own code doesn't reference any oancommon symbol directly, so the linker would drop liboancommon.so from the link line — and the transitive reference from oannetutils' LowLatSocket::get_mac to NetworkMapper::get_mac_by_uid stayed unresolved. Wrap both OAN .so's in --no-as-needed/--as-needed on Linux so both are pinned in. macOS' ld64 has no --as-needed concept; plain list is fine there. Previous attempts (--unresolved-symbols=ignore-in-shared-libs, then -z undefs at the .so level) only addressed building the .so itself, not the executable link step. OAN submodule bump: just a comment refresh in netutils/CMakeLists.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.