Skip to content

TSan: IncrementalGcStartSweep's test hook reads a block a GC worker is reclaiming #436

Open

Description

@dimensionscape

The TSan job failed on main at 5a9b560 with two data race reports, both in test/regression/IncrementalGcStartSweep:
https://github.com/openfl/libhxcpp/actions/runs/42460790472/job/128669432620 (ubuntu-26.04, image 20261101.186)

The PR runs built on the same commit passed TSan (#425, #429, #434), so it doesn't happen on every run.

Read of size 4 at 0x726c00330618 by main thread:
  #0 __hxcpp_gc_incremental_test(int, hx::Object*)
  #1 Main_obj::main()

Previous write of size 8 at 0x726c00330618 by thread T1:
  #0 memset
  #2 void BlockDataInfo::reclaim<false>(BlockDataStats*)
  #3 GlobalAllocator::ThreadLoop(int)

Location is heap block of size 1576 allocated by main thread:
  #1 GlobalAllocator::AllocMoreBlocks(bool&, bool)

The second report is the same pair the other way round: a write in GlobalAllocator::ThreadLoop to a 1 MB block, after a read of it by __hxcpp_gc_incremental_test on the main thread. T1 is a GC worker that hx::IncrementalDrain started, from an automatic incremental collection that a large allocation set off.

So the test hook on the main thread reads a block's data while a worker is still reclaiming it. The hook is only built with HXCPP_GC_INCREMENTAL_TEST, so this may be the hook reading without waiting for the workers rather than a race a program can hit, but that hasn't been checked. It isn't #430's pattern (LocalAllocator::DoAlloc reading a block AllocMoreBlocks published), which the TSan job no longer reports on ubuntu-26.04.

Next

Check whether __hxcpp_gc_incremental_test waits for the reclaim jobs before it reads block data, and whether anything outside the hook reads blocks the same way.

Activity

  1. dimensionscape commented on Nov 12, 2026

    @dimensionscape
    MemberAuthor

    It failed the same way on main again at c900b35, the same two reports in IncrementalGcStartSweep and nothing else: https://github.com/openfl/libhxcpp/actions/runs/43181689270/job/131058945561

    That's both of the last two runs on main, against none of the four PR runs on the same code (#425, #429, #434, and #426 before its rebase), so it isn't rare.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions