Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
Q
quasar
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Wiki
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Snippets
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Package Registry
Container Registry
Model registry
Operate
Environments
Terraform modules
Monitor
Incidents
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
Jens Nolte
quasar
Commits
6b8d8263
Commit
6b8d8263
authored
3 years ago
by
Jens Nolte
Browse files
Options
Downloads
Patches
Plain Diff
Implement new resource manager
parent
9d68cdc8
No related branches found
Branches containing commit
No related tags found
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/Quasar/Resources.hs
+140
-17
140 additions, 17 deletions
src/Quasar/Resources.hs
with
140 additions
and
17 deletions
src/Quasar/Resources.hs
+
140
−
17
View file @
6b8d8263
module
Quasar.Resources
(
Resource
(
..
),
Disposer
,
ResourceManager
,
dispose
,
disposeEventuallySTM
,
disposeEventuallySTM_
,
isDisposed
,
newPrimitiveDisposer
,
-- * Resource manager
ResourceManager
,
newResourceManagerSTM
,
attachResource
,
)
where
import
Control.Concurrent
(
forkIO
)
import
Control.Concurrent.STM
import
Control.Monad
(
foldM
)
import
Control.Monad.Catch
import
Data.HashMap.Strict
(
HashMap
)
import
Data.HashMap.Strict
qualified
as
HM
import
Data.HashSet
(
HashSet
)
import
Data.HashSet
qualified
as
HashSet
import
Quasar.Async.STMHelper
import
Quasar.Awaitable
import
Quasar.Exceptions
...
...
@@ -32,11 +41,6 @@ data Disposer
type
DisposeFn
=
IO
(
Awaitable
()
)
newShortDisposer
::
TIOWorker
->
ExceptionChannel
->
IO
()
->
STM
Disposer
newShortDisposer
worker
exChan
disposeFn
=
newPrimitiveDisposer
worker
exChan
(
pure
<$>
disposeFn
)
newShortSTMDisposer
::
TIOWorker
->
ExceptionChannel
->
STM
()
->
STM
Disposer
newShortSTMDisposer
worker
exChan
disposeFn
=
newShortDisposer
worker
exChan
(
atomically
disposeFn
)
-- TODO document: IO has to be "short"
newPrimitiveDisposer
::
TIOWorker
->
ExceptionChannel
->
IO
(
Awaitable
()
)
->
STM
Disposer
...
...
@@ -53,7 +57,8 @@ disposeEventuallySTM resource =
case
getDisposer
resource
of
FnDisposer
_
worker
exChan
state
finalizers
->
do
beginDisposeFnDisposer
worker
exChan
state
finalizers
ResourceManagerDisposer
resourceManager
->
undefined
ResourceManagerDisposer
resourceManager
->
beginDisposeResourceManager
resourceManager
disposeEventuallySTM_
::
Resource
r
=>
r
->
STM
()
disposeEventuallySTM_
resource
=
void
$
disposeEventuallySTM
resource
...
...
@@ -62,8 +67,8 @@ disposeEventuallySTM_ resource = void $ disposeEventuallySTM resource
isDisposed
::
Resource
a
=>
a
->
Awaitable
()
isDisposed
resource
=
case
getDisposer
resource
of
FnDisposer
_
state
_
->
join
(
toAwaitable
state
)
ResourceManagerDisposer
_
resourceManager
->
undefined
--
resource
m
anager
FnDisposer
_
_
_
state
_
->
join
(
toAwaitable
state
)
ResourceManagerDisposer
resourceManager
->
resourceManagerIsDisposed
resource
M
anager
beginDisposeFnDisposer
::
TIOWorker
->
ExceptionChannel
->
DisposerState
->
Finalizers
->
STM
(
Awaitable
()
)
...
...
@@ -89,21 +94,139 @@ beginDisposeFnDisposer worker exChan disposeState finalizers =
atomically
$
runFinalizers
finalizers
throwIO
$
DisposeException
ex
disposerKey
::
Disposer
->
Unique
disposerKey
(
FnDisposer
key
_
_
_
_
)
=
key
disposerKey
(
ResourceManagerDisposer
resourceManager
)
=
resourceManagerKey
resourceManager
data
ResourceManager
=
ResourceManager
beginDisposeResourceManager
::
ResourceManager
->
STM
(
Awaitable
()
)
beginDisposeResourceManager
=
undefined
-- resource manager
disposerFinalizers
::
Disposer
->
Finalizers
disposerFinalizers
(
FnDisposer
_
_
_
_
finalizers
)
=
finalizers
disposerFinalizers
(
ResourceManagerDisposer
rm
)
=
resourceManagerFinalizers
rm
data
DisposeResult
=
DisposeResultDisposed
|
DisposeResultAwait
(
Awaitable
()
)
|
DisposeResultResourceManager
ResourceManagerResult
data
ResourceManagerResult
=
ResourceManagerResult
Unique
(
Awaitable
[
ResourceManagerResult
])
data
DisposeResult
=
DisposeResultAwait
(
Awaitable
()
)
|
DisposeResultDependencies
DisposeDependencies
data
DisposeDependencies
=
DisposeDependencies
Unique
(
Awaitable
[
DisposeDependencies
])
-- * Resource manager
data
ResourceManager
=
ResourceManager
{
resourceManagerKey
::
Unique
,
resourceManagerState
::
TVar
ResourceManagerState
,
resourceManagerFinalizers
::
Finalizers
}
data
ResourceManagerState
=
ResourceManagerNormal
(
TVar
(
HashMap
Unique
Disposer
))
TIOWorker
|
ResourceManagerDisposing
(
Awaitable
[
DisposeDependencies
])
|
ResourceManagerDisposed
newResourceManagerSTM
::
TIOWorker
->
STM
ResourceManager
newResourceManagerSTM
worker
=
do
resourceManagerKey
<-
newUniqueSTM
attachedResources
<-
newTVar
mempty
resourceManagerState
<-
newTVar
(
ResourceManagerNormal
attachedResources
worker
)
resourceManagerFinalizers
<-
newFinalizers
pure
ResourceManager
{
resourceManagerKey
,
resourceManagerState
,
resourceManagerFinalizers
}
attachResource
::
Resource
a
=>
ResourceManager
->
a
->
STM
()
attachResource
resourceManager
resource
=
attachDisposer
resourceManager
(
getDisposer
resource
)
attachDisposer
::
ResourceManager
->
Disposer
->
STM
()
attachDisposer
resourceManager
disposer
=
do
readTVar
(
resourceManagerState
resourceManager
)
>>=
\
case
ResourceManagerNormal
attachedResources
_
->
do
alreadyAttached
<-
isJust
.
HM
.
lookup
key
<$>
readTVar
attachedResources
unless
alreadyAttached
do
-- Returns false if the disposer is already finalized
attachedFinalizer
<-
registerFinalizer
(
disposerFinalizers
disposer
)
finalizer
when
attachedFinalizer
$
modifyTVar
attachedResources
(
HM
.
insert
key
disposer
)
_
->
undefined
-- failed to attach resource
where
key
::
Unique
key
=
disposerKey
disposer
finalizer
::
STM
()
finalizer
=
readTVar
(
resourceManagerState
resourceManager
)
>>=
\
case
ResourceManagerNormal
attachedResources
_
->
modifyTVar
attachedResources
(
HM
.
delete
key
)
-- No finalization required in other states, since all resources are disposed soon
-- (and awaiting each resource is cheaper than modifying a HashMap until it is empty).
_
->
pure
()
beginDisposeResourceManager
::
ResourceManager
->
STM
(
Awaitable
()
)
beginDisposeResourceManager
rm
=
do
void
$
beginDisposeResourceManagerInternal
rm
pure
$
resourceManagerIsDisposed
rm
beginDisposeResourceManagerInternal
::
ResourceManager
->
STM
DisposeDependencies
beginDisposeResourceManagerInternal
rm
=
do
readTVar
(
resourceManagerState
rm
)
>>=
\
case
ResourceManagerNormal
attachedResources
worker
->
do
dependenciesVar
<-
newAsyncVarSTM
writeTVar
(
resourceManagerState
rm
)
(
ResourceManagerDisposing
(
toAwaitable
dependenciesVar
))
attachedDisposers
<-
HM
.
elems
<$>
readTVar
attachedResources
startTrivialIO_
worker
undefined
(
void
$
forkIO
(
disposeThread
dependenciesVar
attachedDisposers
))
pure
$
DisposeDependencies
rmKey
(
toAwaitable
dependenciesVar
)
ResourceManagerDisposing
deps
->
pure
$
DisposeDependencies
rmKey
deps
ResourceManagerDisposed
->
pure
$
DisposeDependencies
rmKey
mempty
where
disposeThread
::
AsyncVar
[
DisposeDependencies
]
->
[
Disposer
]
->
IO
()
disposeThread
dependenciesVar
attachedDisposers
=
do
-- Begin to dispose all attached resources
results
<-
mapM
(
atomically
.
resourceManagerBeginDispose
)
attachedDisposers
-- Await direct resource awaitables and collect indirect dependencies
dependencies
<-
await
(
collectDependencies
results
)
-- Publish "direct dependencies complete"-status
putAsyncVar_
dependenciesVar
dependencies
-- Await indirect dependencies
awaitDisposeDependencies
$
DisposeDependencies
rmKey
(
pure
dependencies
)
-- Set state to disposed and run finalizers
atomically
do
writeTVar
(
resourceManagerState
rm
)
ResourceManagerDisposed
runFinalizers
(
resourceManagerFinalizers
rm
)
rmKey
::
Unique
rmKey
=
resourceManagerKey
rm
resourceManagerBeginDispose
::
Disposer
->
STM
DisposeResult
resourceManagerBeginDispose
(
FnDisposer
_
worker
exChan
state
finalizers
)
=
DisposeResultAwait
<$>
beginDisposeFnDisposer
worker
exChan
state
finalizers
resourceManagerBeginDispose
(
ResourceManagerDisposer
resourceManager
)
=
DisposeResultDependencies
<$>
beginDisposeResourceManagerInternal
resourceManager
collectDependencies
::
[
DisposeResult
]
->
Awaitable
[
DisposeDependencies
]
collectDependencies
(
DisposeResultAwait
awaitable
:
xs
)
=
awaitable
>>
collectDependencies
xs
collectDependencies
(
DisposeResultDependencies
deps
:
xs
)
=
(
deps
:
)
<$>
collectDependencies
xs
collectDependencies
[]
=
pure
[]
awaitDisposeDependencies
::
DisposeDependencies
->
IO
()
awaitDisposeDependencies
=
void
.
go
mempty
where
go
::
HashSet
Unique
->
DisposeDependencies
->
IO
(
HashSet
Unique
)
go
keys
(
DisposeDependencies
key
deps
)
|
HashSet
.
member
key
keys
=
pure
keys
-- loop detection: dependencies were already handled
|
otherwise
=
do
dependencies
<-
await
deps
foldM
go
(
HashSet
.
insert
key
keys
)
dependencies
resourceManagerIsDisposed
::
ResourceManager
->
Awaitable
()
resourceManagerIsDisposed
rm
=
unsafeAwaitSTM
$
readTVar
(
resourceManagerState
rm
)
>>=
\
case
ResourceManagerDisposed
->
pure
()
_
->
retry
-- * Implementation internals
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment