mirror of
https://github.com/unclechu/gRPC-haskell.git
synced 2024-11-22 19:19:42 +01:00
Plumb the MaxMetadataSize
parameter through to the server (#134)
* Plumb the `maxMetadataSize` parameter through to the server * Update `proto3-suite` dependency * Bump proto3-wire to 1.2.2 * Re-generate Echo.hs * Regenerate `Arithmetic.hs` * Derp * Fix codegen * Update the tests for new codegen * Patch-bump to `0.2.1` * Bump to `0.3.0` to indicate a breaking API change... Suggested by @intractable.
This commit is contained in:
parent
e1091b9c0d
commit
112777023f
11 changed files with 55 additions and 48 deletions
|
@ -1,5 +1,5 @@
|
||||||
name: grpc-haskell-core
|
name: grpc-haskell-core
|
||||||
version: 0.2.0
|
version: 0.3.0
|
||||||
synopsis: Haskell implementation of gRPC layered on shared C library.
|
synopsis: Haskell implementation of gRPC layered on shared C library.
|
||||||
homepage: https://github.com/awakenetworks/gRPC-haskell
|
homepage: https://github.com/awakenetworks/gRPC-haskell
|
||||||
license: Apache-2.0
|
license: Apache-2.0
|
||||||
|
|
13
examples/echo/echo-hs/Echo.hs
generated
13
examples/echo/echo-hs/Echo.hs
generated
|
@ -4,12 +4,12 @@
|
||||||
{-# LANGUAGE GADTs #-}
|
{-# LANGUAGE GADTs #-}
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
|
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
|
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-missing-export-lists #-}
|
||||||
|
|
||||||
-- | Generated by Haskell protocol buffer compiler. DO NOT EDIT!
|
-- | Generated by Haskell protocol buffer compiler. DO NOT EDIT!
|
||||||
|
|
||||||
module Echo where
|
module Echo where
|
||||||
import qualified Prelude as Hs
|
import qualified Prelude as Hs
|
||||||
import qualified Proto3.Suite.Class as HsProtobuf
|
import qualified Proto3.Suite.Class as HsProtobuf
|
||||||
|
@ -52,7 +52,7 @@ echoServer ::
|
||||||
echoServer Echo{echoDoEcho = echoDoEcho}
|
echoServer Echo{echoDoEcho = echoDoEcho}
|
||||||
(ServiceOptions serverHost serverPort useCompression
|
(ServiceOptions serverHost serverPort useCompression
|
||||||
userAgentPrefix userAgentSuffix initialMetadata sslConfig logger
|
userAgentPrefix userAgentSuffix initialMetadata sslConfig logger
|
||||||
serverMaxReceiveMessageLength)
|
serverMaxReceiveMessageLength serverMaxMetadataSize)
|
||||||
= (HsGRPC.serverLoop
|
= (HsGRPC.serverLoop
|
||||||
HsGRPC.defaultOptions{HsGRPC.optNormalHandlers =
|
HsGRPC.defaultOptions{HsGRPC.optNormalHandlers =
|
||||||
[(HsGRPC.UnaryHandler (HsGRPC.MethodName "/echo.Echo/DoEcho")
|
[(HsGRPC.UnaryHandler (HsGRPC.MethodName "/echo.Echo/DoEcho")
|
||||||
|
@ -65,7 +65,8 @@ echoServer Echo{echoDoEcho = echoDoEcho}
|
||||||
optUserAgentSuffix = userAgentSuffix,
|
optUserAgentSuffix = userAgentSuffix,
|
||||||
optInitialMetadata = initialMetadata, optSSLConfig = sslConfig,
|
optInitialMetadata = initialMetadata, optSSLConfig = sslConfig,
|
||||||
optLogger = logger,
|
optLogger = logger,
|
||||||
optMaxReceiveMessageLength = serverMaxReceiveMessageLength})
|
optMaxReceiveMessageLength = serverMaxReceiveMessageLength,
|
||||||
|
optMaxMetadataSize = serverMaxMetadataSize})
|
||||||
|
|
||||||
echoClient ::
|
echoClient ::
|
||||||
HsGRPC.Client ->
|
HsGRPC.Client ->
|
||||||
|
|
13
examples/tutorial/Arithmetic.hs
generated
13
examples/tutorial/Arithmetic.hs
generated
|
@ -4,12 +4,12 @@
|
||||||
{-# LANGUAGE GADTs #-}
|
{-# LANGUAGE GADTs #-}
|
||||||
{-# LANGUAGE TypeApplications #-}
|
{-# LANGUAGE TypeApplications #-}
|
||||||
{-# LANGUAGE OverloadedStrings #-}
|
{-# LANGUAGE OverloadedStrings #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
|
{-# OPTIONS_GHC -fno-warn-unused-imports #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
{-# OPTIONS_GHC -fno-warn-name-shadowing #-}
|
||||||
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
|
{-# OPTIONS_GHC -fno-warn-unused-matches #-}
|
||||||
|
{-# OPTIONS_GHC -fno-warn-missing-export-lists #-}
|
||||||
|
|
||||||
-- | Generated by Haskell protocol buffer compiler. DO NOT EDIT!
|
-- | Generated by Haskell protocol buffer compiler. DO NOT EDIT!
|
||||||
|
|
||||||
module Arithmetic where
|
module Arithmetic where
|
||||||
import qualified Prelude as Hs
|
import qualified Prelude as Hs
|
||||||
import qualified Proto3.Suite.Class as HsProtobuf
|
import qualified Proto3.Suite.Class as HsProtobuf
|
||||||
|
@ -63,7 +63,7 @@ arithmeticServer
|
||||||
arithmeticRunningSum = arithmeticRunningSum}
|
arithmeticRunningSum = arithmeticRunningSum}
|
||||||
(ServiceOptions serverHost serverPort useCompression
|
(ServiceOptions serverHost serverPort useCompression
|
||||||
userAgentPrefix userAgentSuffix initialMetadata sslConfig logger
|
userAgentPrefix userAgentSuffix initialMetadata sslConfig logger
|
||||||
serverMaxReceiveMessageLength)
|
serverMaxReceiveMessageLength serverMaxMetadataSize)
|
||||||
= (HsGRPC.serverLoop
|
= (HsGRPC.serverLoop
|
||||||
HsGRPC.defaultOptions{HsGRPC.optNormalHandlers =
|
HsGRPC.defaultOptions{HsGRPC.optNormalHandlers =
|
||||||
[(HsGRPC.UnaryHandler
|
[(HsGRPC.UnaryHandler
|
||||||
|
@ -81,7 +81,8 @@ arithmeticServer
|
||||||
optUserAgentSuffix = userAgentSuffix,
|
optUserAgentSuffix = userAgentSuffix,
|
||||||
optInitialMetadata = initialMetadata, optSSLConfig = sslConfig,
|
optInitialMetadata = initialMetadata, optSSLConfig = sslConfig,
|
||||||
optLogger = logger,
|
optLogger = logger,
|
||||||
optMaxReceiveMessageLength = serverMaxReceiveMessageLength})
|
optMaxReceiveMessageLength = serverMaxReceiveMessageLength,
|
||||||
|
optMaxMetadataSize = serverMaxMetadataSize})
|
||||||
|
|
||||||
arithmeticClient ::
|
arithmeticClient ::
|
||||||
HsGRPC.Client ->
|
HsGRPC.Client ->
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
name: grpc-haskell
|
name: grpc-haskell
|
||||||
version: 0.2.0
|
version: 0.3.0
|
||||||
synopsis: Haskell implementation of gRPC layered on shared C library.
|
synopsis: Haskell implementation of gRPC layered on shared C library.
|
||||||
homepage: https://github.com/awakenetworks/gRPC-haskell
|
homepage: https://github.com/awakenetworks/gRPC-haskell
|
||||||
license: Apache-2.0
|
license: Apache-2.0
|
||||||
|
@ -29,9 +29,9 @@ library
|
||||||
build-depends:
|
build-depends:
|
||||||
base >=4.8 && <5.0
|
base >=4.8 && <5.0
|
||||||
, bytestring ==0.10.*
|
, bytestring ==0.10.*
|
||||||
, proto3-suite >=0.4.1
|
, proto3-suite >=0.4.3
|
||||||
, proto3-wire >=1.2.0
|
, proto3-wire >=1.2.2
|
||||||
, grpc-haskell-core >=0.2.0
|
, grpc-haskell-core >=0.2.1
|
||||||
, async >=2.1 && <2.3
|
, async >=2.1 && <2.3
|
||||||
, managed >= 1.0.5
|
, managed >= 1.0.5
|
||||||
|
|
||||||
|
|
|
@ -2,19 +2,20 @@
|
||||||
, base64-bytestring, binary, bytestring, cereal, containers
|
, base64-bytestring, binary, bytestring, cereal, containers
|
||||||
, contravariant, deepseq, doctest, fetchgit, filepath, foldl
|
, contravariant, deepseq, doctest, fetchgit, filepath, foldl
|
||||||
, generic-arbitrary, hashable, haskell-src
|
, generic-arbitrary, hashable, haskell-src
|
||||||
, insert-ordered-containers, lens, mtl, neat-interpolation
|
, insert-ordered-containers, lens, lib, mtl, neat-interpolation
|
||||||
, optparse-applicative, optparse-generic, parsec, parsers, pretty
|
, optparse-applicative, optparse-generic, parsec, parsers, pretty
|
||||||
, pretty-show, proto3-wire, QuickCheck, quickcheck-instances
|
, pretty-show, proto3-wire, QuickCheck, quickcheck-instances
|
||||||
, range-set-list, safe, stdenv, swagger2, system-filepath, tasty
|
, range-set-list, safe, swagger2, system-filepath, tasty
|
||||||
, tasty-hunit, tasty-quickcheck, text, transformers, turtle, vector
|
, tasty-hunit, tasty-quickcheck, text, time, transformers, turtle
|
||||||
|
, vector
|
||||||
}:
|
}:
|
||||||
mkDerivation {
|
mkDerivation {
|
||||||
pname = "proto3-suite";
|
pname = "proto3-suite";
|
||||||
version = "0.4.2.0";
|
version = "0.4.3";
|
||||||
src = fetchgit {
|
src = fetchgit {
|
||||||
url = "https://github.com/awakesecurity/proto3-suite.git";
|
url = "https://github.com/awakesecurity/proto3-suite.git";
|
||||||
sha256 = "0mpy35r6qd1v5sixhy2lqcn5x81rfj4dc079g1kpa4fb1f23dbha";
|
sha256 = "0bjqczi6wddxv0n7qmfbrr19ajgq66xdkxx8vfcgbmv8ygma3vlw";
|
||||||
rev = "0af901f9ef3b9719e08eae4fab8fd700d6c8047a";
|
rev = "7af7d76dcf9cc71ddada3aa4a38abf46f65550ca";
|
||||||
fetchSubmodules = true;
|
fetchSubmodules = true;
|
||||||
};
|
};
|
||||||
isLibrary = true;
|
isLibrary = true;
|
||||||
|
@ -26,7 +27,7 @@ mkDerivation {
|
||||||
hashable haskell-src insert-ordered-containers lens mtl
|
hashable haskell-src insert-ordered-containers lens mtl
|
||||||
neat-interpolation parsec parsers pretty pretty-show proto3-wire
|
neat-interpolation parsec parsers pretty pretty-show proto3-wire
|
||||||
QuickCheck quickcheck-instances safe swagger2 system-filepath text
|
QuickCheck quickcheck-instances safe swagger2 system-filepath text
|
||||||
transformers turtle vector
|
time transformers turtle vector
|
||||||
];
|
];
|
||||||
executableHaskellDepends = [
|
executableHaskellDepends = [
|
||||||
base containers mtl optparse-applicative optparse-generic
|
base containers mtl optparse-applicative optparse-generic
|
||||||
|
@ -38,6 +39,6 @@ mkDerivation {
|
||||||
proto3-wire QuickCheck swagger2 tasty tasty-hunit tasty-quickcheck
|
proto3-wire QuickCheck swagger2 tasty tasty-hunit tasty-quickcheck
|
||||||
text transformers turtle vector
|
text transformers turtle vector
|
||||||
];
|
];
|
||||||
description = "A low level library for writing out data in the Protocol Buffers wire format";
|
description = "A higher-level API to the proto3-wire library";
|
||||||
license = stdenv.lib.licenses.asl20;
|
license = lib.licenses.asl20;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,12 @@
|
||||||
{ mkDerivation, base, bytestring, cereal, containers, deepseq
|
{ mkDerivation, base, bytestring, cereal, containers, deepseq
|
||||||
, doctest, fetchgit, ghc-prim, hashable, parameterized, primitive
|
, doctest, ghc-prim, hashable, lib, parameterized, primitive
|
||||||
, QuickCheck, safe, stdenv, tasty, tasty-hunit, tasty-quickcheck
|
, QuickCheck, safe, tasty, tasty-hunit, tasty-quickcheck, text
|
||||||
, text, transformers, unordered-containers, vector
|
, transformers, unordered-containers, vector
|
||||||
}:
|
}:
|
||||||
mkDerivation {
|
mkDerivation {
|
||||||
pname = "proto3-wire";
|
pname = "proto3-wire";
|
||||||
version = "1.2.0";
|
version = "1.2.2";
|
||||||
src = fetchgit {
|
sha256 = "8d409536a89a0187f0576711966d2ef45d43acab7b6a3a1c5ee12f6d01adbfb9";
|
||||||
url = "https://github.com/awakesecurity/proto3-wire.git";
|
|
||||||
sha256 = "062b05ab8icwjxaqrh3wmg8s26m620pigqj3dj6rdx9qas1cq6mi";
|
|
||||||
rev = "d92ec32ef0f15842b07fb226d8f2d15f36c5fb20";
|
|
||||||
fetchSubmodules = true;
|
|
||||||
};
|
|
||||||
libraryHaskellDepends = [
|
libraryHaskellDepends = [
|
||||||
base bytestring cereal containers deepseq ghc-prim hashable
|
base bytestring cereal containers deepseq ghc-prim hashable
|
||||||
parameterized primitive QuickCheck safe text transformers
|
parameterized primitive QuickCheck safe text transformers
|
||||||
|
@ -22,5 +17,5 @@ mkDerivation {
|
||||||
tasty-quickcheck text transformers vector
|
tasty-quickcheck text transformers vector
|
||||||
];
|
];
|
||||||
description = "A low-level implementation of the Protocol Buffers (version 3) wire format";
|
description = "A low-level implementation of the Protocol Buffers (version 3) wire format";
|
||||||
license = stdenv.lib.licenses.asl20;
|
license = lib.licenses.asl20;
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,7 +80,9 @@ data ServiceOptions = ServiceOptions
|
||||||
, logger :: String -> IO ()
|
, logger :: String -> IO ()
|
||||||
-- ^ Logging function to use to log errors in handling calls.
|
-- ^ Logging function to use to log errors in handling calls.
|
||||||
, serverMaxReceiveMessageLength :: Maybe Natural
|
, serverMaxReceiveMessageLength :: Maybe Natural
|
||||||
-- ^ Maximum length (in bytes) that the service may receive in a single message
|
-- ^ Maximum length (in bytes) that the service may receive in a single message.
|
||||||
|
, serverMaxMetadataSize :: Maybe Natural
|
||||||
|
-- ^ Maximum metadata size (in bytes) that the service may receive in a single request.
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultServiceOptions :: ServiceOptions
|
defaultServiceOptions :: ServiceOptions
|
||||||
|
@ -95,6 +97,7 @@ defaultServiceOptions = ServiceOptions
|
||||||
, Network.GRPC.HighLevel.Generated.sslConfig = Nothing
|
, Network.GRPC.HighLevel.Generated.sslConfig = Nothing
|
||||||
, Network.GRPC.HighLevel.Generated.logger = hPutStrLn stderr
|
, Network.GRPC.HighLevel.Generated.logger = hPutStrLn stderr
|
||||||
, Network.GRPC.HighLevel.Generated.serverMaxReceiveMessageLength = Nothing
|
, Network.GRPC.HighLevel.Generated.serverMaxReceiveMessageLength = Nothing
|
||||||
|
, Network.GRPC.HighLevel.Generated.serverMaxMetadataSize = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
withGRPCClient :: ClientConfig -> (Client -> IO a) -> IO a
|
withGRPCClient :: ClientConfig -> (Client -> IO a) -> IO a
|
||||||
|
|
|
@ -227,6 +227,9 @@ data ServerOptions = ServerOptions
|
||||||
, optLogger :: String -> IO ()
|
, optLogger :: String -> IO ()
|
||||||
-- ^ Logging function to use to log errors in handling calls.
|
-- ^ Logging function to use to log errors in handling calls.
|
||||||
, optMaxReceiveMessageLength :: Maybe Natural
|
, optMaxReceiveMessageLength :: Maybe Natural
|
||||||
|
-- ^ Maximum length (in bytes) that the service may receive in a single message.
|
||||||
|
, optMaxMetadataSize :: Maybe Natural
|
||||||
|
-- ^ Maximum metadata size (in bytes) that the service may receive in a single request.
|
||||||
}
|
}
|
||||||
|
|
||||||
defaultOptions :: ServerOptions
|
defaultOptions :: ServerOptions
|
||||||
|
@ -244,6 +247,7 @@ defaultOptions = ServerOptions
|
||||||
, optSSLConfig = Nothing
|
, optSSLConfig = Nothing
|
||||||
, optLogger = hPutStrLn stderr
|
, optLogger = hPutStrLn stderr
|
||||||
, optMaxReceiveMessageLength = Nothing
|
, optMaxReceiveMessageLength = Nothing
|
||||||
|
, optMaxMetadataSize = Nothing
|
||||||
}
|
}
|
||||||
|
|
||||||
serverLoop :: ServerOptions -> IO ()
|
serverLoop :: ServerOptions -> IO ()
|
||||||
|
|
|
@ -124,5 +124,7 @@ serverLoop ServerOptions{..} =
|
||||||
]
|
]
|
||||||
++
|
++
|
||||||
foldMap (pure . MaxReceiveMessageLength) optMaxReceiveMessageLength
|
foldMap (pure . MaxReceiveMessageLength) optMaxReceiveMessageLength
|
||||||
|
++
|
||||||
|
foldMap (pure . MaxMetadataSize) optMaxMetadataSize
|
||||||
, sslConfig = optSSLConfig
|
, sslConfig = optSSLConfig
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import Test.Tasty.HUnit ((@?=), assertFailure, testCase)
|
||||||
testNormalCall client = testCase "Normal call" $
|
testNormalCall client = testCase "Normal call" $
|
||||||
do randoms <- fromList <$> replicateM 1000 (randomRIO (1, 1000))
|
do randoms <- fromList <$> replicateM 1000 (randomRIO (1, 1000))
|
||||||
let req = SimpleServiceRequest "NormalRequest" randoms
|
let req = SimpleServiceRequest "NormalRequest" randoms
|
||||||
res <- simpleServiceNormalCall client
|
res <- simpleServicenormalCall client
|
||||||
(ClientNormalRequest req 10 mempty)
|
(ClientNormalRequest req 10 mempty)
|
||||||
case res of
|
case res of
|
||||||
ClientErrorResponse err -> assertFailure ("ClientErrorResponse: " <> show err)
|
ClientErrorResponse err -> assertFailure ("ClientErrorResponse: " <> show err)
|
||||||
|
@ -48,7 +48,7 @@ testNormalCall client = testCase "Normal call" $
|
||||||
testClientStreamingCall client = testCase "Client-streaming call" $
|
testClientStreamingCall client = testCase "Client-streaming call" $
|
||||||
do iterationCount <- randomRIO (5, 50)
|
do iterationCount <- randomRIO (5, 50)
|
||||||
v <- newEmptyMVar
|
v <- newEmptyMVar
|
||||||
res <- simpleServiceClientStreamingCall client . ClientWriterRequest 10 mempty $ \send ->
|
res <- simpleServiceclientStreamingCall client . ClientWriterRequest 10 mempty $ \send ->
|
||||||
do (finalName, totalSum) <-
|
do (finalName, totalSum) <-
|
||||||
fmap ((mconcat *** (sum . mconcat)) . unzip) .
|
fmap ((mconcat *** (sum . mconcat)) . unzip) .
|
||||||
replicateM iterationCount $
|
replicateM iterationCount $
|
||||||
|
@ -86,7 +86,7 @@ testServerStreamingCall client = testCase "Server-streaming call" $
|
||||||
do response @?= "Test"
|
do response @?= "Test"
|
||||||
num @?= expNum
|
num @?= expNum
|
||||||
checkResults nums recv
|
checkResults nums recv
|
||||||
res <- simpleServiceServerStreamingCall client $
|
res <- simpleServiceserverStreamingCall client $
|
||||||
ClientReaderRequest (SimpleServiceRequest "Test" (fromList nums)) 10 mempty
|
ClientReaderRequest (SimpleServiceRequest "Test" (fromList nums)) 10 mempty
|
||||||
(\_ _ -> checkResults nums)
|
(\_ _ -> checkResults nums)
|
||||||
case res of
|
case res of
|
||||||
|
@ -113,7 +113,7 @@ testBiDiStreamingCall client = testCase "Bidi-streaming call" $
|
||||||
|
|
||||||
iterations <- randomRIO (50, 500)
|
iterations <- randomRIO (50, 500)
|
||||||
|
|
||||||
res <- simpleServiceBiDiStreamingCall client $
|
res <- simpleServicebiDiStreamingCall client $
|
||||||
ClientBiDiRequest 10 mempty (\_ _ -> handleRequests iterations)
|
ClientBiDiRequest 10 mempty (\_ _ -> handleRequests iterations)
|
||||||
case res of
|
case res of
|
||||||
ClientErrorResponse err -> assertFailure ("ClientErrorResponse: " <> show err)
|
ClientErrorResponse err -> assertFailure ("ClientErrorResponse: " <> show err)
|
||||||
|
@ -132,4 +132,4 @@ main = do
|
||||||
, testClientStreamingCall service
|
, testClientStreamingCall service
|
||||||
, testServerStreamingCall service
|
, testServerStreamingCall service
|
||||||
, testBiDiStreamingCall service ]) `finally`
|
, testBiDiStreamingCall service ]) `finally`
|
||||||
(simpleServiceDone service (ClientNormalRequest SimpleServiceDone 10 mempty))
|
(simpleServicedone service (ClientNormalRequest SimpleServiceDone 10 mempty))
|
||||||
|
|
|
@ -64,11 +64,11 @@ main :: IO ()
|
||||||
main = do exitVar <- newEmptyMVar
|
main = do exitVar <- newEmptyMVar
|
||||||
|
|
||||||
forkIO $ simpleServiceServer (SimpleService
|
forkIO $ simpleServiceServer (SimpleService
|
||||||
{ simpleServiceDone = handleDone exitVar
|
{ simpleServicedone = handleDone exitVar
|
||||||
, simpleServiceNormalCall = handleNormalCall
|
, simpleServicenormalCall = handleNormalCall
|
||||||
, simpleServiceClientStreamingCall = handleClientStreamingCall
|
, simpleServiceclientStreamingCall = handleClientStreamingCall
|
||||||
, simpleServiceServerStreamingCall = handleServerStreamingCall
|
, simpleServiceserverStreamingCall = handleServerStreamingCall
|
||||||
, simpleServiceBiDiStreamingCall = handleBiDiStreamingCall })
|
, simpleServicebiDiStreamingCall = handleBiDiStreamingCall })
|
||||||
defaultServiceOptions
|
defaultServiceOptions
|
||||||
|
|
||||||
takeMVar exitVar
|
takeMVar exitVar
|
||||||
|
|
Loading…
Reference in a new issue