package db import ( "net/netip" "sync" "testing" "github.com/juanfont/headscale/hscontrol/types" "github.com/stretchr/testify/require" ) // TestAllocatorConcurrentNextAndBackfillNoRace exercises the registration path // (Next) concurrently with the backfill allocation path (allocateNext) on // the same allocator. Backfill used to read prev4/prev6 in the caller's frame // without the lock, racing Next's writes; both must now take i.mu. Run with // -race. func TestAllocatorConcurrentNextAndBackfillNoRace(t *testing.T) { p4 := netip.MustParsePrefix("100.64.0.0/10") p6 := netip.MustParsePrefix("fd7a:115c:a1e0::/48") alloc, err := NewIPAllocator(nil, &p4, &p6, types.IPAllocationStrategySequential) require.NoError(t, err) const iterations = 2000 var wg sync.WaitGroup wg.Go(func() { for range iterations { _, _, err := alloc.Next() if err != nil { return } } }) wg.Go(func() { for range iterations { _, err := alloc.allocateNext(&alloc.prev4, alloc.prefix4) if err != nil { return } _, err = alloc.allocateNext(&alloc.prev6, alloc.prefix6) if err != nil { return } } }) wg.Wait() }