implicit tx ---> ThreadLocal

36 views
Skip to first unread message

Sciss

unread,
Aug 11, 2010, 8:15:21 AM8/11/10
to scala...@googlegroups.com
hi,

(first of all thanks for the reply regarding the sbt setup; i haven't had time to make the switch, but will try out soon)

i'm in need of eliminating explicit "implicit tx" from my DSL, as this starts to create ambiguities, for instance:

def trigger( name: String )( fun: Txn => Unit ) {}

def test( implicit tx: Txn ) {
val x = gen( "test" ) {
trigger( "schnuck" ) { implicit tx2 =>
p2.play // play takes an implicit ProcTxn
}
}
}

this fails to compile, as both tx and tx2 are in scope when p2.play is called, and i don't want the user to be forced into

trigger( "schnuck" ) { tx2 =>
p2.play( tx2 )
}

(in fact, i don't want the user to have any explicit mentioning of tx in the DSL)

... so i am looking again in your CCSTM paper, and you describe an alternative approach using dynamic scoping using a ThreadLocal ...

is this currently possible with CCSTM, and how would i access the Txn this way?


thanks, -sciss-

Nathan Bronson

unread,
Aug 11, 2010, 4:32:03 PM8/11/10
to scala...@googlegroups.com
As a preface, in Scala you can fix the ambiguous implicits problem by
giving both implicit parameters the same name. The inner one will
shadow the outer one if you change tx2 to tx.

There are three ways to get rid of the implicit Txn parameter to your
DSL functions: rediscover the current Txn, start a new atomic block
(that will nest in the outer one), or use Ref.View instead of Ref.

1) Rediscover the current Txn:

{
implicit val tx = Txn.currentOrNull
// use Ref-s here
}

CCSTM already keeps a ThreadLocal internally that stores the Txn active
on a current thread, Txn.current and Txn.currentOrNull fetch the Txn
using that ThreadLocal. The caller will still be responsible for
running everything in an atomic block, but this will only be checked at
runtime (rather than compile time like the implicit Txn parameter).

2) Start a new atomic block:

atomic { implicit tx =>
// use Ref-s here
}

Nesting atomic blocks is fine. Commit handlers won't fire until the
outer-most atomic block finishes. Unlike access to Ref-s, calls to
atomic always check the ThreadLocal (this is the hybrid scheme from the
paper). Using this scheme the caller can choose whether or not to run
inside an atomic block, which might be an advantage or a disadvantage.

3) Use the Ref.View returned from Ref.single:

val x = Ref(initialValue).single
// use Ref.View here

In this scheme x will have type Ref.View[T] instead of Ref[T]. The view
can be accessed without a Txn, it will always perform the ThreadLocal
lookup. This has the same behavior (although it is more effecient
underneath) as performing each operation on x inside its own atomic
block:

val ref = Ref(v0)
val view = ref.single

view() <==> atomic { implicit tx => ref() }
view() = x <==> atomic { implicit tx => ref() = x }

The following link contains some more information on Ref.View:

http://ppl.stanford.edu/ccstm/site/modes.html

I'm interested in which solution (if any) you end up trying.

Cheers,
Nathan

Reply all
Reply to author
Forward
0 new messages