Search code examples
scalaconcurrencyatomicrace-conditionconcurrent.futures

Completing scala promises race


I can't seem to find anywhere whether complete and tryComplete are atomic operations on Promises in Scala. Promises are only supposed to be written to once, but if two tryCompletes happen concurrently in two different callbacks for example could something go wrong? Or are we assured that tryComplete is atomic?


Solution

  • First a quick note that success(...) is equivalent to calling complete(Success(...)) and tryComplete(...) is equivalent to complete(...).isCompleted.

    In the docs it says

    As mentioned before, promises have single-assignment semantics. As such, they can be completed only once. Calling success on a promise that has already been completed (or failed) will throw an IllegalStateException.

    A promise can only complete once. Digging into the source code, DefaultPromise extends AtomicReference (ie. thread safe) and so all writes are atomic. This means that if you have two threads completing a promise, only one of them can ever succeed and it'll be whichever did so first. The other will throw an IllegalStateException.

    Here's a small example of what happens when you try and complete a promise twice.

    https://scastie.scala-lang.org/hTYBqVywSQCl8bFSgQI0Sg

    Though apparently it seems one can circumvent the immutability of a Future by doing a bunch of weird casting acrobatics.

    https://contributors.scala-lang.org/t/defaultpromise-violates-encapsulation/3440

    One should probably avoid that.