Embedded Txn

Skip to first unread message


Mar 3, 2013, 10:05:09 PM3/3/13
to scala-stm-e...@googlegroups.com

I am trying to implement an object in scalaSTM and use lock inside this object. In the object, the methods like 'put()', I write atomic nesteds  ' atomic { implicit txn =>...} '. And inside the 'put()' method, I try to lock the value where the lock is implemented in the other class. For the lock class, I have ' def lock (): Unit ={atomic { implicit txn =>Txn.afterCompletion { status =>lock.unlock() } '.

Suppose there is a transaction that called the 'put()' method, will it notify the lock to unlock after it rolls back or commits? 


Nathan Bronson

Mar 3, 2013, 11:33:39 PM3/3/13
to scala-stm-e...@googlegroups.com

Using afterCompletion like this calls unlock at the right time, but it is unsafe for another reason. When there is a conflict, the STM has to pick an order in which to execute the transactions. If you try to impose an external order (using locks, for example) that is different, the system will deadlock.

Another way to think of this is that the STM implicitly acquires read or write locks for all of the Ref-s that are used in a transaction, and it automatically detects any deadlock cycles, breaking them by rolling back one of the transactions. Since it isn't aware of the locks that you are using, it can't detect those deadlock cycles.

If you don't have lots of lock contention, one possibility is to use tryLock, and then to trigger transaction rollback if you can't acquire it. Also, if you are always using this inside a transaction (otherwise the lock would be immediately released) it will be slightly faster to use Txn.findCurrent.

  def lock(): Unit = {
    implicit txn = Txn.findCurrent.get // this will throw an exception if not inside a txn already
    if (!realLock.tryLock()) Txn.rollback
    Txn.afterCompletion { _ => realLock.unlock() }

This is safe, but if you actually need to wait for locks it turns the waiting into busy-loops (not so good).

Another possibility is to build the lock yourself, using a Ref[Boolean] and ScalaSTM's ability to wait

  val lockHeld = Ref(false)

  def lock(): Unit = atomic { implicit txn =>
    if (lockHeld()) retry
    lockHeld() = true

  def unlock(): Unit = {
    lockHeld.single() = false

  def lockForTxnDuration(): Unit = {
    implicit txn = Txn.findCurrent.get
    Txn.afterCompletion { _ => unlock() }

This lock isn't as fast as the non-STM aware ones (for most cases it will still be sub-microsecond, though), but it cooperates fully with the STM. If you are going to use this approach and call lock() a lot from outside a transaction, let me know and we can optimize lock() a bit for that case.

 - Nathan

You received this message because you are subscribed to the Google Groups "Scala STM Discussion" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-stm-expert-...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Nathan Grasso Bronson
Reply all
Reply to author
0 new messages