Embedded Txn

45 views
Skip to first unread message

Lixing

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

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? 

Thanks,
Lixing

Nathan Bronson

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

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
    lock()
    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
ngbr...@gmail.com
Reply all
Reply to author
Forward
0 new messages