| ... |
... |
@@ -29,6 +29,10 @@ module GHCi.Message |
|
29
|
29
|
, handleRemoteCall, handleRemoteTHCall
|
|
30
|
30
|
, RemoteProcessException(..), PipeClosedException(..)
|
|
31
|
31
|
|
|
|
32
|
+ , InboxId(..)
|
|
|
33
|
+ , newMailbox
|
|
|
34
|
+ , closeMailbox
|
|
|
35
|
+
|
|
32
|
36
|
, BreakModule
|
|
33
|
37
|
, BreakUnitId
|
|
34
|
38
|
, LoadedDLL
|
| ... |
... |
@@ -237,14 +241,16 @@ data Message a where |
|
237
|
241
|
-- they did, we have to serialize the value anyway, so we might
|
|
238
|
242
|
-- as well serialize it to force it.
|
|
239
|
243
|
RunTH
|
|
240
|
|
- :: RemoteRef (IORef QState)
|
|
|
244
|
+ :: InboxId -- ^ announce mailbox for related 'remoteTHCall's
|
|
|
245
|
+ -> RemoteRef (IORef QState)
|
|
241
|
246
|
-> HValueRef {- e.g. TH.Q TH.Exp -}
|
|
242
|
247
|
-> THResultType
|
|
243
|
248
|
-> Maybe TH.Loc
|
|
244
|
249
|
-> Message (QResult ByteString)
|
|
245
|
250
|
|
|
246
|
251
|
-- | Run the given mod finalizers.
|
|
247
|
|
- RunModFinalizers :: RemoteRef (IORef QState)
|
|
|
252
|
+ RunModFinalizers :: InboxId -- ^ announce mailbox for related 'remoteTHCall's
|
|
|
253
|
+ -> RemoteRef (IORef QState)
|
|
248
|
254
|
-> [RemoteRef (TH.Q ())]
|
|
249
|
255
|
-> Message (QResult ())
|
|
250
|
256
|
|
| ... |
... |
@@ -536,11 +542,13 @@ instance Binary THResultType |
|
536
|
542
|
-- StartTH message. A new one is created per module that GHC
|
|
537
|
543
|
-- typechecks.
|
|
538
|
544
|
data QState = QState
|
|
539
|
|
- { qsMap :: Map TypeRep Dynamic
|
|
|
545
|
+ { qsMap :: Map TypeRep Dynamic
|
|
540
|
546
|
-- ^ persistent data between splices in a module
|
|
541
|
|
- , qsLocation :: Maybe TH.Loc
|
|
|
547
|
+ , qsLocation :: Maybe TH.Loc
|
|
542
|
548
|
-- ^ location for current splice, if any
|
|
543
|
|
- , qsPipe :: Pipe
|
|
|
549
|
+ , qsInboxId :: InboxId
|
|
|
550
|
+ -- ^ the mailbox expecting requests for each RunTH-like run
|
|
|
551
|
+ , qsPipe :: Pipe
|
|
544
|
552
|
-- ^ pipe to communicate with GHC
|
|
545
|
553
|
}
|
|
546
|
554
|
instance Show QState where show _ = "<QState>"
|
| ... |
... |
@@ -623,9 +631,9 @@ getMessage = do |
|
623
|
631
|
29 -> Msg <$> (BreakpointStatus <$> get <*> get)
|
|
624
|
632
|
30 -> Msg <$> (GetBreakpointVar <$> get <*> get)
|
|
625
|
633
|
31 -> Msg <$> return StartTH
|
|
626
|
|
- 32 -> Msg <$> (RunModFinalizers <$> get <*> get)
|
|
|
634
|
+ 32 -> Msg <$> (RunModFinalizers <$> get <*> get <*> get)
|
|
627
|
635
|
33 -> Msg <$> (AddSptEntry <$> get <*> get)
|
|
628
|
|
- 34 -> Msg <$> (RunTH <$> get <*> get <*> get <*> get)
|
|
|
636
|
+ 34 -> Msg <$> (RunTH <$> get <*> get <*> get <*> get <*> get)
|
|
629
|
637
|
35 -> Msg <$> (GetClosure <$> get)
|
|
630
|
638
|
36 -> Msg <$> (Seq <$> get)
|
|
631
|
639
|
37 -> Msg <$> return RtsRevertCAFs
|
| ... |
... |
@@ -671,9 +679,9 @@ putMessage m = case m of |
|
671
|
679
|
BreakpointStatus arr ix -> putWord8 29 >> put arr >> put ix
|
|
672
|
680
|
GetBreakpointVar a b -> putWord8 30 >> put a >> put b
|
|
673
|
681
|
StartTH -> putWord8 31
|
|
674
|
|
- RunModFinalizers a b -> putWord8 32 >> put a >> put b
|
|
|
682
|
+ RunModFinalizers a b c -> putWord8 32 >> put a >> put b >> put c
|
|
675
|
683
|
AddSptEntry a b -> putWord8 33 >> put a >> put b
|
|
676
|
|
- RunTH st q loc ty -> putWord8 34 >> put st >> put q >> put loc >> put ty
|
|
|
684
|
+ RunTH ii st q loc ty -> putWord8 34 >> put ii >> put st >> put q >> put loc >> put ty
|
|
677
|
685
|
GetClosure a -> putWord8 35 >> put a
|
|
678
|
686
|
Seq a -> putWord8 36 >> put a
|
|
679
|
687
|
RtsRevertCAFs -> putWord8 37
|
| ... |
... |
@@ -774,7 +782,7 @@ closePipe Pipe{..} = do |
|
774
|
782
|
-- (blocks on the calling side, doesn't necessarily block the interpreter)
|
|
775
|
783
|
remoteCall :: Binary a => Pipe -> Message a -> IO a
|
|
776
|
784
|
remoteCall pipe msg = either throwIO (pure . runGet get)
|
|
777
|
|
- =<< remoteAsyncReq pipe (putMessage msg)
|
|
|
785
|
+ =<< remoteAsyncReq pipe (putMessage msg) Nothing
|
|
778
|
786
|
|
|
779
|
787
|
-- | Handle a single 'Message' request on the interpreter and reply to the host
|
|
780
|
788
|
handleRemoteCall :: Pipe
|
| ... |
... |
@@ -782,22 +790,28 @@ handleRemoteCall :: Pipe |
|
782
|
790
|
-> (forall a. (Show a, Binary a) => Message a -> IO a)
|
|
783
|
791
|
-> IO ()
|
|
784
|
792
|
handleRemoteCall pipe hook k =
|
|
785
|
|
- handleAsyncReq pipe getMessage \writer msg0 -> IO \s0 ->
|
|
|
793
|
+ handleAsyncReq pipe getMessage Nothing \writer msg0 -> IO \s0 ->
|
|
786
|
794
|
case hook msg0 |#> s0 of -- unIO for linearity checking
|
|
787
|
795
|
(# s1, Msg msg #) -> case k msg |#> s1 of
|
|
788
|
796
|
(# s2, a #) -> writeReply writer (put a) |#> s2
|
|
789
|
797
|
|
|
790
|
798
|
-- | Send a 'THMessage' request to the host and wait for a reply from the host
|
|
791
|
|
--- to the interpreter
|
|
792
|
|
-remoteTHCall :: Binary a => Pipe -> THMessage a -> IO a
|
|
793
|
|
-remoteTHCall pipe msg = either throwIO (pure . runGet get)
|
|
794
|
|
- =<< remoteAsyncReq pipe (putTHMessage msg)
|
|
|
799
|
+-- to the interpreter.
|
|
|
800
|
+--
|
|
|
801
|
+-- A 'THMessage' request from the interpreter is always associated with a
|
|
|
802
|
+-- 'RunTH'-like run. Since the host may have many in-flight TH runs, we always
|
|
|
803
|
+-- want a specific host 'InboxId' (which is announced in the 'RunTH' request)
|
|
|
804
|
+remoteTHCall :: Binary a => Pipe -> InboxId -> THMessage a -> IO a
|
|
|
805
|
+remoteTHCall pipe ii msg = either throwIO (pure . runGet get)
|
|
|
806
|
+ =<< remoteAsyncReq pipe (putTHMessage msg) (Just ii)
|
|
795
|
807
|
|
|
796
|
808
|
-- | Handle a 'THMessage' request on the host and reply to the interpreter.
|
|
|
809
|
+-- Symmterically to 'remoteTHCall', we always want to specify the 'InboxId' for
|
|
|
810
|
+-- RunTH "reverse requests"
|
|
797
|
811
|
handleRemoteTHCall
|
|
798
|
|
- :: Pipe -> (forall a. (Show a, Binary a) => THMessage a -> IO a) -> IO ()
|
|
799
|
|
-handleRemoteTHCall pipe k =
|
|
800
|
|
- handleAsyncReq pipe getTHMessage \writer (THMsg msg) -> IO \s0 ->
|
|
|
812
|
+ :: Pipe -> InboxId -> (forall a. (Show a, Binary a) => THMessage a -> IO a) -> IO ()
|
|
|
813
|
+handleRemoteTHCall pipe ii k =
|
|
|
814
|
+ handleAsyncReq pipe getTHMessage (Just ii) \writer (THMsg msg) -> IO \s0 ->
|
|
801
|
815
|
case k msg |#> s0 of -- unIO for linearity checking
|
|
802
|
816
|
(# s1, a #) -> writeReply writer (put a) |#> s1
|
|
803
|
817
|
|
| ... |
... |
@@ -806,13 +820,13 @@ handleRemoteTHCall pipe k = |
|
806
|
820
|
--------------------------------------------------------------------------------
|
|
807
|
821
|
|
|
808
|
822
|
-- | Send an asynchronous request and block waiting for a matching reply
|
|
809
|
|
-remoteAsyncReq :: Pipe -> Put -> IO (Either SomeException LB.ByteString)
|
|
810
|
|
-remoteAsyncReq pipe msg = do
|
|
|
823
|
+remoteAsyncReq :: Pipe -> Put -> (Maybe InboxId) -> IO (Either SomeException LB.ByteString)
|
|
|
824
|
+remoteAsyncReq pipe msg ii = do
|
|
811
|
825
|
|
|
812
|
826
|
let pendingReplies = pendingReply (pipeAsyncMngr pipe)
|
|
813
|
827
|
|
|
814
|
828
|
-- Mark pending request
|
|
815
|
|
- uq <- freshReqId (pipeAsyncMngr pipe)
|
|
|
829
|
+ uq <- freshId (pipeAsyncMngr pipe)
|
|
816
|
830
|
wv <- newEmptyTMVarIO
|
|
817
|
831
|
|
|
818
|
832
|
isClosed <- atomically $ do
|
| ... |
... |
@@ -827,7 +841,7 @@ remoteAsyncReq pipe msg = do |
|
827
|
841
|
throwIO PipeClosedException
|
|
828
|
842
|
|
|
829
|
843
|
-- Write request
|
|
830
|
|
- writePipe pipe (Request uq (runPut msg))
|
|
|
844
|
+ writePipe pipe (Request uq ii (runPut msg))
|
|
831
|
845
|
`onException`
|
|
832
|
846
|
-- the req wasn't done, or no one will be reading the reply:
|
|
833
|
847
|
-- in either case, drop it from the pending map.
|
| ... |
... |
@@ -845,18 +859,20 @@ remoteAsyncReq pipe msg = do |
|
845
|
859
|
-- The handler receives as an argument the function to use to reply to the request.
|
|
846
|
860
|
-- It must be used exactly once (all requests expect a reply, and you can't
|
|
847
|
861
|
-- reply more than once). Linear types enforce the handler replies in every
|
|
848
|
|
--- branch, and if the handler otherwise throws, it is handled by 'withAsyncHandler'.
|
|
849
|
|
-handleAsyncReq :: Pipe -> Get a -> (WriteReply Put %1 -> a -> IO r) -> IO r
|
|
850
|
|
-handleAsyncReq pipe getit k = mask $ \restore -> do
|
|
|
862
|
+-- branch, and if the handler otherwise throws, it is handled by 'handleAsyncReq'.
|
|
|
863
|
+handleAsyncReq :: Pipe -> Get a -> (Maybe InboxId) -> (WriteReply Put %1 -> a -> IO r) -> IO r
|
|
|
864
|
+handleAsyncReq pipe getit mii k = mask $ \restore -> do
|
|
|
865
|
+ let am = pipeAsyncMngr pipe
|
|
851
|
866
|
ack <- newEmptyMVar
|
|
852
|
867
|
|
|
853
|
868
|
-- Block waiting for request
|
|
854
|
869
|
mbreq <- atomically $ do
|
|
855
|
870
|
isClosed <- readTVar (pipeIsClosed pipe)
|
|
856
|
|
- isEmpty <- isEmptyTQueue (requestsQueue (pipeAsyncMngr pipe))
|
|
857
|
|
- if isClosed && isEmpty
|
|
|
871
|
+ if isClosed
|
|
858
|
872
|
then return Nothing
|
|
859
|
|
- else Just <$> readTQueue (requestsQueue (pipeAsyncMngr pipe))
|
|
|
873
|
+ else Just <$> case mii of
|
|
|
874
|
+ Nothing -> readTQueue $ requestsQueue am
|
|
|
875
|
+ Just ii -> readTQueue =<< getMailbox am ii
|
|
860
|
876
|
|
|
861
|
877
|
case mbreq of
|
|
862
|
878
|
Nothing -> throwIO PipeClosedException
|
| ... |
... |
@@ -907,15 +923,40 @@ data AsyncManager = AsyncManager |
|
907
|
923
|
-- TMVar is signaled when the reply arrives. The exception can originate
|
|
908
|
924
|
-- from the host process, or from the remote one (with 'RemoteProcessException').
|
|
909
|
925
|
, requestsQueue :: !(TQueue (CorrelationId, LB.ByteString))
|
|
910
|
|
- -- ^ A queue of pending received requests. When a request is received,
|
|
911
|
|
- -- queue it here. A request handler will pop the next request from it.
|
|
912
|
|
- -- (Nothing except the 'drainPipe' thread should read from the pipe).
|
|
|
926
|
+ -- ^ A queue of pending received requests. When a request is received
|
|
|
927
|
+ -- /without/ a specific 'InboxId', queue it here. A request handler will
|
|
|
928
|
+ -- pop the next request from it. (Nothing except the 'drainPipe' thread
|
|
|
929
|
+ -- should read from the pipe). Read with @'handleAsyncReq' p get Nothing@.
|
|
|
930
|
+ , mailboxQueues :: !(TVar (IntMap (TQueue (CorrelationId, LB.ByteString))))
|
|
|
931
|
+ -- ^ Requests for a specific 'InboxId' that can be read using
|
|
|
932
|
+ -- @'handleAsyncReq' p get (Just <mailboxid>)@.
|
|
913
|
933
|
, asyncDrainTid :: !ThreadId
|
|
914
|
|
- , asyncNextReq :: !(IORef Int)
|
|
|
934
|
+ , asyncFreshId :: !(IORef Int)
|
|
915
|
935
|
}
|
|
916
|
936
|
|
|
|
937
|
+-- | Each 'Request' expects a 'Reply' with a matching 'CorrelationId'.
|
|
917
|
938
|
type CorrelationId = Int
|
|
918
|
939
|
|
|
|
940
|
+-- | A 'Request' may optionally declare an 'InboxId'. If it does, it can only
|
|
|
941
|
+-- be read by a 'handleAsyncReq' for that specific 'InboxId'. That is,
|
|
|
942
|
+-- @'handleAsyncReq' p get Nothing@ will not receive messages meant for a
|
|
|
943
|
+-- specific inbox.
|
|
|
944
|
+--
|
|
|
945
|
+-- This is useful to prevent mixing up reverse requests, where we want to send
|
|
|
946
|
+-- many reverse requests but have the caller handle them without interleaving
|
|
|
947
|
+-- them.
|
|
|
948
|
+newtype InboxId = InboxId Int deriving (Show, Binary)
|
|
|
949
|
+
|
|
|
950
|
+-- | Create a new mailbox from where we can read all requests addressed to this 'InboxId'.
|
|
|
951
|
+newMailbox :: AsyncManager -> IO InboxId
|
|
|
952
|
+newMailbox am = InboxId <$> freshId am
|
|
|
953
|
+ -- `mailboxQueues` is written on demand, no need to do it here.
|
|
|
954
|
+
|
|
|
955
|
+-- | Clean up a created mailbox by 'InboxId'.
|
|
|
956
|
+closeMailbox :: AsyncManager -> InboxId -> IO ()
|
|
|
957
|
+closeMailbox AsyncManager{..} (InboxId ii) =
|
|
|
958
|
+ atomically (modifyTVar mailboxQueues (IntMap.delete ii))
|
|
|
959
|
+
|
|
919
|
960
|
-- | We have a single type for requests and replies because both the host and
|
|
920
|
961
|
-- interpreter may send requests *and* replies on the write end *and* on recv
|
|
921
|
962
|
-- both on the read end.
|
| ... |
... |
@@ -925,28 +966,37 @@ type CorrelationId = Int |
|
925
|
966
|
-- 2. The interpreter sends a NewName TH request on its Interpreter Write End (IWE)
|
|
926
|
967
|
-- 3. The host sends a reply to the TH request on its HWE
|
|
927
|
968
|
-- 4. The interpreter sends the reply to the RunTH request on its IWE
|
|
928
|
|
-data IddMsg = Request !CorrelationId LB.ByteString
|
|
|
969
|
+--
|
|
|
970
|
+-- Additionally, we want to keep concurrent TH requests for independent RunTH
|
|
|
971
|
+-- calls separated. Thus the interpreter requests additionally specify a
|
|
|
972
|
+-- mailbox announced by RunTH:
|
|
|
973
|
+-- 1. To interpreter: @Request _ Nothing (RunTH <mailid>)@
|
|
|
974
|
+-- 2. From interpreter: @Request _ (Just <mailid>) NewName@
|
|
|
975
|
+-- And the host will relate the <mailid> reverse requests to that RunTH run
|
|
|
976
|
+-- exclusively.
|
|
|
977
|
+data IddMsg = Request !CorrelationId !(Maybe InboxId) LB.ByteString
|
|
929
|
978
|
| Reply !CorrelationId (Either SerializableException LB.ByteString)
|
|
930
|
979
|
|
|
931
|
980
|
instance Binary IddMsg where
|
|
932
|
|
- put (Request i bs) = putWord8 0 >> put i >> put bs
|
|
933
|
|
- put (Reply i bs) = putWord8 1 >> put i >> put bs
|
|
|
981
|
+ put (Request i ii bs) = putWord8 0 >> put i >> put ii >> put bs
|
|
|
982
|
+ put (Reply i bs) = putWord8 1 >> put i >> put bs
|
|
934
|
983
|
get = do
|
|
935
|
984
|
b <- getWord8
|
|
936
|
985
|
case b of
|
|
937
|
|
- 0 -> Request <$> get <*> get
|
|
|
986
|
+ 0 -> Request <$> get <*> get <*> get
|
|
938
|
987
|
1 -> Reply <$> get <*> get
|
|
939
|
988
|
_ -> fail $ "impossible: Binary IddMsg - " ++ show b
|
|
940
|
989
|
|
|
941
|
990
|
newAsyncManager :: Pipe -> IO AsyncManager
|
|
942
|
991
|
newAsyncManager pipe = do
|
|
943
|
|
- asyncNextReq <- newIORef 0
|
|
|
992
|
+ asyncFreshId <- newIORef 0
|
|
944
|
993
|
pendingReply <- newTVarIO IntMap.empty
|
|
945
|
|
- requestsQueue <- newTQueueIO
|
|
|
994
|
+ requestsQueue <- newTQueueIO
|
|
|
995
|
+ mailboxQueues <- newTVarIO IntMap.empty
|
|
946
|
996
|
|
|
947
|
997
|
-- Fork thread to drain read end.
|
|
948
|
998
|
-- (cleaned up on 'closePipe')
|
|
949
|
|
- asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
|
|
|
999
|
+ asyncDrainTid <- forkIOWithUnmask (drainPipe pipe)
|
|
950
|
1000
|
|
|
951
|
1001
|
let am = AsyncManager{..}
|
|
952
|
1002
|
pure am
|
| ... |
... |
@@ -975,8 +1025,15 @@ drainPipe p unmask = catch (unmask loop) replyToPendings where |
|
975
|
1025
|
putTMVar wv (bimap (toException . RemoteProcessException) id bs)
|
|
976
|
1026
|
loop
|
|
977
|
1027
|
-- Receive a request and queue it
|
|
978
|
|
- Just (Request uq bs) -> do
|
|
979
|
|
- atomically $ writeTQueue (requestsQueue am) (uq,bs)
|
|
|
1028
|
+ Just (Request uq mbi bs) -> do
|
|
|
1029
|
+ atomically $ case mbi of
|
|
|
1030
|
+ Nothing ->
|
|
|
1031
|
+ -- "Global" request
|
|
|
1032
|
+ writeTQueue (requestsQueue am) (uq,bs)
|
|
|
1033
|
+ Just ii -> do
|
|
|
1034
|
+ -- "Specific mailbox" request
|
|
|
1035
|
+ q <- getMailbox am ii
|
|
|
1036
|
+ writeTQueue q (uq,bs)
|
|
980
|
1037
|
loop
|
|
981
|
1038
|
-- EOF on read handle
|
|
982
|
1039
|
Nothing -> do
|
| ... |
... |
@@ -991,9 +1048,20 @@ drainPipe p unmask = catch (unmask loop) replyToPendings where |
|
991
|
1048
|
|
|
992
|
1049
|
am = pipeAsyncMngr p
|
|
993
|
1050
|
|
|
994
|
|
-freshReqId :: AsyncManager -> IO CorrelationId
|
|
995
|
|
-freshReqId AsyncManager{asyncNextReq} =
|
|
996
|
|
- atomicModifyIORef' asyncNextReq $ \i -> (i+1, i)
|
|
|
1051
|
+freshId :: AsyncManager -> IO Int
|
|
|
1052
|
+freshId AsyncManager{asyncFreshId} =
|
|
|
1053
|
+ atomicModifyIORef' asyncFreshId $ \i -> (i+1, i)
|
|
|
1054
|
+
|
|
|
1055
|
+getMailbox :: AsyncManager -> InboxId -> STM (TQueue (CorrelationId, LB.ByteString))
|
|
|
1056
|
+getMailbox am (InboxId ii) = do
|
|
|
1057
|
+ mboxes <- readTVar (mailboxQueues am)
|
|
|
1058
|
+ case IntMap.lookup ii mboxes of
|
|
|
1059
|
+ Nothing -> do
|
|
|
1060
|
+ q <- newTQueue
|
|
|
1061
|
+ writeTVar (mailboxQueues am) (IntMap.insert ii q mboxes)
|
|
|
1062
|
+ pure q
|
|
|
1063
|
+ Just q ->
|
|
|
1064
|
+ pure q
|
|
997
|
1065
|
|
|
998
|
1066
|
--------------------------------------------------------------------------------
|
|
999
|
1067
|
-- * Pipe thread-safe read/write
|