Skip to content

Parallel step races: allocation counters, the stepping thread's index, stage clocks - #103

Merged
nicolas-maman merged 1 commit into
mainfrom
fix/parallel-races
Oct 4, 2026
Merged

nicolas-maman merged 1 commit into
mainfrom
fix/parallel-races

Conversation

@nicolas-maman

Copy link
Copy Markdown
Collaborator

I audited the parallel step for data races and found three real defects, fixed here:

  1. Allocation counters. basics.alloc and free_bytes updated the allocation and byte counters with plain read-modify-writes. The collide pass and finalize allocate from several workers at once: a new mesh contact's triangle cache, an arena's overflow, the continuous pass's sensor hits. Updates were lost, so a multi-worker world's leak count drifted. The counters are atomic now.
  2. The stepping thread's worker index. A world's task contexts, one per worker, are chosen by the thread's worker index. parallel_for ran a one-worker range inline without setting that index. A host stepping a one-worker world from one of its own pool's threads (index k > 0) therefore indexed past the world's single task context and corrupted the heap. A step now runs on the calling thread as worker 0 and gives the thread its index back at the end; parallel_for does the same for its caller.
  3. Stage clocks (AEPHYSICS_STAGES=1). Whichever worker timed a block first allocated them, and the sums weren't atomic. Each worker now has its own row, created on the stepping thread and summed for the report.

test_physics_world's test_parallel_bookkeeping covers the first two:

  • a four-worker world over a mesh frees everything it counted;
  • a step from a thread at worker index 5 completes and restores that index. On main this crashes with an access violation.

Not fixed: the audit also flagged the bullet pass reading another bullet's flags while that bullet sets bits in the same word. The flag being read never changes, and upstream does the same, so it's harmless in practice. It's recorded with the multi-world limits in the issue filed alongside this PR.

All 28 test programs pass locally on aether 0.772.0, with no warnings.

… index, the stage clocks

Found by an audit of the parallel step for data races:

- basics.alloc and free_bytes bumped the allocation and byte counters with
  plain read-modify-writes, and the collide pass and finalize allocate from
  several workers at once (a new mesh contact's triangle cache, an arena's
  overflow, the continuous pass's sensor hits): updates were lost and a
  multi-worker world's leak count drifted. They move atomically now.
- A world's task contexts (one per worker) are chosen by the thread's
  worker index. parallel_for ran a one-worker range inline without setting
  it, so a host stepping a one-worker world from one of its pool's threads
  (index k > 0) indexed past the single context: heap corruption. A step now
  runs on the calling thread as worker 0 and gives the thread its index
  back at the end, and parallel_for does the same for its caller.
- The stage clocks (AEPHYSICS_STAGES=1) were allocated lazily by whichever
  worker timed a block first and summed without atomics. Each worker has
  its own row now, made on the stepping thread and summed for the report.

test_physics_world's test_parallel_bookkeeping: a four-worker world over a
mesh frees everything it counted, and a step from a thread at index 5 runs
and gives the index back (main crashes).
@nicolas-maman
nicolas-maman merged commit 8155974 into main Oct 4, 2026
1 check passed
@nicolas-maman
nicolas-maman deleted the fix/parallel-races branch October 6, 2026 23:09
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