8000 Improve catching of Ctrl-C and logging of Async exceptions in UCM by ChrisPenner · Pull Request #2498 · unisonweb/unison · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Improve catching of Ctrl-C and logging of Async exceptions in UCM #2498

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 4 commits into from
Oct 15, 2021

Conversation

ChrisPenner
Copy link
Member

Overview

Previously ctrl-c on the UCM prompt would just exit, which is really annoying and out-of-line with how shells typically handle things. In most shells, ctrl-c simply clears the prompt and re-prompts. This PR makes that the default behaviour.

You can still exit the CLI using ctrl-d.

Before:

.> cd .base
.base>
$ echo $?
130

After:

.base>
Interrupt.
.base>

Implementation notes

  • I installed the recommended ctrl-c handler from haskeline.
  • The awaitInput action wasn't catching async exceptions, and UserInterrupt is an async exception (catch ignores them), so I adjusted to catchSyncOrAsync to allow it to catch User Interrupts.

Interesting/controversial decisions

We have to be careful using catchSyncOrAsync, using it incorrectly can make a process unkillable. It's important that we rethrow on all exceptions OTHER than UserInterrupt

We might not actually need the catchSyncOrAsync if the haskeline catch is enough.

Loose ends

A ctrl-c during intensive operations (e.g. if you run a slow find command and ctrl-c, UCM will still exit with an async exception).
I tried to catch this in multiple spots, but it seems that some async operation inside UCM is converting UserInterrupt into an AsyncCancelled somewhere. I'd like to track that down eventually, but this is an incremental improvement for now.

@ChrisPenner ChrisPenner added the codebase-manager / ucm Issues related to the codebase editor CLI application (ucm) label Oct 14, 2021
interruptHandler (asyncExceptionFromException -> Just UserInterrupt) = awaitInput
interruptHandler e = error (show e)
interruptHandler e = putStrLn ("Exception: " <> show e) *> throwIO e
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous error wasn't actually rendering the exception for some reason, perhaps because we weren't catching async exceptions, but I think re-throwing is probably more correct here regardless.

@@ -170,9 +176,9 @@ main dir welcome initialPath (config, cancelConfig) initialInputs runtime codeba
e
x -> do
writeIORef pageOutput True
pure x) `catch` interruptHandler
pure x) `catchSyncOrAsync` interruptHandler
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to be careful when catching async exceptions; should be okay here as long as we're sure to rethrow all exceptions other than UserInterrupt

-- Run the main program loop, always run cleanup,
-- If an exception occurred, print it before exiting.
(loop (HandleInput.loopState0 root initialPath)
`withException` \e -> putStrLn ("Exce 10000 ption: " <> show (e :: SomeException)))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We were silently exiting without printing errors or exceptions before, this prints the exception, then re-throws, which then runs the finally

@ChrisPenner ChrisPenner self-assigned this Oct 14, 2021
Copy link
Contributor
@aryairani aryairani left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great!

where
-- catch ctrl-c and simply re-render the prompt.
haskelineCtrlCHandling act =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion:

haskelineCtrlCHandling act =
  forever do
    Line.handleInterrupt
      (Line.outputStrLn "Interrupted.")
      (Line.withInterrupt act)

Otherwise, each invocation of the action after the first would run with asynchronous exceptions (interruptibly) masked, as that is the behavior of catch/handle.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also: perhaps we don't want to even print "Interrupted"? That's the behavior of ghci, anyway.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great Catch!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't use forever since we need to return the input on a success; but switched to return a maybe and then check the result outside of the masked section instead 👍🏼

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a while at least, we were trying to use friendly complete sentences instead of the more commonplace robotic computer messages. I couldn't come up with an alternative suggestion I liked better than "Interrupted.", but wanted to put the thought out there.

@@ -44,6 +44,8 @@ import qualified Unison.CommandLine.Welcome as Welcome
import Text.Regex.TDFA
import Control.Lens (view)
import Control.Error (rightMay)
import UnliftIO (catchSyncOrAsync, throwIO, withException)
8000 Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe in a follow-up PR we could replace Control.Exceptiom with UnliftIO.Exception in this module, and maybe beyond
Since they have different semantics, it's confusing to have both in use.

@ChrisPenner ChrisPenner merged commit fdf7932 into trunk Oct 15, 2021
@ChrisPenner ChrisPenner deleted the cp/catch-ctrl-c-haskeline branch October 15, 2021 15:22
@hojberg hojberg mentioned this pull request Nov 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
codebase-manager / ucm Issues related to the codebase editor CLI application (ucm)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0