on master help sign in
6 files changed+40−24
since splitsnapshots
unifiedsplit
Changelog.markdown+4 README.markdown+4 cabal.project+1−1 curryer.cabal → curryer-rpc.cabal+9−8 src/Network/RPC/Curryer/Server.hs+21−14 test/Curryer/Test/Basic.hs+1−1
Changelog.markdownnew file+4
@@ -0,0 +1,4 @@
1+# v0.1 (2020-12-27)
2+
3+* initial release to support [Project:M36](https://github.com/agentm/project-m36)
4+
README.markdown+4
@@ -11,6 +11,10 @@ Curryer is inspired by the now unmaintained [distributed-process](https://hackag
1111* timeouts
1212* leverages [winery](https://hackage.haskell.org/package/winery) for high-performance serialization
1313
14+## Requirements
15+
16+* GHC 8.6+
17+
1418## Code Example
1519
1620[Server](https://github.com/agentm/curryer/examples/SimpleKeyValueServer.hs):
cabal.project+1−1
@@ -3,4 +3,4 @@ packages: .
33source-repository-package
44 type: git
55 location: https://github.com/composewell/streamly.git
6− tag: ac3af8749194f1788704dda8667d0b3807075cc2
6+ tag: 4e48b52e8709390a47e99c107ebed2d1c9420076
curryer.cabal → curryer-rpc.cabalrenamed+9−8
@@ -1,4 +1,4 @@
1−Name: curryer
1+Name: curryer-rpc
22Version: 0.1
33License: PublicDomain
44Build-Type: Simple
@@ -11,14 +11,14 @@ Maintainer: agentm@themactionfaction.com
1111Cabal-Version: >= 1.10
1212Synopsis: Fast, Haskell RPC
1313Description: Haskell-to-Haskell RPC using Winery serialization.
14−Extra-Source-Files: Changelog.markdown README.markdown
14+Extra-Source-Files: Changelog.markdown README.markdown cabal.project
1515
1616Source-Repository head
1717 Type: git
1818 location: https://github.com/agentm/curryer
1919
2020Library
21− Build-Depends: base >= 4.8
21+ Build-Depends: base >= 4.12 && < 4.15
2222 , winery
2323 , bytestring
2424 , streamly >= 0.7.2
@@ -51,7 +51,7 @@ Test-Suite test
5151 build-depends: tasty
5252 , tasty-hunit
5353 , base
54− , curryer
54+ , curryer-rpc
5555 , winery
5656 , network
5757 , async
@@ -61,21 +61,22 @@ Test-Suite test
6161Benchmark perf
6262 Default-Language: Haskell2010
6363 Default-Extensions: OverloadedStrings
64− Build-Depends: base, criterion, curryer, network, winery, async, bytestring
64+ Build-Depends: base, criterion, curryer-rpc, network, winery, async, bytestring
6565
6666 Main-Is: Basic.hs
6767 Type: exitcode-stdio-1.0
68− GHC-Options: -Wall -threaded -rtsopts -fprof-auto
68+ GHC-Options: -Wall -threaded -rtsopts
69+ -- -fprof-auto
6970 HS-Source-Dirs: ./bench
7071
7172Executable SimpleKeyValueServer
72− Build-Depends: stm-containers, stm, base, curryer, winery
73+ Build-Depends: stm-containers, stm, base, curryer-rpc, winery
7374 Main-Is: examples/SimpleKeyValueServer.hs
7475 GHC-Options: -Wall -threaded
7576 Default-Language: Haskell2010
7677
7778Executable SimpleKeyValueClient
78− Build-Depends: stm, base, curryer, winery, optparse-generic
79+ Build-Depends: stm, base, curryer-rpc, winery, optparse-generic
7980 Main-Is: examples/SimpleKeyValueClient.hs
8081 GHC-Options: -Wall -threaded
8182 Default-Language: Haskell2010
src/Network/RPC/Curryer/Server.hs+21−14
@@ -1,4 +1,4 @@
1−{-# LANGUAGE DerivingVia, DeriveGeneric, RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, OverloadedStrings, GeneralizedNewtypeDeriving, CPP, ExistentialQuantification, StandaloneDeriving, GADTs #-}
1+{-# LANGUAGE DerivingVia, DeriveGeneric, RankNTypes, ScopedTypeVariables, MultiParamTypeClasses, OverloadedStrings, GeneralizedNewtypeDeriving, CPP, ExistentialQuantification, StandaloneDeriving, GADTs, RecordWildCards #-}
22{-# OPTIONS_GHC -fno-warn-orphans #-}
33{- HLINT ignore "Use lambda-case" -}
44module Network.RPC.Curryer.Server where
@@ -35,13 +35,13 @@ import Data.Hashable
3535import System.Timeout
3636import qualified Network.ByteOrder as BO
3737
38+
3839-- for toArrayS conversion
39−{-import qualified Data.ByteString.Internal as BSI
40−import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as SA
41−import Foreign.ForeignPtr (plusForeignPtr)
40+import qualified Data.ByteString.Internal as BSI
41+import qualified Streamly.Internal.Data.Array.Storable.Foreign.Types as Arr
4242import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)
43−import GHC.Ptr (plusPtr)
44−-}
43+import GHC.Ptr (minusPtr)
44+
4545-- define CURRYER_SHOW_BYTES 1
4646#if CURRYER_SHOW_BYTES == 1
4747import Debug.Trace
@@ -111,13 +111,14 @@ data ConnectionState a = ConnectionState {
111111 connectionSocket :: Locking Socket
112112 }
113113
114−-- | Used by server-side request handlers to send additional messages to the client. This is useful for sending asynchronous responses to the client outside of the normal request-response flow.
115−sendMessage :: Serialise a => ConnectionState s -> a -> IO ()
116−sendMessage sState msg = do
114+-- | Used by server-side request handlers to send additional messages to the client. This is useful for sending asynchronous responses to the client outside of the normal request-response flow. The locking socket can be found in the ConnectionState when a request handler is called.
115+sendMessage :: Serialise a => Locking Socket -> a -> IO ()
116+sendMessage lockSock msg = do
117117 requestID <- UUID <$> UUIDBase.nextRandom
118118 let env =
119− Envelope (fingerprint msg) (RequestMessage 0) requestID (serialise msg)
120− sendEnvelope env (connectionSocket sState)
119+ Envelope (fingerprint msg) (RequestMessage timeout') requestID (serialise msg)
120+ timeout' = 0
121+ sendEnvelope env lockSock
121122
122123--avoid orphan instance
123124newtype UUID = UUID { _unUUID :: UUIDBase.UUID }
@@ -158,8 +159,7 @@ localHostAddr = (127,0,0,1)
158159-- Each message is length-prefixed by a 32-bit unsigned length.
159160envelopeP :: Parser IO Word8 Envelope
160161envelopeP = do
161− let s = FL.toList
162− msgTypeP = (P.satisfy (== 0) *>
162+ let msgTypeP = (P.satisfy (== 0) *>
163163 (RequestMessage . fromIntegral <$> word32P)) `P.alt`
164164 (P.satisfy (== 1) $> ResponseMessage) `P.alt`
165165 (P.satisfy (== 2) $> TimeoutResponseMessage) `P.alt`
@@ -170,7 +170,7 @@ envelopeP = do
170170 if c == 0 then
171171 pure BS.empty
172172 else
173− BS.pack <$> P.takeEQ c s
173+ fromArray <$> P.takeEQ c (Arr.writeN c)
174174 Envelope <$> fingerprintP <*> msgTypeP <*> uuidP <*> lenPrefixedByteStringP
175175
176176encodeEnvelope :: Envelope -> BS.ByteString
@@ -333,3 +333,10 @@ sendEnvelope envelope sockLock = do
333333fingerprint :: Typeable a => a -> Fingerprint
334334fingerprint = typeRepFingerprint . typeOf
335335
336+fromArray :: Arr.Array Word8 -> BSI.ByteString
337+fromArray Arr.Array {..}
338+ | aLen == 0 = mempty
339+ | otherwise = BSI.PS aStart 0 aLen
340+ where
341+ aStartPtr = unsafeForeignPtrToPtr aStart
342+ aLen = aEnd `minusPtr` aStartPtr
test/Curryer/Test/Basic.hs+1−1
@@ -78,7 +78,7 @@ testServerRequestHandlers mAsyncMVar =
7878 maybe (pure ()) (\mvar -> putMVar mvar v) mAsyncMVar
7979 -- an async hello to the server generates an async hello to the client
8080 , AsyncRequestHandler $ \sState (AsyncHelloReq s) -> do
81− sendMessage sState (AsyncHelloReq s)
81+ sendMessage (connectionSocket sState) (AsyncHelloReq s)
8282
8383 , RequestHandler $ \_ (RoundtripStringReq s) -> pure s
8484 , RequestHandler $ \_ (DelayMicrosecondsReq ms) -> do