1
+ {-# LANGUAGE CPP #-}
1
2
{-# LANGUAGE DataKinds #-}
2
3
{-# LANGUAGE DefaultSignatures #-}
3
4
{-# LANGUAGE FlexibleContexts #-}
@@ -16,8 +17,10 @@ module Control.Monad.Class.MonadAsync
16
17
, AsyncCancelled (.. )
17
18
, ExceptionInLinkedThread (.. )
18
19
, link
19
- , linkTo
20
20
, linkOnly
21
+ , link2
22
+ , link2Only
23
+ , linkTo
21
24
, linkToOnly
22
25
, mapConcurrently
23
26
, forConcurrently
@@ -365,11 +368,12 @@ instance MonadAsync IO where
365
368
-- We don't use the implementation of linking from 'Control.Concurrent.Async'
366
369
-- directly because:
367
370
--
368
- -- 1. We need a generalized form of linking that links an async to an arbitrary
369
- -- thread ('linkTo')
370
- -- 2. If we /did/ use the real implementation, then the mock implementation and
371
+ -- 1. If we /did/ use the real implementation, then the mock implementation and
371
372
-- the real implementation would not be able to throw the same exception,
372
373
-- because the exception type used by the real implementation is
374
+ -- 2. We need a generalized form of linking that links an async to an arbitrary
375
+ -- thread ('linkTo'), which is exposed only if cabal flag `+non-standard` is
376
+ -- used.
373
377
--
374
378
-- > data ExceptionInLinkedThread =
375
379
-- > forall a . ExceptionInLinkedThread (Async a) SomeException
@@ -399,6 +403,35 @@ instance Exception ExceptionInLinkedThread where
399
403
fromException = E. asyncExceptionFromException
400
404
toException = E. asyncExceptionToException
401
405
406
+ link :: (MonadAsync m , MonadFork m , MonadMask m )
407
+ => Async m a -> m ()
408
+ link = linkOnly (not . isCancel)
409
+
410
+ linkOnly :: forall m a . (MonadAsync m , MonadFork m , MonadMask m )
411
+ => (SomeException -> Bool ) -> Async m a -> m ()
412
+ linkOnly shouldThrow a = do
413
+ me <- myThreadId
414
+ linkToOnly me shouldThrow a
415
+
416
+ link2 :: (MonadAsync m , MonadFork m , MonadMask m )
417
+ => Async m a -> Async m b -> m ()
418
+ link2 = link2Only (not . isCancel)
419
+
420
+ link2Only :: (MonadAsync m , MonadFork m , MonadMask m )
421
+ => (SomeException -> Bool ) -> Async m a -> Async m b -> m ()
422
+ link2Only shouldThrow left right =
423
+ void $ forkRepeat (" link2Only " <> show (tl, tr)) $ do
424
+ r <- waitEitherCatch left right
425
+ case r of
426
+ Left (Left e) | shouldThrow e ->
427
+ throwTo tr (ExceptionInLinkedThread (show tl) e)
428
+ Right (Left e) | shouldThrow e ->
429
+ throwTo tl (ExceptionInLinkedThread (show tr) e)
430
+ _ -> return ()
431
+ where
432
+ tl = asyncThreadId left
433
+ tr = asyncThreadId right
434
+
402
435
-- | Generalization of 'link' that links an async to an arbitrary thread.
403
436
linkTo :: (MonadAsync m , MonadFork m , MonadMask m )
404
437
=> ThreadId m -> Async m a -> m ()
@@ -420,16 +453,6 @@ linkToOnly tid shouldThrow a = do
420
453
exceptionInLinkedThread =
421
454
ExceptionInLinkedThread (show linkedThreadId)
422
455
423
- link :: (MonadAsync m , MonadFork m , MonadMask m )
424
- => Async m a -> m ()
425
- link = linkOnly (not . isCancel)
426
-
427
- linkOnly :: forall m a . (MonadAsync m , MonadFork m , MonadMask m )
428
- => (SomeException -> Bool ) -> Async m a -> m ()
429
- linkOnly shouldThrow a = do
430
- me <- myThreadId
431
- linkToOnly me shouldThrow a
432
-
433
456
isCancel :: SomeException -> Bool
434
457
isCancel e
435
458
| Just AsyncCancelled <- fromException e = True
@@ -457,6 +480,8 @@ newtype WrappedAsync r (m :: Type -> Type) a =
457
480
458
481
instance ( MonadAsync m
459
482
, MonadCatch (STM m )
483
+ , MonadFork m
484
+ , MonadMask m
460
485
) => MonadAsync (ReaderT r m ) where
461
486
type Async (ReaderT r m ) = WrappedAsync r m
462
487
asyncThreadId (WrappedAsync a) = asyncThreadId a
0 commit comments