Skip to content

Commit e339c1d

Browse files
committed
refactor SessionState management for improved batch loading logic
1 parent f140a2a commit e339c1d

File tree

1 file changed

+58
-35
lines changed

1 file changed

+58
-35
lines changed

ghcide/session-loader/Development/IDE/Session.hs

+58-35
Original file line numberDiff line numberDiff line change
@@ -418,14 +418,33 @@ getHieDbLoc dir = do
418418
createDirectoryIfMissing True cDir
419419
pure (cDir </> db)
420420

421+
{- Note [SessionState and batch load]
422+
SessionState manages the state for batch loading files in the session loader.
423+
424+
- When a new file needs to be loaded, it is added to the pendingFiles set.
425+
- The loader processes files from pendingFiles, attempting to load them in batches.
426+
- If a file is already in failedFiles, it is loaded individually (single-file mode).
427+
- Otherwise, the loader tries to load as many files as possible together (batch mode).
428+
429+
On success:
430+
- All successfully loaded files are removed from pendingFiles and failedFiles,
431+
and added to loadedFiles.
432+
433+
On failure:
434+
- If loading a single file fails, it is added to failedFiles and removed from loadedFiles and pendingFiles.
435+
- If batch loading fails, all files attempted are added to failedFiles.
436+
437+
This approach ensures efficient batch loading while isolating problematic files for individual handling.
438+
-}
439+
421440
data SessionState = SessionState
422-
{ cradle_files :: !(IORef (HashSet FilePath))
423-
, error_loading_files :: !(IORef (HashSet FilePath))
424-
, hscEnvs :: !(Var HieMap)
425-
, fileToFlags :: !(STM.Map (Maybe FilePath) (HashMap NormalizedFilePath (IdeResult HscEnvEq, DependencyInfo)))
426-
, filesMap :: !(STM.Map NormalizedFilePath (Maybe FilePath))
427-
, pendingFileSet :: !(S.OrderedSet FilePath)
428-
, version :: !(Var Int)
441+
{ loadedFiles :: !(IORef (HashSet FilePath)),
442+
failedFiles :: !(IORef (HashSet FilePath)),
443+
pendingFiles :: !(S.OrderedSet FilePath),
444+
hscEnvs :: !(Var HieMap),
445+
fileToFlags :: !(STM.Map (Maybe FilePath) (HashMap NormalizedFilePath (IdeResult HscEnvEq, DependencyInfo))),
446+
filesMap :: !(STM.Map NormalizedFilePath (Maybe FilePath)),
447+
version :: !(Var Int)
429448
}
430449

431450
-- | Helper functions for SessionState management
@@ -434,34 +453,34 @@ data SessionState = SessionState
434453
-- | Add a file to the set of files with errors during loading
435454
addErrorLoadingFile :: SessionState -> FilePath -> IO ()
436455
addErrorLoadingFile state file =
437-
atomicModifyIORef' (error_loading_files state) (\xs -> (Set.insert file xs, ()))
456+
atomicModifyIORef' (failedFiles state) (\xs -> (Set.insert file xs, ()))
438457

439458
addErrorLoadingFiles :: SessionState -> [FilePath] -> IO ()
440459
addErrorLoadingFiles = mapM_ . addErrorLoadingFile
441460

442461
-- | Remove a file from the set of files with errors during loading
443462
removeErrorLoadingFile :: SessionState -> FilePath -> IO ()
444463
removeErrorLoadingFile state file =
445-
atomicModifyIORef' (error_loading_files state) (\xs -> (Set.delete file xs, ()))
464+
atomicModifyIORef' (failedFiles state) (\xs -> (Set.delete file xs, ()))
446465

447466
addCradleFiles :: SessionState -> HashSet FilePath -> IO ()
448467
addCradleFiles state files =
449-
atomicModifyIORef' (cradle_files state) (\xs -> (files <> xs, ()))
468+
atomicModifyIORef' (loadedFiles state) (\xs -> (files <> xs, ()))
450469

451470
-- | Remove a file from the cradle files set
452471
removeCradleFile :: SessionState -> FilePath -> IO ()
453472
removeCradleFile state file =
454-
atomicModifyIORef' (cradle_files state) (\xs -> (Set.delete file xs, ()))
473+
atomicModifyIORef' (loadedFiles state) (\xs -> (Set.delete file xs, ()))
455474

456475
-- | Clear error loading files and reset to empty set
457476
clearErrorLoadingFiles :: SessionState -> IO ()
458477
clearErrorLoadingFiles state =
459-
atomicModifyIORef' (error_loading_files state) (\_ -> (Set.empty, ()))
478+
atomicModifyIORef' (failedFiles state) (\_ -> (Set.empty, ()))
460479

461480
-- | Clear cradle files and reset to empty set
462481
clearCradleFiles :: SessionState -> IO ()
463482
clearCradleFiles state =
464-
atomicModifyIORef' (cradle_files state) (\_ -> (Set.empty, ()))
483+
atomicModifyIORef' (loadedFiles state) (\_ -> (Set.empty, ()))
465484

466485
-- | Reset the file maps in the session state
467486
resetFileMaps :: SessionState -> STM ()
@@ -482,12 +501,12 @@ insertFileMapping state hieYaml ncfp =
482501
-- | Remove a file from the pending file set
483502
removeFromPending :: SessionState -> FilePath -> STM ()
484503
removeFromPending state file =
485-
S.delete file (pendingFileSet state)
504+
S.delete file (pendingFiles state)
486505

487506
-- | Add a file to the pending file set
488507
addToPending :: SessionState -> FilePath -> STM ()
489508
addToPending state file =
490-
S.insert file (pendingFileSet state)
509+
S.insert file (pendingFiles state)
491510

492511

493512
-- | Insert multiple file mappings at once
@@ -501,7 +520,7 @@ incrementVersion state = modifyVar' (version state) succ
501520

502521
-- | Get files from the pending file set
503522
getPendingFiles :: SessionState -> IO (HashSet FilePath)
504-
getPendingFiles state = atomically $ Set.fromList <$> S.toUnOrderedList (pendingFileSet state)
523+
getPendingFiles state = atomically $ Set.fromList <$> S.toUnOrderedList (pendingFiles state)
505524

506525
-- | Handle errors during session loading by recording file as having error and removing from pending
507526
handleSessionError :: SessionState -> Maybe FilePath -> FilePath -> PackageSetupException -> IO ()
@@ -527,8 +546,8 @@ handleFileProcessingError state hieYaml file diags extraDepFiles = do
527546
getExtraFilesToLoad :: SessionState -> FilePath -> IO [FilePath]
528547
getExtraFilesToLoad state cfp = do
529548
pendingFiles <- getPendingFiles state
530-
errorFiles <- readIORef (error_loading_files state)
531-
old_files <- readIORef (cradle_files state)
549+
errorFiles <- readIORef (failedFiles state)
550+
old_files <- readIORef (loadedFiles state)
532551
-- if the file is in error loading files, we fall back to single loading mode
533552
return $
534553
Set.toList $
@@ -537,6 +556,19 @@ getExtraFilesToLoad state cfp = do
537556
-- remove error files from pending files since error loading need to load one by one
538557
else (Set.delete cfp $ pendingFiles `Set.difference` errorFiles) <> old_files
539558

559+
newSessionState :: IO SessionState
560+
newSessionState = do
561+
-- Initialize SessionState
562+
sessionState <- SessionState
563+
<$> newIORef (Set.fromList []) -- loadedFiles
564+
<*> newIORef (Set.fromList []) -- failedFiles
565+
<*> S.newIO -- pendingFiles
566+
<*> newVar Map.empty -- hscEnvs
567+
<*> STM.newIO -- fileToFlags
568+
<*> STM.newIO -- filesMap
569+
<*> newVar 0 -- version
570+
return sessionState
571+
540572
-- | Given a root directory, return a Shake 'Action' which setups an
541573
-- 'IdeGhcSession' given a file.
542574
-- Some of the many things this does:
@@ -555,16 +587,7 @@ loadSessionWithOptions :: Recorder (WithPriority Log) -> SessionLoadingOptions -
555587
loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
556588
let toAbsolutePath = toAbsolute rootDir -- see Note [Root Directory]
557589

558-
-- Initialize SessionState
559-
sessionState <- SessionState
560-
<$> newIORef (Set.fromList []) -- cradle_files
561-
<*> newIORef (Set.fromList []) -- error_loading_files
562-
<*> newVar Map.empty -- hscEnvs
563-
<*> STM.newIO -- fileToFlags
564-
<*> STM.newIO -- filesMap
565-
<*> S.newIO -- pendingFileSet
566-
<*> newVar 0 -- version
567-
590+
sessionState <- newSessionState
568591
biosSessionLoadingVar <- newVar Nothing :: IO (Var (Maybe SessionLoadingPreferenceConfig))
569592
let returnWithVersion fun = IdeGhcSession fun <$> liftIO (readVar (version sessionState))
570593

@@ -709,13 +732,13 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
709732
])
710733
Nothing
711734

712-
pendingFiles <- getPendingFiles sessionState
735+
pendings <- getPendingFiles sessionState
713736
-- this_flags_map might contains files not in pendingFiles, take the intersection
714-
let newLoaded = pendingFiles `Set.intersection` Set.fromList (fromNormalizedFilePath <$> HM.keys this_flags_map)
737+
let newLoaded = pendings `Set.intersection` Set.fromList (fromNormalizedFilePath <$> HM.keys this_flags_map)
715738
atomically $ do
716739
STM.insert this_flags_map hieYaml (fileToFlags sessionState)
717740
insertAllFileMappings sessionState $ map ((hieYaml,) . fst) $ concatMap toFlagsMap all_targets
718-
forM_ newLoaded $ flip S.delete (pendingFileSet sessionState)
741+
forM_ newLoaded $ flip S.delete (pendingFiles sessionState)
719742

720743
logWith recorder Info $ LogSessionNewLoadedFiles $ Set.toList newLoaded
721744
-- remove all new loaded file from error loading files
@@ -781,15 +804,15 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
781804
Left err -> do
782805
-- what if the error to load file is one of old_files ?
783806
let attemptToLoadFiles = Set.delete cfp $ Set.fromList $ concatMap cradleErrorLoadingFiles err
784-
old_files <- readIORef (cradle_files sessionState)
807+
old_files <- readIORef (loadedFiles sessionState)
785808
let errorToLoadNewFiles = cfp : Set.toList (attemptToLoadFiles `Set.difference` old_files)
786809
if length errorToLoadNewFiles > 1
787810
then do
788811
-- we are loading more files and failed, we need to retry
789812
-- mark as less loaded files as failedLoadingFiles as possible
790813
-- limitation is that when we are loading files, and the dependencies of old_files
791814
-- are changed, and old_files are not valid anymore.
792-
-- but they will still be in the old_files, and will not move to error_loading_files.
815+
-- but they will still be in the old_files, and will not move to failedFiles.
793816
-- And make other files failed to load in batch mode.
794817
addErrorLoadingFiles sessionState errorToLoadNewFiles
795818
-- retry without other files
@@ -869,7 +892,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
869892
let getOptionsLoop :: IO ()
870893
getOptionsLoop = do
871894
-- Get the next file to load
872-
file <- atomically $ S.readQueue (pendingFileSet sessionState)
895+
file <- atomically $ S.readQueue (pendingFiles sessionState)
873896
logWith recorder Debug (LogGetOptionsLoop file)
874897
let ncfp = toNormalizedFilePath' file
875898
cachedHieYamlLocation <- join <$> atomically (STM.lookup ncfp (filesMap sessionState))
@@ -887,7 +910,7 @@ loadSessionWithOptions recorder SessionLoadingOptions{..} rootDir que = do
887910
let ncfp = toNormalizedFilePath' absFile
888911
res <- atomically $ do
889912
-- wait until target file is not in pendingFiles
890-
Extra.whenM (S.lookup absFile (pendingFileSet sessionState)) STM.retry
913+
Extra.whenM (S.lookup absFile (pendingFiles sessionState)) STM.retry
891914
-- check if in the cache
892915
checkInCache ncfp
893916
logWith recorder Debug $ LogLookupSessionCache absFile

0 commit comments

Comments
 (0)