Skip to content

Commit ff91d58

Browse files
committed
Add timeout to called functions (fixes #29)
- 10 seconds for synchronous calls - 5 minutes for asynchronous calls Maybe make this configurable?
1 parent fc48abb commit ff91d58

File tree

2 files changed

+33
-13
lines changed

2 files changed

+33
-13
lines changed

library/Neovim/Plugin.hs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,11 @@ import Data.Maybe (catMaybes)
5252
import Data.MessagePack
5353
import Data.Traversable (forM)
5454
import System.Log.Logger
55-
import Text.PrettyPrint.ANSI.Leijen (Doc)
55+
import Text.PrettyPrint.ANSI.Leijen (Doc, (<+>), Pretty(..), text)
5656
import UnliftIO.Exception (SomeException, try)
57-
import UnliftIO.Async (Async, async)
57+
import UnliftIO.Async (Async, async, race)
5858
import UnliftIO.STM
59+
import UnliftIO.Concurrent (threadDelay)
5960

6061
import Prelude
6162

@@ -301,6 +302,14 @@ registerStatefulFunctionality (Plugin { environment = env, exports = fs }) = do
301302
Left e -> return . Left $ show (e :: SomeException)
302303
Right res -> return $ Right res
303304

305+
timeoutAndLog :: Word -> FunctionName -> Neovim anyEnv String
306+
timeoutAndLog seconds functionName = do
307+
threadDelay (fromIntegral seconds * 1000 * 1000)
308+
return . show $
309+
pretty functionName <+> text "has been aborted after"
310+
<+> text (show seconds) <+> text "seconds"
311+
312+
304313
listeningThread :: TQueue SomeMessage
305314
-> TVar (Map FunctionName ([Object] -> Neovim env Object))
306315
-> Neovim env ()
@@ -309,13 +318,24 @@ registerStatefulFunctionality (Plugin { environment = env, exports = fs }) = do
309318

310319
forM_ (fromMessage msg) $ \req@Request{..} -> do
311320
route' <- liftIO $ readTVarIO route
312-
forM_ (Map.lookup reqMethod route') $ \f ->
313-
respond req =<< executeFunction f reqArgs
321+
forM_ (Map.lookup reqMethod route') $ \f -> do
322+
respond req . either Left id =<< race
323+
(timeoutAndLog 10 reqMethod)
324+
(executeFunction f reqArgs)
314325

315326
forM_ (fromMessage msg) $ \Notification{..} -> do
316327
route' <- liftIO $ readTVarIO route
317328
forM_ (Map.lookup notMethod route') $ \f ->
318-
void $ executeFunction f notArgs
329+
void . async $ do
330+
result <- either Left id <$> race
331+
(timeoutAndLog 600 notMethod)
332+
(executeFunction f notArgs)
333+
case result of
334+
Left message ->
335+
nvim_err_writeln' message
336+
Right _ ->
337+
return ()
338+
319339

320340
listeningThread q route
321341

library/Neovim/RPC/SocketReader.hs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ handleResponse i result = do
106106
-- function call identifier.
107107
handleRequestOrNotification :: Maybe Int64 -> FunctionName -> [Object]
108108
-> ConduitT a Void SocketHandler ()
109-
handleRequestOrNotification mi m params = do
109+
handleRequestOrNotification requestId functionToCall params = do
110110
cfg <- lift Internal.ask'
111111
void . liftIO . async $ race logTimeout (handle cfg)
112112
return ()
@@ -115,7 +115,7 @@ handleRequestOrNotification mi m params = do
115115
lookupFunction
116116
:: TMVar Internal.FunctionMap
117117
-> STM (Maybe (FunctionalityDescription, Internal.FunctionType))
118-
lookupFunction funMap = Map.lookup m <$> readTMVar funMap
118+
lookupFunction funMap = Map.lookup functionToCall <$> readTMVar funMap
119119

120120
logTimeout :: IO ()
121121
logTimeout = do
@@ -127,25 +127,25 @@ handleRequestOrNotification mi m params = do
127127
handle rpc = atomically (lookupFunction (Internal.globalFunctionMap rpc)) >>= \case
128128

129129
Nothing -> do
130-
let errM = "No provider for: " <> show m
130+
let errM = "No provider for: " <> show functionToCall
131131
debugM logger errM
132-
forM_ mi $ \i -> atomically' . writeTQueue (Internal.eventQueue rpc)
132+
forM_ requestId $ \i -> atomically' . writeTQueue (Internal.eventQueue rpc)
133133
. SomeMessage $ MsgpackRPC.Response i (Left (toObject errM))
134134

135135
Just (copts, Internal.Stateful c) -> do
136136
now <- liftIO getCurrentTime
137137
reply <- liftIO newEmptyTMVarIO
138138
let q = (recipients . Internal.customConfig) rpc
139-
liftIO . debugM logger $ "Executing stateful function with ID: " <> show mi
140-
case mi of
139+
liftIO . debugM logger $ "Executing stateful function with ID: " <> show requestId
140+
case requestId of
141141
Just i -> do
142142
atomically' . modifyTVar q $ Map.insert i (now, reply)
143143
atomically' . writeTQueue c . SomeMessage $
144-
Request m i (parseParams copts params)
144+
Request functionToCall i (parseParams copts params)
145145

146146
Nothing ->
147147
atomically' . writeTQueue c . SomeMessage $
148-
Notification m (parseParams copts params)
148+
Notification functionToCall (parseParams copts params)
149149

150150

151151
parseParams :: FunctionalityDescription -> [Object] -> [Object]

0 commit comments

Comments
 (0)