Skip to content

Fix race conditions due to MVar usage #5

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
May 1, 2025

Conversation

alt-romes
Copy link
Collaborator

@alt-romes alt-romes commented Apr 8, 2025

Consider the deadlock described in #6

We fix this by getting rid of the MVar internally and adding a ReaderT to the stack transformer with a debugger-server-wide sessionId backed by an IORef.

Adaptor now has an additional type parameter denoting the type of the request we are responding to. Crucially, this will be Request when responding to a DAP request (e.g. in send*** functions). On the other hand, this will be () for the withAdaptor continuation argument of registerNewDebugSession which unlifts Adaptor to IO because, when unlifting, we are not replying to any request.

These changes to the internal implementation of Adaptor which allow us to get rid of the MVar are sufficient to fix the deadlock and now avoid footguns uses of withAdaptor.

Fixes #6

@alt-romes alt-romes force-pushed the wip/fix-mvar branch 2 times, most recently from fb46969 to aa902fb Compare April 8, 2025 10:24
alt-romes and others added 2 commits April 14, 2025 12:19
The lock `dap` takes on the MVar could cause a nasty deadlock while
doing something seemingly standard in the debugger threads that are
launched by registerNewDebugSession.

Consider

  (readDebuggerOutput, writeDebuggerOutput) <- liftIO P.createPipe
  registerNewDebugSession (maybe "debug-session" T.pack __sessionId) DAS{..}
    [ debuggerThread ... writeDebuggerOutput
    , outputEventsThread readDebuggerOutput
    ]

An outline of the deadlock is:

1. Initialisation starts, starts writing to a pipe
2. Worker thread tries to output things from the pipe, but can't because that requires taking a lock on the MVar
3. Step 1 blocks because the pipe gets full
4. Step 2 blocks, since it is waiting for step 1 to finish

We fix this by getting rid of the `MVar` internally and adding a
`ReaderT` to the stack transformer with a debugger-server-wide
sessionId backed by an IORef.

`Adaptor` now has an additional type parameter denoting the type of the
request we are responding to. Crucially, this will be `Request` when
responding to a DAP request (e.g. in `send***` functions). On the other
hand, this will be `()` for the `withAdaptor` continuation argument of
`registerNewDebugSession` which unlifts `Adaptor` to `IO` because, when
unlifting, we are not replying to any request.

These changes to the internal implementation of `Adaptor` which allow us
to get rid of the `MVar` are sufficient to fix the deadlock and now
avoid footguns uses of `withAdaptor`.

Fixes haskell-debugger#6
@alt-romes
Copy link
Collaborator Author

@dmjio I've fixed the testsuite now!

@dmjio
Copy link
Contributor

dmjio commented Apr 15, 2025

@alt-romes @mpickering , the solution seems reasonable to me. Let me ensure that this fix is compatible with the haskell-estgi-debugger, ideally this library will be general enough to support the needs of both. I will say that the MVar you're referring to is used heavily in the estgi-debugger. But if we can express the same logic there as you have here, that would be ideal.

Do you have a link to the usage of dap in GHC I could look at ? This way I can compare the usage in estgi for reference as we make this change.

@alt-romes
Copy link
Collaborator Author

Thanks @dmjio. Please take your time.

We've made the source public at https://github.com/well-typed/ghc-debugger, even though the debugger is not quite ready for the general public yet. We'll happily let everyone know once we have a first version.

We believe the dap library is well positioned to be a common denominator amongst debug adapters written in Haskell and that is why we are hoping to upstream and collaborate with you.

I would also like to point out that I'd be happy to be involved in the maintenance and release process of dap, if you feel like you need any help or would like to share the workload.

Thanks again!

@dmjio
Copy link
Contributor

dmjio commented Apr 16, 2025

@alt-romes, would be happy to collaborate. I can add you as a maintainer, along with @mpickering as well, on Github and Hackage. @csabahruska and I would be happy to partner on this. Exciting you guys are using this for GHC. That's very cool.

I'll go ahead and add you guys, but for now, regarding this PR, would it be possible to reinstate the MVar but make it a Maybe, and default it to Nothing. I can adjust the estgi code later (maybe make a note that this is specific to the usage of the estg interpreter). Then we can discuss the fate of the MVar later, if we want to go ahead with a full removal. Then we can merge these changes.

This way we can keep development on the mainline branch and it should unblock things, and allow releases for you guys.

@alt-romes
Copy link
Collaborator Author

Happy to be on board!

I could try to reinstate the MVar, as that seems a reasonable compromise -- on the other hand, we are not in a rush to get a release out as the debugger is still well under progress.

Therefore, I suggest we leave this be while you find time to take a deeper look at estgi and figure out if it is really necessary to have the MVar (at which point we can have the Maybe compromise).

In the meantime I may put up other MRs rebased on this one, and then I'll rebase them once this lands. No hurries.

@dmjio
Copy link
Contributor

dmjio commented Apr 16, 2025

You should now have hackage rights, GitHub maintanership, and an invite to the org @haskell-debugger

@dmjio
Copy link
Contributor

dmjio commented Apr 16, 2025

@alt-romes sounds good to me, I'll repost when I can take a deeper look. Thank you.

@alt-romes
Copy link
Collaborator Author

@dmjio I tried to update haskell-estgi-debugger but building it was complicated because of souffle-haskell c++ code that doesn't build properly.

I was wondering if you'll have some time to take a look at updating it in the near future. We are planning a release for an alpha version of the GHC debugger and we'd appreciate it if an updated dap is released in the meantime.

Note: we have one or two more patches on dap that we'll open as MRs after this one lands

@dmjio
Copy link
Contributor

dmjio commented Apr 30, 2025

@alt-romes I'll take a look at it.

I'm planning on abstracting out the MVar using a state variable. This will make the AdaptorState extensible for users to put anything they want in there. Then we can instantiate state at MVar for the haskell-estgi-debugger. Since the MVar technically isn't specific to the DAP protocol, but to the implementation details of the haskell-estgi-debugger, so it should belong in there.

The souffle build is tough, will try to tackle it with nix.

@alt-romes
Copy link
Collaborator Author

I'm planning on abstracting out the MVar using a state variable. This will make the AdaptorState extensible for users to put anything they want in there. Then we can instantiate state at MVar for the haskell-estgi-debugger.

This seems fine. Feel free to push commits on top of mine in this branch

- Renames new 'r' type variable to be 'request' (for library consumers)
- Uses 'throwError' instead of 'error' when using the new 'sendEvent' function
- Bumps nix to use ghc966 in shell.nix
- Light formatting updates
@dmjio
Copy link
Contributor

dmjio commented May 1, 2025

@alt-romes I went ahead and pushed to your branch with some light housekeeping-related changes.

Now that we have grin-compiler/ghc-whole-program-compiler-project#13 and haskell-debugger/haskell-estgi-debugger#23, the dap-estgi-server will always work with a pinned nixpkgs (on an older dap hash).

@csabahruska is currently working on a dependently-typed version of Haskell that has a typed-macro system, which he is considering porting some of the logic from the external-stg-interpeter into. At that point, if and when a debugger is needed the dap will be ready, but current development on dap-estgi-server has stalled because of this. Therefore, we can keep it as is right now and it will always work thanks to nix, and we might consider archiving it.

So moving forward I think we can merge these changes and not worry about dap-estgi-server, since we won't be prioritizing new work on it, but instead on a debugger for csip when it comes online.

@dmjio dmjio merged commit 243e585 into haskell-debugger:master May 1, 2025
1 check passed
@alt-romes
Copy link
Collaborator Author

alt-romes commented May 1, 2025

I've seen your many patches across the board. Great work.

I'm happy to proceed as you described if you think that's a good path forward for dap-estgi-server. Indeed it seems that using nix to preserve a working state there is a good idea.

Csaba's project sounds interesting too, good luck. I'll be on the lookout for news.

Land at will! I'll rebase and push the subsequent patches tomorrow.

@dmjio
Copy link
Contributor

dmjio commented May 2, 2025

Thanks for the kind words @alt-romes. Sounds good will be on the lookout for additional patches tomorrow.

If interested here is the typed-macro system Haskell project (based on 2LTT)

https://github.com/faulhornlabs/csip

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Deadlock in registerNewDebugSession threads
3 participants