pod afConcurrent

Utility classes for sharing data between threads

Classes

AtomicList

A List that provides fast reads and lightweight writes between threads.

AtomicMap

A Map that provides fast reads and lightweight writes between threads.

LocalList

Manages a List stored in Actor.locals with a unique key.

LocalMap

Manages a Map stored in Actor.locals with a unique key.

LocalRef

Manages an Obj reference stored in Actor.locals with a unique key.

Synchronized

Provides synchronized access to blocks of code.

SynchronizedFileMap

A Synchronized cache whose values update should their associated file key be modified.

SynchronizedList

A List that provides fast reads and synchronised writes between threads, ensuring data integrity.

SynchronizedMap

A Map that provides fast reads and synchronised writes between threads, ensuring data integrity.

SynchronizedState

Provides synchronized access to a (non- const) mutable state object.

Overview

Concurrent builds upon the standard Fantom concurrent library and provides a collection of utility classes for sharing data between threads.

Install

Install Concurrent with the Fantom Repository Manager ( fanr ):

C:\> fanr install -r http://repo.status302.com/fanr/ afConcurrent

To use in a Fantom project, add a dependency to build.fan:

depends = ["sys 1.0", ..., "afConcurrent 1.0+"]

Documentation

Full API & fandocs are available on the Status302 repository.

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 exciting on their own, BedSheet and IoC use them to keep track of data to be cleaned up at the end of HTTP web requests.

Release Notes

v1.0.8

  • New: Added Synchronized.inSync() method to tell if you're currently in the Sync thread or not.
  • Chg: Renamed listType to valType in LocalList and AtomicList. (Potential breaking change.)

v1.0.6

  • New: Synchronized locks are re-entrant by default.
  • Chg: SynchronizedFileMap only caches values whose associated file exists.
  • Bug: SynchronizedFileMap would Err if handed a non-existant file.

v1.0.4

  • New: Runtime type checks on all List and Map, keys and values.
  • Bug: Synchronized could mistakenly wrap un-related IOErrs with it's own immutable err msg.
  • Bug: Could not set the SynchronizedFileMap timeout to null.

v1.0.2

  • New: All Maps and Lists can be parameterized with Types. Set them in the it-block ctor.
  • New: LocalRefs, LocalLists and LocalMaps are now really lazy and don't create objects unless they really need to.
  • New: Added SynchronizedFileMap.isModified(File).

v1.0.0

  • New: Initial release.
  • New: Added SynchronizedFileMap.
  • Chg: LocalRef ctor now takes a defFunc instead of an initValue.