Skip to content

Commit dde5f22

Browse files
committed
Fix package exports hack
1 parent 91990de commit dde5f22

File tree

2 files changed

+25
-8
lines changed

2 files changed

+25
-8
lines changed

ghcide/src/Development/IDE/Plugin/CodeAction.hs

+2-2
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@ codeAction lsp state _ (TextDocumentIdentifier uri) _range CodeActionContext{_di
8686
<*> use GhcSession `traverse` mbFile
8787
<*> use GetAnnotatedParsedSource `traverse` mbFile
8888
-- This is quite expensive 0.6-0.7s on GHC
89-
let pkgExports = envPackageExports <$> env
89+
pkgExports <- fromMaybe mempty (envPackageExports <$> env)
9090
localExports <- readVar (exportsMap $ shakeExtras state)
9191
let
92-
exportsMap = localExports <> fromMaybe mempty pkgExports
92+
exportsMap = localExports <> pkgExports
9393
df = ms_hspp_opts . pm_mod_summary <$> parsedModule
9494
actions =
9595
[ mkCA title [x] edit

ghcide/src/Development/IDE/Types/HscEnvEq.hs

+23-6
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import TcRnMonad (initIfaceLoad, WhereFrom (ImportByUser))
2424
import LoadIface (loadInterface)
2525
import qualified Maybes
2626
import OpenTelemetry.Eventlog (withSpan)
27-
import System.IO.Unsafe (unsafePerformIO)
28-
import Control.Monad.Extra (mapMaybeM)
27+
import Control.Monad.Extra (mapMaybeM, join)
28+
import Control.Concurrent.Extra (newVar, modifyVar)
29+
import Control.Concurrent.Async (Async, async, waitCatch)
30+
import Control.Exception (throwIO, mask)
2931

3032
-- | An 'HscEnv' with equality. Two values are considered equal
3133
-- if they are created with the same call to 'newHscEnvEq'.
@@ -39,7 +41,7 @@ data HscEnvEq = HscEnvEq
3941
, envImportPaths :: Maybe [String]
4042
-- ^ If Just, import dirs originally configured in this env
4143
-- If Nothing, the env import dirs are unaltered
42-
, envPackageExports :: ExportsMap
44+
, envPackageExports :: IO ExportsMap
4345
}
4446

4547
-- | Wrap an 'HscEnv' into an 'HscEnvEq'.
@@ -58,9 +60,8 @@ newHscEnvEqWithImportPaths :: Maybe [String] -> HscEnv -> [(InstalledUnitId, Dyn
5860
newHscEnvEqWithImportPaths envImportPaths hscEnv deps = do
5961
envUnique <- newUnique
6062

61-
let
62-
-- evaluate lazily, using unsafePerformIO for a pure API
63-
envPackageExports = unsafePerformIO $ withSpan "Package Exports" $ \_sp -> do
63+
-- it's very important to delay the package exports computation
64+
envPackageExports <- onceAsync $ withSpan "Package Exports" $ \_sp -> do
6465
-- compute the package imports
6566
let pkgst = pkgState (hsc_dflags hscEnv)
6667
depends = explicitPackages pkgst
@@ -119,3 +120,19 @@ instance Hashable HscEnvEq where
119120
instance Binary HscEnvEq where
120121
put _ = error "not really"
121122
get = error "not really"
123+
124+
-- | Given an action, produce a wrapped action that runs at most once.
125+
-- The action is run in an async so it won't be killed by async exceptions
126+
-- If the function raises an exception, the same exception will be reraised each time.
127+
onceAsync :: IO a -> IO (IO a)
128+
onceAsync act = do
129+
var <- newVar OncePending
130+
let run as = either throwIO pure =<< waitCatch as
131+
pure $ mask $ \unmask -> join $ modifyVar var $ \v -> case v of
132+
OnceRunning x -> pure (v, unmask $ run x)
133+
OncePending -> do
134+
x <- async (unmask act)
135+
pure (OnceRunning x, unmask $ run x)
136+
137+
data Once a = OncePending | OnceRunning (Async a)
138+

0 commit comments

Comments
 (0)