-
Notifications
You must be signed in to change notification settings - Fork 47
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
A couple spots async exceptions aren't handled gracefully #187
Comments
That's the whole point. https://www.well-typed.com/blog/2015/07/checked-exceptions/ . |
fwiw, I'd suggest adding a link to that blogpost to the |
It may also be good to have it wait for the lock to become available - commercialhaskell/stack#3055 (comment) |
Could this be the case why commercialhaskell/stack#3055 happens even with just ctrl-c (sigint/sigterm)? I've seen it happen on CI, where I'd be surprised anything is calling SIGKILL. |
Managed to reproduce it with:
|
I think this is because of hackage-security/hackage-security/src/Hackage/Security/Util/IO.hs Lines 33 to 48 in 71a24d6
So this seems to just create a plain directory to implement a lock. Any form of |
@nh2 unfortunately |
https://hackage.haskell.org/package/filelock-0.1.1.2/docs/System-FileLock.html works well on Windows as long as you don't use lock paths for anything. |
@domenkozar I'm aware of the prior art (and there's a lot of history regarding file/record locking in Unix...) :-) |
So what are you working on to address this? |
Ah sorry, I assume you don't due to your previous comment. I guess my implicit question was if we can use filelock to solve this properly and what would be the cons (besides obvious extra dependency) to fix this bug? |
Due to the nature of directory-based locking, if the process exits before exception handlers are run, the directory is never removed and the system remains in a "locked" state indefinitely. This can happen due to SIGKILL or power failure, as well as a blocked thread when main exits. This repro follows that last approach, forking a thread and then letting main exit while that locked thread delays. On my machine, I get the following output for running issue-187.sh: $ ./issue-187.sh + rm -rf tmp + ghc --version The Glorious Glasgow Haskell Compilation System, version 8.2.2 + cabal --version cabal-install version 2.0.0.1 compiled using version 2.0.1.1 of the Cabal library + cabal sandbox init Writing a default package environment file to /Users/michael/Documents/hackage-security/cabal.sandbox.config Using an existing sandbox located at /Users/michael/Documents/hackage-security/.cabal-sandbox + cabal install ./hackage-security Resolving dependencies... In order, the following will be installed: hackage-security-0.5.2.2 (reinstall) Warning: Note that reinstalls are always dangerous. Continuing anyway... Notice: installing into a sandbox located at /Users/michael/Documents/hackage-security/.cabal-sandbox Configuring hackage-security-0.5.2.2... Building hackage-security-0.5.2.2... Installed hackage-security-0.5.2.2 + cabal exec -- ghc -hide-all-packages -package hackage-security -package base -package directory -Wall -Werror issue-187.hs [1 of 1] Compiling Main ( issue-187.hs, issue-187.o ) Linking issue-187 ... + ./issue-187 + ./issue-187 issue-187: /Users/michael/Documents/hackage-security/tmp/hackage-security-lock: createDirectory: already exists (File exists)
I've written a commit to demonstrate one failure mode for directory-based file locking: Quoting the commit message:
Regarding the larger issue of async exception safety here: one possibility is to implement the same technique used in safe-exceptions in the
Note that, AFAICT, the async exception safety issue is almost entirely orthogonal to the issue of using directory creation/removal as a form of locking. |
This commit uses the same async-exception detection mechanism as is used by the safe-exceptions package, via checking if the given exception is cast to a SomeAsyncException. (On older GHCs without SomeAsyncException, it contains a hard-coded list of async exception types.) It then ensures that: * Throwing via throwChecked always generates a synchronous exception * Catching via catchChecked (et al) never catches an asynchronous exception Unfortunately, I don't currently have a reliable test case to ensure that this fixes the problems described in haskell#187. Hopefully with this patch available we can begin testing cabal-install and Stack against the change and see if it resolves the issues.
I've opened up #202 with the async exception detection logic I described above. |
This commit SHOULD NOT BE MERGED TO master. It adds an extra-dep for hackage-security from Hackage to work around #3073 and haskell/hackage-security#187. Hopefully this will be merged and released to Hackage.
I can also report that, with my patch in #202, Stack no longer has the buggy behavior described in commercialhaskell/stack#3073 (see my comment commercialhaskell/stack#3073 (comment)). |
This commit simply imports the code from the filelock package verbatim into a subdirectory, filelock. Depending on filelock as an external package instead would be more straightforward, but I'm not sure what the rules for external dependencies are here.
This commit simply imports the code from the filelock package verbatim into a subdirectory, filelock. Depending on filelock as an external package instead would be more straightforward, but I'm not sure what the rules for external dependencies are here.
* Use file instead of dir locking #187 This commit simply imports the code from the filelock package verbatim into a subdirectory, filelock. Depending on filelock as an external package instead would be more straightforward, but I'm not sure what the rules for external dependencies are here. * Switch to upstream filelock Given that the extra dependency doesn't seem to be a problem, remove the inlined code. If in fact the dependency should be avoided, just ignore this commit and use the parent.
* Detect asynchronous exceptions via their types #187 This commit uses the same async-exception detection mechanism as is used by the safe-exceptions package, via checking if the given exception is cast to a SomeAsyncException. (On older GHCs without SomeAsyncException, it contains a hard-coded list of async exception types.) It then ensures that: * Throwing via throwChecked always generates a synchronous exception * Catching via catchChecked (et al) never catches an asynchronous exception Unfortunately, I don't currently have a reliable test case to ensure that this fixes the problems described in #187. Hopefully with this patch available we can begin testing cabal-install and Stack against the change and see if it resolves the issues. * Treat Timeout as an async exception too * Remove exceptions not actually considered async
This reverts commit d91afd3.
Based on commercialhaskell/stack#3073 , I suspected hackage-security wasn't handling async exceptions quite right. Indeed, a search for SomeException led me to find the following spots where SomeException gets caught and not rethrown:
Relevant to the linked issue:
hackage-security/hackage-security/src/Hackage/Security/Client/Repository/Remote.hs
Line 278 in 226e1e4
Part of hackage-repo-tool:
hackage-security/hackage-repo-tool/src/Hackage/Security/RepoTool/Util/IO.hs
Line 84 in 226e1e4
I also noticed that the UpdateFailed exception constructor is never used -
hackage-security/hackage-security/src/Hackage/Security/Client/Repository.hs
Line 336 in 226e1e4
The text was updated successfully, but these errors were encountered: