using concurrent**** A helper class used to store, access and retrieve mutable state within a 'const' class. For IoC ** this means your services can be declared as 'perApplication' or singleton scope.** ** 'ConcurrentState' wraps a state object in an Actor, and provides access to it via the ** 'withState' and 'getState' methods. Note that by their nature, these methods are immutable ** boundaries. Meaning that while all data in the State object can be mutable, but data passed in ** and out of the methods can not be. ** ** 'ConcurrentState' has been designed to be *type safe*, that is you cannot accidently call ** methods on your State object. The compiler forces all access to the state object to be made ** through the 'withState' and 'getState' methods.** ** A full example of a mutable const map class is as follows:** ** pre>** const class ConstMap {** const ConcurrentState conState := ConcurrentState(ConstMapState#)** ** ** Note that both 'key' and 'value' need to be immutable** @Operator** Obj get(Obj key) {** getState {** it.map[key]** }** }** ** ** Note that both 'key' and 'value' need to be immutable** @Operator** Void set(Obj key, Obj value) {** withState {** it.map[key] = value** }** }** ** ** helper method used to narrow the state type** private Void withState(|ConstMapState| state) {** conState.withState(state)** }** ** ** helper method used to narrow the state type** private Obj? getState(|ConstMapState -> Obj?| state) {** conState.getState(state)** }** }** ** class ConstMapState {** Obj:Obj map := [:]** }** <pre** ** As alternatives to 'ConcurrentState' don't forget you also have ** [AtomicBool]`concurrent::AtomicBool`, [AtomicInt]`concurrent::AtomicInt` and ** [AtomicRef]`concurrent::AtomicRef`** constclass ConcurrentState {privatestaticconst Log log := Utils.getLog(ConcurrentState#)internalstaticconst ActorPool actorPool := ActorPool()privateconst Actor stateActor := Actor(actorPool, |Obj? obj -> Obj?| { receive(obj)})privateconst |->Obj| stateFactoryprivateconst ThreadStash stashprivate Obj? state { get { stash["state"]} set { stash["state"] = it}}** The given state type must have a public no-args ctor as per `sys::Type.make`new makeWithStateType(Type stateType){this.stateFactory = |->Obj| { stateType.make }this.stash = ThreadStash(ConcurrentState#.name + "." + stateType.name)}new makeWithStateFactory(|->Obj| stateFactory){this.stateFactory = stateFactorythis.stash = ThreadStash(ConcurrentState#.name + ".defaultName")}** Use to access state. Call 'get()' on the returned 'Future' to ensure any Errs are rethrown. virtual Future withState(|Obj->Obj?| f){// explicit call to .toImmutable() - see http://fantom.org/sidewalk/topic/1798#c12190 func := f.toImmutable future := stateActor.send([true, func].toImmutable)return future}** Use to return statevirtual Obj? getState(|Obj->Obj?| f){// explicit call to .toImmutable() - see http://fantom.org/sidewalk/topic/1798#c12190 func := f.toImmutable future := stateActor.send([false, func].toImmutable)return get(future)}private Obj? get(Future future){try{return future.get}catch(NotImmutableErr err){throw NotImmutableErr("Return value not immutable", err)}}private Obj? receive(Obj[] msg){ logErr := msg[0]as Bool func := msg[1]as |Obj->Obj?|try{// lazily create our stateif(state == null) state = stateFactory()return func.call(state)}catch(Err e){// if the func has a return type, then an the Err is rethrown on assignment// else we log the Err so the Thread doesn't fail silentlyif(logErr || func.returns == Void#) log.err("receive()", e)throw e}}}