The STM should work well for this. If you want to avoid retry, there are two approaches:
1) perform the reads in a transaction, but then do the rest of the work outside:
val (x, y, z) = atomic { implicit txn => (xRef(), yRef(), zRef()) }
if (decision(x, y, z)) ...
In this form the atomic block might retry (invisibly) until it gets a consistent snapshot of the refs, but once you get to the decision part there will be no more rollback. If you have a TMap or TSet you can call clone() inside the atomic block and rely on their lazy copy-on-write behavior for efficiency.
2) Use Ref.getWith(f), which will only roll the transaction back if f(ref.get) changes, rather than just the ref itself. For example, if x changes from 1 to 2 concurrent with the following transaction, there will be no rollback:
atomic { implicit txn =>
if (x.getWith( _ >= 0)) ...
....
}
Since _ >= 0 yields true for both values, the result of getWith has remained stable and the transaction can complete. At the moment there is not a convenient form for a multi-Ref version of this construct.
Thanks,
Nathan