Skip to content

BigInt not strictly thread-safe #12778

Open
@charpov

Description

@charpov

I believe that BigInt, which is nominally immutable, is not strictly thread-safe because of the non-final field _bigInteger. If N is a Java BigInteger, there is not enough happens-before between a thread doing x = new BigInt(N) and another doing x.bigInteger, which could return a BigInteger equal to Long.MinValue instead of N.

bigInteger is implemented as:

  def bigInteger: BigInteger = {
    val read = _bigInteger
    if (read ne null) read else {
      val write = BigInteger.valueOf(_long)
      _bigInteger = write // reference assignment is atomic; this is multi-thread safe (if possibly wasteful)
      write
    }
  }

The comment about atomicity ignores memory visibility issues, which are not discussed anywhere in the source file. In the scenario above, read = _bigInteger could read null instead of N and create an incorrect BigInteger from _long (which is Long.MinValue in this case).

One solution is to add a memory fence at the end of the BigInt constructor. Alternatively, since _long is final and initialized after _bigInteger (assuming fields are initialized in declaration order), one could always read _long before reading _bigInteger. This seems to be the case everywhere in the source, except inside the bigInteger method. So, adding val _long = this._long at the beginning of this method (before val read = ...) should be sufficient.

Note that there is no danger of _bigInteger being neither N nor null because BigInteger is immutable (except for a few lazily initialized int fields which are not an issue).

Maybe I'm missing something subtle and the code is correct, but because the comments in the source of BigInt never mention JMM issues, I wonder if they have been considered at all.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions