Tom's recent talk of Actors made me re-look at how I use them, especially in the likes of IoC. So I've tidied up, renamed and ripped out several classes that I find myself using over and over again. I figured if I find them useful, others may too!
Tom - you were asking about name clashes in Actor.locals... I give an example below (in #local) of how easily that can happen.
Usage
The Concurrent library provides a few strategies for sharing data:
Synchronized
Synchronized provides synchronized serial access to a block of code, akin to Java's synchronized keyword. Extend the Synchronized class to use the familiar syntax:
const class Example : Synchronized {
new make() : super(ActorPool()) { }
Void main() {
synchronized |->| {
// ...
// important stuff
// ...
}
}
}
Synchronized works by calling the function from within the receive() method of an Actor, which has important implications. First, the passed in function needs to be an immutable func. Next, any object returned also has to be immutable (preferably) or serializable.
Instances of Synchronized may also be used as a mechanism for exclusive locking. For example:
class Example {
Synchronized lock := Synchronized(ActorPool())
Void main() {
lock.synchronized |->| {
// ...
// important stuff
// ...
}
}
}
Atomic
Atomic Lists and Maps are similar to their Synchronized counterparts in that they are backed by an object held in an AtomicRef. But their write operations are not synchronized. This means they are much more lightweight but it also means they are susceptible to data-loss during race conditions between multiple threads. If used for caching situations where it is not essential for values to exist, this may be acceptable.
Local
Local Refs, Lists and Maps do not share data between threads, in fact, quite the opposite!
They wrap data stored in Actor.locals() thereby constraining it to only be accessed by the executing thread. The data is said to be local to that thread.
But data held in Actor.locals() is susceptible to being overwritten due to name clashes. Consider:
class Drink {
Str beer {
get { Actor.locals["beer"] }
set { Actor.locals["beer"] = it }
}
}
man := Drink()
man.beer = "Ale"
kid := Drink()
kid.beer = "Ginger Ale"
echo(man.beer) // --> Ginger Ale (WRONG!)
echo(kid.beer) // --> Ginger Ale
To prevent this, LocalRef creates a unique qualified name to store the data under:
class Drink {
LocalRef beer := LocalRef("beer")
}
man := Drink()
man.beer.val = "Ale"
kid := Drink()
kid.beer.val = "Ginger Ale"
echo(man.beer.val) // --> Ale
echo(kid.beer.val) // --> Ginger Ale
echo(man.beer.qname) // --> 0001.beer
echo(kid.beer.qname) // --> 0002.beer
While LocalRefs are not too exiting on their own, BedSheet and IoC use them to keep track of data to be cleaned up at the end of HTTP web requests.
Have fun!
: )
tomclSun 4 May 2014
This is great. I keep on feeling that Actor.locals should be more like LocalRef.
Having said that, the problem with global over-writing only appears if you use Actor.locals outside of an Actor instance and expect it not to be global. The proper solution is to restrict scope of Actor.locals so you cannot do this inadvertently. In current form it works, but is not nice.
SlimerDude Wed 30 Apr 2014
afConcurrent Preview Release!
Concurrent
builds upon the standard Fantom concurrent library and provides a collection of utility classes for sharing data between threads.fanr install -r http://repo.status302.com/fanr/ afConcurrent
Tom's recent talk of Actors made me re-look at how I use them, especially in the likes of IoC. So I've tidied up, renamed and ripped out several classes that I find myself using over and over again. I figured if I find them useful, others may too!
Tom - you were asking about name clashes in
Actor.locals
... I give an example below (in #local) of how easily that can happen.Usage
The
Concurrent
library provides a few strategies for sharing data:Synchronized
Synchronized provides synchronized serial access to a block of code, akin to Java's
synchronized
keyword. Extend theSynchronized
class to use the familiar syntax:Synchronized
works by calling the function from within thereceive()
method of anActor
, which has important implications. First, the passed in function needs to be animmutable func
. Next, any object returned also has to be immutable (preferably) or serializable.Instances of
Synchronized
may also be used as a mechanism for exclusive locking. For example:Atomic
Atomic Lists and Maps are similar to their Synchronized counterparts in that they are backed by an object held in an
AtomicRef
. But their write operations are not synchronized. This means they are much more lightweight but it also means they are susceptible to data-loss during race conditions between multiple threads. If used for caching situations where it is not essential for values to exist, this may be acceptable.Local
Local Refs, Lists and Maps do not share data between threads, in fact, quite the opposite!
They wrap data stored in
Actor.locals()
thereby constraining it to only be accessed by the executing thread. The data is said to be local to that thread.But data held in
Actor.locals()
is susceptible to being overwritten due to name clashes. Consider:To prevent this, LocalRef creates a unique qualified name to store the data under:
While
LocalRefs
are not too exiting on their own, BedSheet and IoC use them to keep track of data to be cleaned up at the end of HTTP web requests.Have fun!
: )
tomcl Sun 4 May 2014
This is great. I keep on feeling that
Actor.locals
should be more likeLocalRef
.Having said that, the problem with global over-writing only appears if you use Actor.locals outside of an Actor instance and expect it not to be global. The proper solution is to restrict scope of
Actor.locals
so you cannot do this inadvertently. In current form it works, but is not nice.