{- | Cycle through a set of resources (randomly), recreating them when they expire -}{-# LANGUAGE RecordWildCards, NamedFieldPuns, FlexibleContexts #-}moduleVar.PoolwhereimportControl.Applicative((<$>))importControl.Monad.MVarimportData.Array.IOimportData.Maybe(catMaybes)importControl.Monad.ErrorimportSystem.Random(randomRIO)importControl.Exception(assert)-- | Creator, destroyer, and checker of resources of type r. Creator may throw error or type e.dataFactoryer=Factory{newResource::ErrorTeIOr,killResource::r->IO(),isExpired::r->IOBool}newPool::Factoryer->Int->IO(Pooler)-- ^ Create new pool of initial max size, which must be >= 1newPoolfn=assert(n>0)$doarr<-newArray(0,n-1)Nothingvar<-newMVararrreturn(Poolfvar)dataPooler=Pool{factory::Factoryer,resources::MVar(IOArrayInt(Mayber))}-- ^ Pool of maximum N resources. Resources may expire on their own or be killed. Resources will initially be created on demand up N resources then recycled in random fashion. N may be changed by resizing the pool. Random is preferred to round-robin to distribute effect of pathological use cases that use every Xth resource the most and N is a multiple of X.-- Resources *must* close/kill themselves when garbage collected ('resize' relies on this).aResource::(Errore)=>Pooler->ErrorTeIOr-- ^ Return a random live resource in pool or create new one if expired or not yet createdaResourcePool{..}=withMVarresources$\array->doi<-liftIO$randomRIO=<<getBoundsarraymr<-liftIO$readArrayarrayir<-maybe(newarrayi)(checkarrayi)mrreturnrwherenewarrayi=dor<-newResourcefactoryliftIO$writeArrayarrayi(Justr)returnrcheckarrayir=dobad<-liftIO$isExpiredfactoryrifbadthennewarrayielsereturnrpoolSize::Pooler->IOInt-- ^ current max size of poolpoolSizePool{resources}=withMVarresources(fmaprangeSize.getBounds)resize::Pooler->Int->IO()-- ^ resize max size of pool. When shrinking some resource will be dropped without closing since they may still be in use. They are expected to close themselves when garbage collected.resizePool{resources}n=modifyMVar_resources$\array->dors<-taken<$>getElemsarrayarray'<-newListArray(0,n-1)(rs++repeatNothing)returnarray'killAll::Pooler->IO()-- ^ Kill all resources in pool so subsequent access creates new oneskillAll(PoolFactory{killResource}resources)=withMVarresources$\array->domapM_killResource.catMaybes=<<getElemsarraymapM_(\i->writeArrayarrayiNothing).range=<<getBoundsarray