i'm getting some bad exception here with the ccstm 0.2.1 and my TxnModel class i had described some time ago ( http://github.com/Sciss/SoundProcesses/blob/master/src/main/scala/de/sciss/synth/proc/TxnModel.scala )
the exception is:
discarding exception from txn callback, status is Committed
java.lang.IllegalStateException: Committed
at edu.stanford.ppl.ccstm.impl.StatusHolder.rollbackOrIllegalState(StatusHolder.scala:71)
at edu.stanford.ppl.ccstm.impl.StatusHolder.requireActiveOrValidating(StatusHolder.scala:81)
(8) at edu.stanford.ppl.ccstm.Txn.addWriteResource(Txn.scala:644)
at de.sciss.synth.proc.ProcTxn$$anonfun$atomic$1.apply(ProcTxn.scala:72)
at de.sciss.synth.proc.ProcTxn$$anonfun$atomic$1.apply(ProcTxn.scala:70)
(7) at edu.stanford.ppl.ccstm.STM$.nestedAtomic(STM.scala:45)
(6) at edu.stanford.ppl.ccstm.STM$.atomic(STM.scala:33)
(5) at de.sciss.synth.proc.ProcTxn$.atomic(ProcTxn.scala:70)
(4) at de.sciss.cupola.ProcessManager$$anon$2.updated(ProcessManager.scala:86)
(3) at de.sciss.cupola.ProcessManager$$anon$2.updated(ProcessManager.scala:83)
(2) at de.sciss.synth.proc.TxnModel$$anonfun$touch$1$$anonfun$apply$4$$anonfun$apply$6.apply(TxnModel.scala:88)
at de.sciss.synth.proc.TxnModel$$anonfun$touch$1$$anonfun$apply$4$$anonfun$apply$6.apply(TxnModel.scala:88)
at scala.collection.Iterator$class.foreach(Iterator.scala:631)
at scala.collection.LinearSeqLike$$anon$1.foreach(LinearSeqLike.scala:52)
at scala.collection.IterableLike$class.foreach(IterableLike.scala:79)
at scala.collection.immutable.Queue.foreach(Queue.scala:31)
(1) at de.sciss.synth.proc.TxnModel$$anonfun$touch$1$$anonfun$apply$4.apply(TxnModel.scala:88)
at de.sciss.synth.proc.TxnModel$$anonfun$touch$1$$anonfun$apply$4.apply(TxnModel.scala:86)
at de.sciss.synth.proc.ProcTxn$Impl$$anonfun$afterCommit$1.apply(ProcTxn.scala:236)
at de.sciss.synth.proc.ProcTxn$Impl$$anonfun$afterCommit$1.apply(ProcTxn.scala:236)
at edu.stanford.ppl.ccstm.Txn$$anonfun$callAfter$1.apply(Txn.scala:740)
at edu.stanford.ppl.ccstm.Txn$$anonfun$callAfter$1.apply(Txn.scala:738)
at edu.stanford.ppl.ccstm.impl.CallbackPrioSlot.visitOne(CallbackList.scala:129)
at edu.stanford.ppl.ccstm.impl.CallbackList.attemptSlot(CallbackList.scala:78)
at edu.stanford.ppl.ccstm.impl.CallbackList.attemptForeach(CallbackList.scala:66)
at edu.stanford.ppl.ccstm.impl.CallbackList.foreach(CallbackList.scala:51)
at edu.stanford.ppl.ccstm.impl.CallbackList.foreach(CallbackList.scala:47)
at edu.stanford.ppl.ccstm.Txn.callAfter(Txn.scala:738)
at edu.stanford.ppl.ccstm.impl.TxnImpl.commitImpl(TxnImpl.scala:267)
at edu.stanford.ppl.ccstm.Txn.commit(Txn.scala:558)
at edu.stanford.ppl.ccstm.Txn.commitAndRethrow(Txn.scala:564)
at edu.stanford.ppl.ccstm.STM$.attemptImpl(STM.scala:134)
at edu.stanford.ppl.ccstm.STM$.topLevelAtomic(STM.scala:61)
at edu.stanford.ppl.ccstm.STM$.atomic(STM.scala:35)
at de.sciss.synth.proc.ProcTxn$.atomic(ProcTxn.scala:70)
at de.sciss.cupola.ProcessManager.de$sciss$cupola$ProcessManager$$timerUpdate(ProcessManager.scala:66)
at de.sciss.cupola.ProcessManager$$anon$1.run(ProcessManager.scala:57)
at java.util.TimerThread.mainLoop(Timer.java:512)
at java.util.TimerThread.run(Timer.java:462)
without explaining each line, the situation is:
- i am waiting for an update dispatch from my TxnModel which happens in the afterCommit of the txn (1).
- the dispatch iterates over the current observers and calls their updated method (2)
- i have here a particular listener (3) which issues a new transaction to delete some object (4)
- ProcTxn is my wrapper around Txn (5), and that has a method atomic which just wraps STM.atomic (6).
- now here is the bug: it seems that, although my code runs in the afterCommit, CCSTM hasn't cleared yet
ThreadContext state, hence (6) falsely invokes nestedAtomic (7) which a bit later, as soon as the transaction
(which is the old transaction by mistake) is touched (8) throws an error.
the problem as i see it is this snippet from TxnImpl:
} finally {
callAfter()
// cleanup
val ctx = ThreadContext.get
ctx.put(_callbacks, _readSet, _writeBuffer, _strongRefSet, _slot)
_callbacks = null
_readSet = null
_writeBuffer = null
slotManager.release(_slot)
detach(ctx)
}
because here clearly callAfter is invoked before detach, so essentially it's not possible to create a new transaction in an afterCommit { } code block.
i don't know if there are new issues with moving callAfter after detach? if not, i would suggest that this would fix the problem.
i am working around this now deferring the action to another thread, but this is not a very elegant solution.
best, -sciss-
You're right, the current txn should be detached before firing the
after-completion callbacks. I've deployed a snapshot release of CCSTM
that allows this. You can access it by changing the CCSTM dependency
section in your sbt project file to:
val ccstm = "edu.stanford.ppl" % "ccstm" %
"0.2.2-for-scala-2.8.0-SNAPSHOT"
val ccstmRepo = "CCSTM Release Repository at PPL" at
"http://ppl.stanford.edu/ccstm/repo-releases"
val ccstmSnap = "CCSTM Snapshot Repository at PPL" at
"http://ppl.stanford.edu/ccstm/repo-snapshots"
- Nathan