Description
SyncTable.Grow publishes the expanded table with Volatile.Write, and the sync index is subsequently published in the object header with Interlocked.CompareExchange.
The lock-free readers use ordinary loads for both the header and s_entries. On ARM64, the independent table load can execute before the header load.
Reproduction Steps
Source-level interleaving; no ARM64 runtime repro yet:
- A reader speculatively loads the current 128-entry
s_entries.
- Another thread grows the table to 256 entries, initializes entry 128, and publishes index 128 in an object's header.
- The reader observes the new index while still using the old table snapshot.
Expected behavior
Observing a published sync index also makes the preceding table expansion and entry initialization visible.
Actual behavior
A reader can combine a newly published sync index with an older table snapshot that does not contain that entry.
Configuration
NativeAOT on ARM64; source inspected at 6f1d933.
Other information
Relevant code: object-header read, SyncTable lookup.
Using acquire reads for the object headers on lock-free lookup paths would provide the required ordering. Making only the s_entries load volatile does not order the preceding header read.
Minimal fix: put read barrier here.
Description
SyncTable.Growpublishes the expanded table withVolatile.Write, and the sync index is subsequently published in the object header withInterlocked.CompareExchange.The lock-free readers use ordinary loads for both the header and
s_entries. On ARM64, the independent table load can execute before the header load.Reproduction Steps
Source-level interleaving; no ARM64 runtime repro yet:
s_entries.Expected behavior
Observing a published sync index also makes the preceding table expansion and entry initialization visible.
Actual behavior
A reader can combine a newly published sync index with an older table snapshot that does not contain that entry.
Configuration
NativeAOT on ARM64; source inspected at
6f1d933.Other information
Relevant code: object-header read, SyncTable lookup.
Using acquire reads for the object headers on lock-free lookup paths would provide the required ordering. Making only the
s_entriesload volatile does not order the preceding header read.Minimal fix: put read barrier here.