1
1
{-
2
- Module : Development.IDE.WorkerThread
2
+ Module : Development.IDE.Core. WorkerThread
3
3
Author : @soulomoon
4
4
SPDX-License-Identifier: Apache-2.0
5
5
6
6
Description : This module provides an API for managing worker threads in the IDE.
7
7
see Note [Serializing runs in separate thread]
8
8
-}
9
- {-# LANGUAGE LambdaCase #-}
10
- {-# LANGUAGE OverloadedStrings #-}
11
-
12
- module Development.IDE.WorkerThread
9
+ module Development.IDE.Core.WorkerThread
13
10
( LogWorkerThread (.. ),
14
11
withWorkerQueue ,
15
12
awaitRunInThread ,
16
13
TaskQueue ,
17
14
writeTaskQueue ,
18
- withWorkerQueueSimple ,
19
- awaitRunInThreadStm ,
20
- awaitRunInThreadStmInNewThread
21
- ) where
15
+ withWorkerQueueSimple
16
+ )
17
+ where
22
18
23
- import Control.Concurrent.Async ( Async , async , withAsync )
19
+ import Control.Concurrent.Async ( withAsync )
24
20
import Control.Concurrent.STM
25
- import Control.Exception.Safe (MonadMask (.. ),
26
- SomeException (SomeException ),
27
- finally , throw , try )
28
- import Control.Monad.Cont (ContT (ContT ))
29
- import qualified Data.Text as T
30
-
31
- import Control.Concurrent
32
- import Control.Exception (catch )
33
- import Control.Monad (void , when )
21
+ import Control.Concurrent.Strict (newBarrier , signalBarrier ,
22
+ waitBarrier )
23
+ import Control.Exception.Safe (SomeException , finally , throwIO ,
24
+ try )
25
+ import Control.Monad.Cont (ContT (ContT ))
26
+ import qualified Data.Text as T
27
+ import Ide.Logger
34
28
35
29
data LogWorkerThread
36
30
= LogThreadEnding ! T. Text
37
31
| LogThreadEnded ! T. Text
38
32
| LogSingleWorkStarting ! T. Text
39
33
| LogSingleWorkEnded ! T. Text
40
- | LogMainThreadId ! T. Text ! ThreadId
41
34
deriving (Show )
42
35
43
- -- instance Pretty LogWorkerThread where
44
- -- pretty = \case
45
- -- LogThreadEnding t -> "Worker thread ending:" <+> pretty t
46
- -- LogThreadEnded t -> "Worker thread ended:" <+> pretty t
47
- -- LogSingleWorkStarting t -> "Worker starting a unit of work: " <+> pretty t
48
- -- LogSingleWorkEnded t -> "Worker ended a unit of work: " <+> pretty t
49
- -- LogMainThreadId t tid -> "Main thread for" <+> pretty t <+> "is" <+> pretty (show tid)
36
+ instance Pretty LogWorkerThread where
37
+ pretty = \ case
38
+ LogThreadEnding t -> " Worker thread ending:" <+> pretty t
39
+ LogThreadEnded t -> " Worker thread ended:" <+> pretty t
40
+ LogSingleWorkStarting t -> " Worker starting a unit of work: " <+> pretty t
41
+ LogSingleWorkEnded t -> " Worker ended a unit of work: " <+> pretty t
50
42
51
43
{-
52
44
Note [Serializing runs in separate thread]
@@ -62,17 +54,14 @@ data TaskQueue a = TaskQueue (TQueue a)
62
54
newTaskQueueIO :: IO (TaskQueue a )
63
55
newTaskQueueIO = TaskQueue <$> newTQueueIO
64
56
data ExitOrTask t = Exit | Task t
65
- type Logger = LogWorkerThread -> IO ()
66
57
67
58
-- | 'withWorkerQueue' creates a new 'TQueue', and launches a worker
68
59
-- thread which polls the queue for requests and runs the given worker
69
60
-- function on them.
70
- withWorkerQueueSimple :: Logger -> T. Text -> ContT () IO (TaskQueue (IO () ))
61
+ withWorkerQueueSimple :: Recorder ( WithPriority LogWorkerThread ) -> T. Text -> ContT () IO (TaskQueue (IO () ))
71
62
withWorkerQueueSimple log title = withWorkerQueue log title id
72
- withWorkerQueue :: Logger -> T. Text -> (t -> IO () ) -> ContT () IO (TaskQueue t )
63
+ withWorkerQueue :: Recorder ( WithPriority LogWorkerThread ) -> T. Text -> (t -> IO () ) -> ContT () IO (TaskQueue t )
73
64
withWorkerQueue log title workerAction = ContT $ \ mainAction -> do
74
- tid <- myThreadId
75
- log (LogMainThreadId title tid)
76
65
q <- newTaskQueueIO
77
66
-- Use a TMVar as a stop flag to coordinate graceful shutdown.
78
67
-- The worker thread checks this flag before dequeuing each job; if set, it exits immediately,
@@ -87,8 +76,8 @@ withWorkerQueue log title workerAction = ContT $ \mainAction -> do
87
76
-- if we want to debug the exact location the worker swallows an async exception, we can
88
77
-- temporarily comment out the `finally` clause.
89
78
`finally` atomically (putTMVar b () )
90
- log (LogThreadEnding title)
91
- log (LogThreadEnded title)
79
+ logWith log Debug (LogThreadEnding title)
80
+ logWith log Debug (LogThreadEnded title)
92
81
where
93
82
-- writerThread :: TaskQueue t -> TMVar () -> (forall a. IO a -> IO a) -> IO ()
94
83
writerThread q b =
@@ -104,46 +93,24 @@ withWorkerQueue log title workerAction = ContT $ \mainAction -> do
104
93
case task of
105
94
Exit -> return ()
106
95
Task t -> do
107
- log $ LogSingleWorkStarting title
96
+ logWith log Debug $ LogSingleWorkStarting title
108
97
workerAction t
109
- log $ LogSingleWorkEnded title
98
+ logWith log Debug $ LogSingleWorkEnded title
110
99
writerThread q b
111
100
112
101
113
102
-- | 'awaitRunInThread' queues up an 'IO' action to be run by a worker thread,
114
103
-- and then blocks until the result is computed. If the action throws an
115
104
-- non-async exception, it is rethrown in the calling thread.
116
- awaitRunInThreadStm :: TaskQueue (IO () ) -> IO result -> STM result
117
- awaitRunInThreadStm (TaskQueue q) act = do
118
- barrier <- newEmptyTMVar
119
- -- Take an action from TQueue, run it and
120
- -- use barrier to wait for the result
121
- writeTQueue q (try act >>= atomically . putTMVar barrier)
122
- resultOrException <- takeTMVar barrier
123
- case resultOrException of
124
- Left e -> throw (e :: SomeException )
125
- Right r -> return r
126
-
127
- awaitRunInThreadStmInNewThread :: STM Int -> Int -> TaskQueue (IO () ) -> TVar [Async () ] -> IO result -> (SomeException -> IO () ) -> STM ()
128
- awaitRunInThreadStmInNewThread getStep deliverStep (TaskQueue q) tthreads act handler = do
129
- -- Take an action from TQueue, run it and
130
- -- use barrier to wait for the result
131
- writeTQueue q (uninterruptibleMask $ \ restore -> do
132
- curStep <- atomically getStep
133
- when (curStep == deliverStep) $ do
134
- sync <- async (restore (void act `catch` \ (SomeException e) -> handler (SomeException e)))
135
- atomically $ modifyTVar' tthreads (sync: )
136
- )
137
-
138
105
awaitRunInThread :: TaskQueue (IO () ) -> IO result -> IO result
139
106
awaitRunInThread (TaskQueue q) act = do
140
- barrier <- newEmptyTMVarIO
141
107
-- Take an action from TQueue, run it and
142
108
-- use barrier to wait for the result
143
- atomically $ writeTQueue q (try act >>= atomically . putTMVar barrier)
144
- resultOrException <- atomically $ takeTMVar barrier
109
+ barrier <- newBarrier
110
+ atomically $ writeTQueue q (try act >>= signalBarrier barrier)
111
+ resultOrException <- waitBarrier barrier
145
112
case resultOrException of
146
- Left e -> throw (e :: SomeException )
113
+ Left e -> throwIO (e :: SomeException )
147
114
Right r -> return r
148
115
149
116
writeTaskQueue :: TaskQueue a -> a -> STM ()
0 commit comments