Skip to content

Cross-platform stability fixes and FP-contract standardization#283

Open
Madreag wants to merge 13 commits into
cortex-command-community:developmentfrom
Madreag:pr/cross-platform-stability
Open

Cross-platform stability fixes and FP-contract standardization#283
Madreag wants to merge 13 commits into
cortex-command-community:developmentfrom
Madreag:pr/cross-platform-stability

Conversation

@Madreag

@Madreag Madreag commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

These are cross platform stability fixes that came up while bringing the determinism work over to Mac and Linux. Most are small correctness issues that Windows happened to hide (its heap fill pattern and thread scheduling masked a few), so they only showed up once the same code ran on the other two platforms. see commit messages.

  • AtomGroup collision iteration now runs in MOID order (the two thread local hitMOAtoms maps go from unordered_map to map; MOIgnoreMap is left alone since it's lookup only and never iterated). Plus a guard against a 0/0 step ratio when every atom in a segment rasterizes to zero steps, which left a dead NaN that still tripped FE_INVALID.

  • HumanBehaviors weapon and tool pickup sort gets a deviceId tie break, since the async pickup callbacks fire in scheduler dependent order and equal scores need a stable total order.

  • FP contract pinned across toolchains: -ffp-contract=off on GCC/Clang and FloatingPointModel=Precise on every MSVC config, so the compiler can't fold FMA or reassociate floats differently per platform. A follow on fixes a Meson bug where the release branch reassigned extra_args and silently dropped those flags on every release TU (checked in compile_commands.json: 203 TUs now, was 0).

  • LuaAdapters async callback id counter is now a std::atomic<int>, closing a race under parallel AI.

  • Two uninitialized reads off the pooled allocator, both returning prior occupant bytes so the value tracked allocation order: Atom's integer step state, and MovableMan::m_SimUpdateFrameNumber. Caught with Valgrind/ASan.

  • An AudioMan use after free: an MO gibbed mid tick frees its SoundContainer, but its still playing FMOD channels keep the freed address in userData. The channels now get disowned on destroy (they keep playing) and the readers skip a container that's gone.

  • A few Mac only fixes: message boxes raised from a worker thread now log to stderr instead of popping a dialog (a cross thread Cocoa alert deadlocks), an explicit file_time_type to system_clock conversion in the save menu, and the libc++ removed-API macros that luabind 0.7.1 and boost 1.75 still need to build on libc++ 19+.

  • a one line guard in Tracy (TracyProfiler.cpp) that clamps broadcastMsg.activeTime to 0 when the timestamp predates the profiler epoch, otherwise it trips Tracy's own assert on startup.

Built and ran clean on Windows, Linux, and macOS arm64. A few of these (the iteration order, FP contract, and uninitialized reads) are prerequisites for the determinism series this is part of, but they all stand on their own as fixes.

Madreag added 13 commits June 20, 2026 15:28
Swap the per-tick hitMOAtoms thread-locals from unordered_map to std::map so
impulse accumulation iterates MOID-ascending, removing a source of cross-run
bit-noise in the physics step.
Add deviceId as a secondary sort key in WeaponSearch and ToolSearch so the
pickup ordering is stable when scores collide; scheduler-dependent async
callback order otherwise leaves the table order non-deterministic.
On macOS, route RTEError message/abort/assert boxes off worker threads to
stderr: Cocoa dispatches the dialog onto the main thread, which deadlocks when
that thread is blocked waiting on the worker. Windows/Linux tolerate
cross-thread boxes and keep their behaviour.

Guard the save-list file_clock -> system_clock conversion on macOS libc++,
whose __int128 file_clock rep is rejected when constructing the time_point
directly; an explicit duration_cast lands it in range.
CalculatePathAsync runs on the parallel ThreadedUpdateAI workers (AI scripts
call Scene:CalculatePathAsync from HumanBehaviors); the static-int increment
could hand two concurrent requests the same id and silently drop one Lua
callback slot. Make the counter std::atomic with relaxed fetch_add.
Pin floating-point so same-arch Win/Linux/macOS builds produce bit-identical
results. meson: -ffp-contract=off plus the no-fast-math family for GCC/Clang,
/fp:precise + /arch:SSE2 for MSVC. RTEA.vcxproj: FloatingPointModel=Precise on
every config (was Fast). Document the FPU + libm path in Source/CI.
The release branch reassigned extra_args to ['-w'], discarding -ffp-contract=off
and the rest of the cross-platform FP block. Append instead of overwrite.
Atom::Clear left m_IntPos and the Bresenham step fields uninitialized.
SetupPos branches on m_IntPos, and StepForward steps on the Bresenham
state, before SetupSeg runs for a freshly-pooled Atom, so the stale pool
value (which varies with allocation order) made collision stepping
nondeterministic run to run. This was the dominant source of the
cross-platform determinism divergence on the combat scenarios.
The sim-frame counter was incremented each tick but never initialized,
so HitWhatMOID and HitWhatTerrMaterial read an uninitialized value when
comparing a per-MO collision frame against it.
A SoundContainer's FMOD channels keep its address in userData, so the positional and channel-ended passes read freed memory once the container is destroyed. Null the back-reference on destroy and skip channels whose container is gone.
When every atom rasterizes to zero steps, stepsOnSeg is 0 and the step ratio divided 0/0. Use 0 in that case.
broadcastMsg.activeTime goes negative when the broadcast timestamp predates
m_epoch, tripping Tracy's own assert(activeTime >= 0) on startup. Clamp to 0.
Vendored luabind 0.7.1 and boost 1.75 still use the C++17-removed APIs
(auto_ptr, unary/binary_function, binders) that libc++ 19+ hides behind these
macros, so the macOS build fails to compile without them.
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.

1 participant