Skip to content

math/big: data race on cacheBase10 #4218

@dvyukov

Description

@dvyukov
14574:2c2052f38c3c tip

Add the following test to math/big tests:

func TestScanPiParallel(t *testing.T) {
    c := make(chan bool)
    for i := 0; i < 2; i++ {
        go func() {
            TestScanPi(t)
            c <- true
        }()
    }
    for i := 0; i < 2; i++ {
        <-c
    }
}

ThreadSanitizer says:

WARNING: DATA RACE at 0x000000ac6ab0
Read by goroutine 4:
  math/big.divisors()
      src/pkg/math/big/nat.go:956 +0x1aa
  math/big.nat.string()
      src/pkg/math/big/nat.go:807 +0x84d
  math/big.nat.decimalString()
      src/pkg/math/big/nat.go:731 +0x7e
  math/big.TestScanPi()
      src/pkg/math/big/nat_test.go:407 +0x1d6
  math/big.func·006()
      src/pkg/math/big/nat_test.go:416 +0x3a

Previous write by goroutine 3:
  math/big.divisors()
      src/pkg/math/big/nat.go:970 +0xa90
  math/big.nat.string()
      src/pkg/math/big/nat.go:807 +0x84d
  math/big.nat.decimalString()
      src/pkg/math/big/nat.go:731 +0x7e
  math/big.TestScanPi()
      src/pkg/math/big/nat_test.go:407 +0x1d6
  math/big.func·006()
      src/pkg/math/big/nat_test.go:416 +0x3a

Goroutine 4 (running) created at:
  math/big.TestScanPiParallel()
      src/pkg/math/big/nat_test.go:418 +0xac
  testing.tRunner()
      src/pkg/testing/testing.go:301 +0x86

Goroutine 3 (finished) created at:
  math/big.TestScanPiParallel()
      src/pkg/math/big/nat_test.go:418 +0xac
  testing.tRunner()
      src/pkg/testing/testing.go:301 +0x86

The issue is in the following code. If table[k-1].ndigits != 0 it does not mean that the
table is fully initialized. Occasionally I see "div by zero" crashes when
running in parallel.

    // extend table
    if table[k-1].ndigits == 0 {
        if cached {
            cacheLock.Lock() // begin critical section
        }

        // add new entries as needed
        var larger nat
        for i := 0; i < k; i++ {
            if table[i].ndigits == 0 {
                if i == 0 {
                    table[i].bbb = nat(nil).expWW(bb, Word(leafSize))
                    table[i].ndigits = ndigits * leafSize
                } else {
                    table[i].bbb = nat(nil).mul(table[i-1].bbb, table[i-1].bbb)
                    table[i].ndigits = 2 * table[i-1].ndigits
                }

                // optimization: exploit aggregated extra bits in macro blocks
                larger = nat(nil).set(table[i].bbb)
                for mulAddVWW(larger, larger, b, 0) == 0 {
                    table[i].bbb = table[i].bbb.set(larger)
                    table[i].ndigits++
                }

                table[i].nbits = table[i].bbb.bitLen()
            }
        }

        if cached {
            cacheLock.Unlock() // end critical section
        }
    }

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions