Skip to content

Instantly share code, notes, and snippets.

@speedcom
Last active May 12, 2016 08:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save speedcom/301bc309f21bc1f5be18b3c9af4690e3 to your computer and use it in GitHub Desktop.
Save speedcom/301bc309f21bc1f5be18b3c9af4690e3 to your computer and use it in GitHub Desktop.
Making asynchronous IO operation from within atomic ScalaSTM block
val cache = Ref[List[Int]](Nil)
val needUpdate = Ref[Boolean](true)
def getOrUpdate = atomic { implicit txn =>
if(needUpdate()) {
asyncCallingToWebservice().map { data =>
cache() = data
needUpdate() = false
data
}
} else {
cache()
}
}
@speedcom
Copy link
Author

Is is safe to run asynchronous non-blocking IO operation from atomic block?

@axel22
Copy link

axel22 commented May 12, 2016

There are three reasons why this is not the case.
First, in the implementation, the transaction book-keeping is stored in the Txn object.
This object is kept in a thread-local variable.
For performance reasons, the Txn object is also available as an implicit variable to
the atomic block.
So, the asynchronous thread will never see the Txn from a thread-local, but might
capture the Txn from the implicit argument.
If this happens, operations on the Txn object will fail, either becase the transaction
has already ended by that time, or because the ID of the thread accessing Txn is
incorrect (if this does not happen, it's a bug).

Second reason is that asynchronous computation is very likely much slower than the code
in the transaction (otherwise, there would be no reason to be asynchronous).
STMs work best when transactions are short, long transactions usually cause scalability
problems (as does blocking in a transaction, this is why retry exists).

Last reason is -- in case the asynchronous computation were somehow able to continue the
transaction, then what if the transaction needs to restart? This would mean that the
asynchronous computation would need to be restarted from the retried transaction as
well. And asynchronous computations often exist solely because they do some
side-effects on remote objects. ScalaSTM, like most STMs, does not allow side-effects
inside the transaction.

You should instead either do restructure the code to make the asynchronous computation
happen after the STM successfully ends (ScalaSTM has support for this), or you should
separate the code into two transactions -- first running before the async block,
and second one running inside the async block and possibly validating if the state seen
in the first transaction is still the same.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment