Skip to content

Lazy values are not guaranteed to be evaluated just once if accessed concurrently #566

@anfelor

Description

@anfelor

In a multi-core setting, it is possible for two threads to read the reference of an uninitialized lazy value and execute the action concurrently:

// Force a delayed value; the value is computed only on the first
// call to `force` and cached afterwards.
pub fun force( delayed : delayed<e,a> ) : e a
unsafe-total
val r = delayed.dref
match !r
Right(x) -> x
Left(action) ->
val x = mask-st{ mask<div>(action) }()
r := Right(x)
x

This implementation is sound for pure computations where duplicate evaluation can not be observed, but not for arbitrary algebraic effects. This mirrors the issue that Haskell had with lazy thunks containing unsafePerformIO. We could solve this problem by using an atomic compare and swap operation instead of the read instruction that puts a special Blackhole value into the reference and blocks if there is already a blackhole value in the thunk. While this is potentially expensive, it is only necessary if the value is thread-shared and we can continue using this faster implementation otherwise.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions