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
21fbd1e0
Commit
21fbd1e0
authored
3 years ago
by
Jens Nolte
Browse files
Options
Downloads
Patches
Plain Diff
Update ResourceManager api draft
parent
7167f4ce
No related branches found
No related tags found
No related merge requests found
Changes
4
Hide whitespace changes
Inline
Side-by-side
Showing
4 changed files
quasar.cabal
+1
-1
1 addition, 1 deletion
quasar.cabal
src/Quasar/Core.hs
+90
-42
90 additions, 42 deletions
src/Quasar/Core.hs
src/Quasar/Disposable.hs
+1
-1
1 addition, 1 deletion
src/Quasar/Disposable.hs
test/Quasar/AsyncSpec.hs
+8
-8
8 additions, 8 deletions
test/Quasar/AsyncSpec.hs
with
100 additions
and
52 deletions
quasar.cabal
+
1
−
1
View file @
21fbd1e0
...
@@ -47,10 +47,10 @@ common shared-properties
...
@@ -47,10 +47,10 @@ common shared-properties
default-language: Haskell2010
default-language: Haskell2010
ghc-options:
ghc-options:
-Weverything
-Weverything
-Wno-all-missed-specialisations
-Wno-missing-safe-haskell-mode
-Wno-missing-safe-haskell-mode
-Wno-missing-import-lists
-Wno-missing-import-lists
-Wno-unsafe
-Wno-unsafe
-Wno-all-missed-specialisations
-Werror=incomplete-patterns
-Werror=incomplete-patterns
-Werror=missing-methods
-Werror=missing-methods
...
...
This diff is collapsed.
Click to expand it.
src/Quasar/Core.hs
+
90
−
42
View file @
21fbd1e0
module
Quasar.Core
(
module
Quasar.Core
(
-- * ResourceManager
ResourceManager
,
ResourceManagerConfiguraiton
(
..
),
HasResourceManager
(
..
),
withResourceManager
,
withDefaultResourceManager
,
withUnlimitedResourceManager
,
newResourceManager
,
disposeResourceManager
,
-- * AsyncTask
AsyncTask
,
cancelTask
,
toAsyncTask
,
successfulTask
,
-- * AsyncIO
-- * AsyncIO
AsyncIO
,
AsyncIO
,
async
,
async
,
await
,
await
,
askPool
,
runAsyncIO
,
awaitResult
,
awaitResult
,
)
where
)
where
import
Control.Concurrent
(
ThreadId
,
forkIO
,
forkIOWithUnmask
,
myThreadId
)
import
Control.Concurrent
(
ThreadId
,
forkIOWithUnmask
,
myThreadId
)
import
Control.Concurrent.STM
import
Control.Concurrent.STM
import
Control.Monad.Catch
import
Control.Monad.Catch
import
Control.Monad.Reader
import
Control.Monad.Reader
...
@@ -18,62 +32,85 @@ import Quasar.Awaitable
...
@@ -18,62 +32,85 @@ import Quasar.Awaitable
import
Quasar.Prelude
import
Quasar.Prelude
-- * AsyncIO
newtype
AsyncT
m
a
=
AsyncT
(
ReaderT
Pool
m
a
)
deriving
newtype
(
MonadTrans
,
Functor
,
Applicative
,
Monad
,
MonadIO
,
MonadThrow
,
MonadCatch
,
MonadMask
)
type
AsyncIO
=
AsyncT
IO
-- | A monad for actions that run on a thread bound to a `ResourceManager`.
newtype
AsyncIO
a
=
AsyncIO
(
ReaderT
ResourceManager
IO
a
)
deriving
newtype
(
Functor
,
Applicative
,
Monad
,
MonadIO
,
MonadThrow
,
MonadCatch
,
MonadMask
)
-- | Run the synchronous part of an `AsyncIO` and then return an `Awaitable` that can be used to wait for completion of the synchronous part.
-- | Run the synchronous part of an `AsyncIO` and then return an `Awaitable` that can be used to wait for completion of the synchronous part.
async
::
AsyncIO
r
->
Async
IO
(
Awaitable
r
)
async
::
HasResourceManager
m
=>
AsyncIO
r
->
m
(
Async
Task
r
)
async
action
=
asyncWithUnmask
(
\
unmask
->
unmask
action
)
async
action
=
asyncWithUnmask
(
\
unmask
->
unmask
action
)
-- | Run the synchronous part of an `AsyncIO` and then return an `Awaitable` that can be used to wait for completion of the synchronous part.
-- | Run the synchronous part of an `AsyncIO` and then return an `Awaitable` that can be used to wait for completion of the synchronous part.
asyncWithUnmask
::
((
forall
a
.
AsyncIO
a
->
AsyncIO
a
)
->
AsyncIO
r
)
->
Async
IO
(
Awaitable
r
)
asyncWithUnmask
::
HasResourceManager
m
=>
((
forall
a
.
AsyncIO
a
->
AsyncIO
a
)
->
AsyncIO
r
)
->
m
(
Async
Task
r
)
-- TODO resource limits
-- TODO resource limits
asyncWithUnmask
action
=
mask_
$
do
asyncWithUnmask
action
=
do
pool
<-
askPool
resourceManager
<-
askResourceManager
resultVar
<-
newAsyncVar
resultVar
<-
newAsyncVar
liftIO
$
forkIOWithUnmask
$
\
unmask
->
do
liftIO
$
mask_
$
do
result
<-
try
$
runOnPool
pool
(
action
(
liftUnmask
unmask
))
void
$
forkIOWithUnmask
$
\
unmask
->
do
putAsyncVarEither_
resultVar
result
result
<-
try
$
runOnResourceManager
resourceManager
(
action
(
liftUnmask
unmask
))
pure
$
toAwaitable
resultVar
putAsyncVarEither_
resultVar
result
pure
$
AsyncTask
(
toAwaitable
resultVar
)
liftUnmask
::
(
IO
a
->
IO
a
)
->
AsyncIO
a
->
AsyncIO
a
liftUnmask
::
(
IO
a
->
IO
a
)
->
AsyncIO
a
->
AsyncIO
a
liftUnmask
unmask
action
=
do
liftUnmask
unmask
action
=
do
pool
<-
askPool
resourceManager
<-
askResourceManager
liftIO
$
unmask
$
runOnPool
pool
action
liftIO
$
unmask
$
runOnResourceManager
resourceManager
action
askPool
::
AsyncIO
Pool
askPool
=
AsyncT
ask
await
::
IsAwaitable
r
a
=>
a
->
AsyncIO
r
await
::
IsAwaitable
r
a
=>
a
->
AsyncIO
r
-- TODO resource limits
-- TODO resource limits
await
=
liftIO
.
awaitIO
await
=
liftIO
.
awaitIO
-- | Run an `AsyncIO` to completion and return the result.
runAsyncIO
::
AsyncIO
r
->
IO
r
runAsyncIO
=
withDefaultPool
class
MonadIO
m
=>
HasResourceManager
m
where
askResourceManager
::
m
ResourceManager
instance
HasResourceManager
AsyncIO
where
askResourceManager
=
AsyncIO
ask
awaitResult
::
AsyncIO
(
Awaitable
r
)
->
AsyncIO
r
awaitResult
::
Is
Awaitable
r
a
=>
AsyncIO
a
->
AsyncIO
r
awaitResult
=
(
await
=<<
)
awaitResult
=
(
await
=<<
)
-- TODO rename to ResourceManager
-- TODO rename to ResourceManager
data
Pool
=
Pool
{
data
ResourceManager
=
ResourceManager
{
configuration
::
Pool
Configuraiton
,
configuration
::
ResourceManager
Configuraiton
,
threads
::
TVar
(
HashSet
ThreadId
)
threads
::
TVar
(
HashSet
ThreadId
)
}
}
-- | A task that is running asynchronously. It has a result and can fail.
-- The result (or exception) can be aquired by using the `Awaitable` class (e.g. by calling `await` or `awaitIO`).
-- It might be possible to cancel the task by using the `Disposable` class if the operation has not been completed.
-- If the result is no longer required the task should be cancelled, to avoid leaking memory.
newtype
AsyncTask
r
=
AsyncTask
(
Awaitable
r
)
newtype
AsyncTask
r
=
AsyncTask
(
Awaitable
r
)
instance
IsAwaitable
r
(
AsyncTask
r
)
where
instance
IsAwaitable
r
(
AsyncTask
r
)
where
toAwaitable
(
AsyncTask
awaitable
)
=
awaitable
toAwaitable
(
AsyncTask
awaitable
)
=
awaitable
instance
Functor
AsyncTask
where
fmap
fn
(
AsyncTask
x
)
=
AsyncTask
(
fn
<$>
x
)
instance
Applicative
AsyncTask
where
pure
=
AsyncTask
.
pure
liftA2
fn
(
AsyncTask
fx
)
(
AsyncTask
fy
)
=
AsyncTask
$
liftA2
fn
fx
fy
cancelTask
::
AsyncTask
r
->
IO
()
-- TODO resource management
cancelTask
=
const
(
pure
()
)
-- | Creates an `AsyncTask` from an `Awaitable`.
-- The resulting task only depends on an external resource, so disposing it has no effect.
toAsyncTask
::
Awaitable
r
->
AsyncTask
r
toAsyncTask
=
AsyncTask
successfulTask
::
r
->
AsyncTask
r
successfulTask
=
AsyncTask
.
successfulAwaitable
data
CancelTask
=
CancelTask
data
CancelTask
=
CancelTask
deriving
stock
Show
deriving
stock
Show
instance
Exception
CancelTask
where
instance
Exception
CancelTask
where
...
@@ -83,29 +120,40 @@ data CancelledTask = CancelledTask
...
@@ -83,29 +120,40 @@ data CancelledTask = CancelledTask
instance
Exception
CancelledTask
where
instance
Exception
CancelledTask
where
data
PoolConfiguraiton
=
PoolConfiguraiton
data
ResourceManagerConfiguraiton
=
ResourceManagerConfiguraiton
{
maxThreads
::
Maybe
Int
}
defaultResourceManagerConfiguration
::
ResourceManagerConfiguraiton
defaultResourceManagerConfiguration
=
ResourceManagerConfiguraiton
{
maxThreads
=
Just
1
}
defaultPoolConfiguration
::
PoolConfiguraiton
unlimitedResourceManagerConfiguration
::
ResourceManagerConfiguraiton
defaultPoolConfiguration
=
PoolConfiguraiton
unlimitedResourceManagerConfiguration
=
ResourceManagerConfiguraiton
{
maxThreads
=
Nothing
}
with
Pool
::
Pool
Configuraiton
->
AsyncIO
r
->
IO
r
with
ResourceManager
::
ResourceManager
Configuraiton
->
AsyncIO
r
->
IO
r
with
Pool
configuration
=
bracket
(
new
Pool
configuration
)
dispose
Pool
.
flip
runOnPool
with
ResourceManager
configuration
=
bracket
(
new
ResourceManager
configuration
)
dispose
ResourceManager
.
flip
runOnResourceManager
runOn
Pool
::
Pool
->
AsyncIO
r
->
IO
r
runOn
ResourceManager
::
ResourceManager
->
AsyncIO
r
->
IO
r
runOn
Pool
pool
(
Async
T
action
)
=
runReaderT
action
pool
runOn
ResourceManager
resourceManager
(
Async
IO
action
)
=
runReaderT
action
resourceManager
withDefaultResourceManager
::
AsyncIO
a
->
IO
a
withDefaultResourceManager
=
withResourceManager
defaultResourceManagerConfiguration
with
DefaultPool
::
AsyncIO
a
->
IO
a
with
UnlimitedResourceManager
::
AsyncIO
a
->
IO
a
with
DefaultPool
=
withPool
defaultPool
Configuration
with
UnlimitedResourceManager
=
withResourceManager
unlimitedResourceManager
Configuration
new
Pool
::
Pool
Configuraiton
->
IO
Pool
new
ResourceManager
::
ResourceManager
Configuraiton
->
IO
ResourceManager
new
Pool
configuration
=
do
new
ResourceManager
configuration
=
do
threads
<-
newTVarIO
mempty
threads
<-
newTVarIO
mempty
pure
Pool
{
pure
ResourceManager
{
configuration
,
configuration
,
threads
threads
}
}
dispose
Pool
::
Pool
->
IO
()
dispose
ResourceManager
::
ResourceManager
->
IO
()
-- TODO resource management
-- TODO resource management
dispose
Pool
=
const
(
pure
()
)
dispose
ResourceManager
=
const
(
pure
()
)
This diff is collapsed.
Click to expand it.
src/Quasar/Disposable.hs
+
1
−
1
View file @
21fbd1e0
...
@@ -19,7 +19,7 @@ class IsDisposable a where
...
@@ -19,7 +19,7 @@ class IsDisposable a where
-- | Dispose a resource in the IO monad.
-- | Dispose a resource in the IO monad.
disposeIO
::
a
->
IO
()
disposeIO
::
a
->
IO
()
disposeIO
=
runAsyncIO
.
dispose
disposeIO
=
withDefaultResourceManager
.
dispose
toDisposable
::
a
->
Disposable
toDisposable
::
a
->
Disposable
toDisposable
=
mkDisposable
.
dispose
toDisposable
=
mkDisposable
.
dispose
...
...
This diff is collapsed.
Click to expand it.
test/Quasar/AsyncSpec.hs
+
8
−
8
View file @
21fbd1e0
...
@@ -26,46 +26,46 @@ spec = parallel $ do
...
@@ -26,46 +26,46 @@ spec = parallel $ do
describe
"AsyncIO"
$
do
describe
"AsyncIO"
$
do
it
"binds pure operations"
$
do
it
"binds pure operations"
$
do
runAsyncIO
(
pure
()
>>=
\
()
->
pure
()
)
withDefaultResourceManager
(
pure
()
>>=
\
()
->
pure
()
)
it
"binds IO actions"
$
do
it
"binds IO actions"
$
do
m1
<-
newEmptyMVar
m1
<-
newEmptyMVar
m2
<-
newEmptyMVar
m2
<-
newEmptyMVar
runAsyncIO
(
liftIO
(
putMVar
m1
()
)
>>=
\
()
->
liftIO
(
putMVar
m2
()
))
withDefaultResourceManager
(
liftIO
(
putMVar
m1
()
)
>>=
\
()
->
liftIO
(
putMVar
m2
()
))
tryTakeMVar
m1
`
shouldReturn
`
Just
()
tryTakeMVar
m1
`
shouldReturn
`
Just
()
tryTakeMVar
m2
`
shouldReturn
`
Just
()
tryTakeMVar
m2
`
shouldReturn
`
Just
()
it
"can continue after awaiting an already finished operation"
$
do
it
"can continue after awaiting an already finished operation"
$
do
runAsyncIO
(
await
=<<
async
(
pure
42
::
AsyncIO
Int
))
`
shouldReturn
`
42
withDefaultResourceManager
(
await
=<<
async
(
pure
42
::
AsyncIO
Int
))
`
shouldReturn
`
42
it
"can fmap the result of an already finished async"
$
do
it
"can fmap the result of an already finished async"
$
do
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
putAsyncVar_
avar
()
putAsyncVar_
avar
()
runAsyncIO
(
id
<$>
await
avar
)
withDefaultResourceManager
(
id
<$>
await
avar
)
it
"can fmap the result of an async that is completed later"
$
do
it
"can fmap the result of an async that is completed later"
$
do
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
void
$
forkIO
$
do
void
$
forkIO
$
do
threadDelay
100000
threadDelay
100000
putAsyncVar_
avar
()
putAsyncVar_
avar
()
runAsyncIO
(
id
<$>
await
avar
)
withDefaultResourceManager
(
id
<$>
await
avar
)
it
"can bind the result of an already finished async"
$
do
it
"can bind the result of an already finished async"
$
do
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
putAsyncVar_
avar
()
putAsyncVar_
avar
()
runAsyncIO
(
await
avar
>>=
pure
)
withDefaultResourceManager
(
await
avar
>>=
pure
)
it
"can bind the result of an async that is completed later"
$
do
it
"can bind the result of an async that is completed later"
$
do
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
avar
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
void
$
forkIO
$
do
void
$
forkIO
$
do
threadDelay
100000
threadDelay
100000
putAsyncVar_
avar
()
putAsyncVar_
avar
()
runAsyncIO
(
await
avar
>>=
pure
)
withDefaultResourceManager
(
await
avar
>>=
pure
)
it
"can terminate when encountering an asynchronous exception"
$
do
it
"can terminate when encountering an asynchronous exception"
$
do
never
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
never
<-
newAsyncVar
::
IO
(
AsyncVar
()
)
result
<-
timeout
100000
$
runAsyncIO
$
result
<-
timeout
100000
$
withDefaultResourceManager
$
-- Use bind to create an AsyncIOPlumbing, which is the interesting case that uses `uninterruptibleMask` when run
-- Use bind to create an AsyncIOPlumbing, which is the interesting case that uses `uninterruptibleMask` when run
await
never
>>=
pure
await
never
>>=
pure
result
`
shouldBe
`
Nothing
result
`
shouldBe
`
Nothing
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