sourceafConcurrent::ActorPools.fan

using concurrent

** (Service) - 
** Maintains a collection of named 'ActorPools'. Use to keep tabs on your resources, particularly 
** useful when creating 'SynchronizedMap' and 'SynchronizedList' instances.
** 
** 'ActorPools' is automatically made available in IoC enabled applications. 
** Contribute to 'ActorPools' via 'AppModule' contributions:
** 
** pre>
** syntax: fantom
** @Contribute { serviceType=ActorPools# }
** Void contributeActorPools(Configuration config) {
**     config["myPool"] = ActorPool() { it.name = "MyPool" }
** }
** <pre
** 
** 'ActorPools' is then used behind the scenes to create and inject instances of 'ActorPool', 'Synchronized', 
** 'SynchronizedList', and 'SynchronizedMap' with a named 'ActorPool'. Example:
** 
** pre>
** syntax: fantom
** @Inject { id="myPool"; type=User[]# }
** private const SynchronizedList loggedInUsers
** <pre
** 
** Note it is always a good idea to name your 'ActorPools' for debugging purposes.
** 
** @uses Configuration of 'Str:ActorPool'
const mixin ActorPools {

    ** Returns the 'ActorPool' mapped to the given name, or throws a 'IocErr' / 'NotFoundErr' if it doesn't exist.
    @Operator
    abstract ActorPool get(Str name)

    ** Returns a map of 'ActorPool' names and the number of times it's been requested. 
    abstract Str:Int stats()

    ** Creates an 'ActorPools' instance. Use in non-IoC environments. 
    static new make(Str:ActorPool actorPools) {
        ActorPoolsImpl(actorPools)
    }
}

internal const class ActorPoolsImpl : ActorPools {
    
    const Str:ActorPool actorPools
    const Str:AtomicInt usageStats
    
    new make(Str:ActorPool actorPools) {
        this.actorPools = actorPools
        
        counts := Str:AtomicInt[:]
        actorPools.keys.each |k| { 
            counts[k] = AtomicInt()
        }
        this.usageStats = counts
    }
    
    @Operator
    override ActorPool get(Str name) {
        pool := actorPools[name] ?: throw ActorPoolNotFoundErr("There is no ActorPool with the name: ${name}", actorPools.keys)
        usageStats[name].incrementAndGet
        return pool
    }
    
    ** Returns a map of 'ActorPool' names and the number of times it's been requested. 
    override Str:Int stats() {
        usageStats.map { it.val }
    }
}

@NoDoc
const class ActorPoolNotFoundErr : ArgErr {
    const Str?[] availableValues
    
    new make(Str msg, Obj?[] availableValues, Err? cause := null) : super(msg, cause) {
        this.availableValues = availableValues.map { it?.toStr }.sort
    }
}