-
Notifications
You must be signed in to change notification settings - Fork 49
/
Copy pathDevelMain.hs
116 lines (104 loc) · 3.34 KB
/
DevelMain.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
{-# LANGUAGE OverloadedStrings,TypeApplications #-}
-- | Running your app inside GHCi.
--
-- > stack ghci
--
-- To start your app, run:
--
-- > :l DevelMain
-- > DevelMain.update
--
-- You can also call @DevelMain.shutdown@ to stop the app
--
-- There is more information about this approach,
-- on the wiki: https://github.com/yesodweb/yesod/wiki/ghci
module DevelMain where
import Prelude
import Data.Typeable
import qualified Data.Text as Text
import Data.Text (Text)
import System.IO
import Control.Concurrent
import Control.Exception.Safe
import Control.Monad
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Foreign.Store (Store(..), lookupStore, readStore, storeAction, withStore)
import GHC.Word (Word32)
import Init (runAppDevel)
import Say
import Data.Monoid
tshow :: Show a => a -> Text
tshow = Text.pack . show
-- | Start or restart the server.
-- newStore is from foreign-store.
-- A Store holds onto some data across ghci reloads
update :: IO ()
update = do
hSetBuffering stdout NoBuffering
hSetBuffering stderr NoBuffering
mtidStore <- lookupStore tidStoreNum
case mtidStore of
Nothing -> do
say "no server running"
done <- storeAction doneStore newEmptyMVar
tid <- start done
_ <- storeAction (Store tidStoreNum) (newIORef tid)
return ()
Just tidStore -> do
say "restarting app..."
restartAppInNewThread tidStore
where
doneStore :: Store (MVar ())
doneStore = Store 0
-- shut the server down with killThread and wait for the done signal
restartAppInNewThread :: Store (IORef ThreadId) -> IO ()
restartAppInNewThread tidStore = modifyStoredIORef tidStore $ \tid -> do
say $ "killing thread: " <> tshow tid
killThread tid
say $ "taking mvar"
withStore doneStore takeMVar
readStore doneStore >>= start
-- | Start the server in a separate thread.
start :: MVar () -- ^ Written to when the thread is killed.
-> IO ThreadId
start done =
myThreadId <* (do
say "in forkFinally"
runAppDevel `catch` \(SomeException e) -> do
say "!!! exception in runAppDevel !!!"
say $ "X exception type: " <> tshow (typeOf e)
say $ "X exception : " <> tshow e
say "runAppDevel terminated"
)
`catch`
(\(SomeException err) -> do
say "finally action"
hFlush stdout
hFlush stderr
putMVar done ()
say $ "Got Exception: " <> tshow err
throwIO err
)
`finally`
(do
say "finally action"
hFlush stdout
hFlush stderr
putMVar done ()
)
-- | kill the server
shutdown :: IO ()
shutdown = do
mtidStore <- lookupStore tidStoreNum
case mtidStore of
-- no server running
Nothing -> say "no app running"
Just tidStore -> do
withStore tidStore $ readIORef >=> killThread
say "App is shutdown"
tidStoreNum :: Word32
tidStoreNum = 1
modifyStoredIORef :: Store (IORef a) -> (a -> IO a) -> IO ()
modifyStoredIORef store f = withStore store $ \ref -> do
v <- readIORef ref
f v >>= writeIORef ref