We observed in #8 a race condition that we could reproduce in the following way:
Open a file and as soon as one can observe compilation progress, close the file in the editor.
This reliably would cause an error in haskell-lsp:src/Language/Haskell/LSP/VFS.hs:131:16.
To see what happens in hie, I created this sequence diagram:
In words: when we receive a NotDidOpenDocument, we send an document update to the dispatcher and then to the GHC Thread. In this update we persist the Virtual File that has been freshly created to a temporary file in the file system.
I assume we send this message to the ghc thread because previously, ghc-mod took care of Virtual Files, and ghcmod lived in the ghc thread.
However, when we send a message to the ghc thread, the action is run in a context. This context is the action: getCradle uri (\lc -> loadCradle df lc >> action). So, now before we actually try to persist the Virtual File to the file system, we load the cradle. After the cradle has been loaded, we persist the Virtual File.
In the meantime, if a NotDidCloseDocument is processed, it will remove the Virtual File from the VFS. The problem is now, that NotDidCloseDocument does not send a message to the ghc thread and can immediately remove the Virtual File, while the processing of the NotDidOpenDocument action is still stalled on the loadCradle function.
A work around is presented here:
In short, we persist the Virtual File immediately in a blocking action, so no race can occur.
We can do this, because we can persist the Virtual File immediately, there is no ghc-mod that manages that for us.
How did that work before? Since VFS and persisting the Virtual File to the file system were two distinct actions, taken care of by different actors that knew nothing about each other. Closing a virtual file had no impact on ghcmod persisting that file in the file system.
We observed in #8 a race condition that we could reproduce in the following way: Open a file and as soon as one can observe compilation progress, close the file in the editor. This reliably would cause an error in
haskell-lsp
:src/Language/Haskell/LSP/VFS.hs:131:16
.To see what happens in hie, I created this sequence diagram:
In words: when we receive a
NotDidOpenDocument
, we send an document update to the dispatcher and then to theGHC Thread
. In this update we persist the Virtual File that has been freshly created to a temporary file in the file system. I assume we send this message to the ghc thread because previously, ghc-mod took care of Virtual Files, and ghcmod lived in the ghc thread.However, when we send a message to the ghc thread, the action is run in a context. This context is the action:
getCradle uri (\lc -> loadCradle df lc >> action)
. So, now before we actually try to persist the Virtual File to the file system, we load the cradle. After the cradle has been loaded, we persist the Virtual File. In the meantime, if aNotDidCloseDocument
is processed, it will remove the Virtual File from the VFS. The problem is now, thatNotDidCloseDocument
does not send a message to the ghc thread and can immediately remove the Virtual File, while the processing of theNotDidOpenDocument
action is still stalled on theloadCradle
function.A work around is presented here:
In short, we persist the Virtual File immediately in a blocking action, so no race can occur.
We can do this, because we can persist the Virtual File immediately, there is no ghc-mod that manages that for us. How did that work before? Since VFS and persisting the Virtual File to the file system were two distinct actions, taken care of by different actors that knew nothing about each other. Closing a virtual file had no impact on ghcmod persisting that file in the file system.