Skip to content

Make the atom id counter atomic and the parameters singleton safe - #177

Merged
antonio-leblanc merged 1 commit into
devfrom
fix/atomic-id-and-singleton
Aug 12, 2026
Merged

Make the atom id counter atomic and the parameters singleton safe#177
antonio-leblanc merged 1 commit into
devfrom
fix/atomic-id-and-singleton

Conversation

@HugoFara

Copy link
Copy Markdown
Collaborator

Two pieces of shared state from the table in #175, both fixed without adding any runtime machinery. Eight lines of substance.

The id counter

Every ForeFireAtom takes its id from instanceNRCount++ (ForeFireAtom.h:106), a plain long. Concurrent construction can hand the same id to two objects. It becomes std::atomic<long> with fetch_add(1, std::memory_order_relaxed) — relaxed because ids only have to be distinct, not ordered against anything else, so this compiles to the same lock xadd a compiler would emit anyway and costs nothing measurable on the single-threaded path.

The parameters singleton

GetInstance was the textbook unsafe lazy singleton:

if ( instance == 0 ) instance = new SimulationParameters;

Two threads can both see null, both construct, and walk away with different parameter objects. It becomes a function-local static, whose once-only initialisation C++11 already guarantees:

static SimulationParameters* const instance = new SimulationParameters();

Still never deleted, matching what it did before — the parameters live for the whole process. The now-unused private instance member goes with it.

What this does not do

It does not make ForeFire safe to use from several threads. I measured that: with this change and the other small fixes applied, the eight-thread stress test in #176 still crashes on all five runs. Safety needs the state refactor described as step 5 of #175, which is a real API change and is not proposed here.

What this does is remove two anti-patterns that are wrong on their own terms. The unsafe lazy singleton is a known bug shape whether or not anyone threads this code, and a shared mutable counter behind an ++ is the same. Neither change adds a lock, a flag, or a branch to any hot path.

Verification

  • Unit tests: 4/4 suites pass.
  • runff: KML and NetCDF both match the references within tolerance. Ids are still allocated in the same order single-threaded, so the outputs are unchanged.
  • Build is clean of new warnings.

Related: #175 (the shared-state issue), #176 (the stress test that measures it).


This pull request, including its code changes and this description, was generated by Claude Opus 5, and reviewed manually before submitting.

…on safe
Two races that the GIL currently hides.
ForeFireAtom::instanceNRCount was a plain long incremented with ++ to give
every atom its id. Concurrent construction could hand the same id to two
objects. It is now std::atomic<long> with a relaxed fetch_add, which is all
the ordering distinct ids need.
SimulationParameters::GetInstance used the textbook unsafe lazy singleton,
so two threads could both see null and both construct, then diverge with
different parameter objects. It is now a function-local static, whose
initialisation C++11 guarantees runs once. The unused private member is
gone.
@antonio-leblanc
antonio-leblanc merged commit 478f232 into devAug 12, 2026
3 checks passed
@antonio-leblanc
antonio-leblanc deleted the fix/atomic-id-and-singleton branch August 13, 2026 02:35
Sign up for freeto 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

@HugoFara@antonio-leblanc