Skip to content
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

Handle SIGBREAK (Windows console close) #2473

Merged
merged 2 commits into from Jul 5, 2018

Conversation

DSpeichert
Copy link
Member

SetConsoleCtrlHandler is not needed because SIGBREAK is also sent
on window close. The process is killed when the thread handling
SIGBREAK returns. That's why we must handle it synchronously
and wait for other threads to finish first. The Windows-defined
timeout is only 5 seconds but there is no way to change that.

https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals

Fixes #1277
Fixes #1853

SetConsoleCtrlHandler is not needed because SIGBREAK is also sent
on window close. The process is killed when the thread handling
SIGBREAK returns. That's why we must handle it synchronously
and wait for other threads to finish first. The Windows-defined
timeout is only 5 seconds but there is no way to change that.

https://docs.microsoft.com/en-us/windows/console/ctrl-c-and-ctrl-break-signals

Fixes otland#1277
Fixes otland#1853
src/signals.cpp Outdated
#else
// this must be a blocking call as Windows calls it in a new thread and terminates
// the process when the handler returns (or after 5 seconds, whichever is earlier)
signal(SIGBREAK, dispatchSignalHandler);
Copy link
Contributor

Choose a reason for hiding this comment

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

There's one problem - dispatchSignalHandler is not signal-safe, invoking it may result in a deadlock (and loss of state).

Copy link
Member Author

Choose a reason for hiding this comment

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

When we invoke it for SIGBREAK, the process is going to be terminated very soon. I don't think queuing up work from other signals would make a difference as we're shutting down anyway. Or is that not what you meant?

Copy link
Contributor

Choose a reason for hiding this comment

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

As far as I understand SIGBREAK, if you use the standard API (the signal function), the handler will be invoked on an existing thread(as opposed to SetConsoleCtrlHandler which spawns a new thread that runs the handler) in an asynchronous manner. The deadlock can occur if a thread is interrupted while holding a mutex and the handler attempts to lock that mutex. Only signal-safe functions and lockfree atomic operations are allowed on mutable shared state.

Copy link
Member Author

Choose a reason for hiding this comment

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

You are right about this being the expected behavior. However, in my tests, it appears that in fact a new thread is created. If the handler was running in an existing thread, it would either block entirely or it wouldn't need to "join" other threads. Yet, it does, because Windows is creating this new thread AND watching for it to finish (and then kills the rest of the process, if any).

image
Above is a debugger showing threads at a breakpoint set on the Signals::dispatchSignalHandler. The KernelBase.dll thread doesn't exist during normal runtime.

The handling of SIGINT looks the same, and the documentation specifically mentions:

Note When a Ctrl+C interrupt occurs, Win32 operating systems generate a new thread to handle the interrupt. This can cause a single-thread application, such as one ported from UNIX, to become multithreaded, potentially resulting in unexpected behavior.

So I believe this is a correct implementation. Also, the reason I don't exit the process with a code (e.g. ExitProcess(0)) is because after the thread returns nothing, it the "default" handler will return the special "window closed code":

The program '[9944] theforgottenserver.exe' has exited with code -1073741510 (0xc000013a).

Found online:

Exit Code 0xC000013A means that the application terminated as a result of a CTRL+C or closing command prompt window

Copy link
Contributor

Choose a reason for hiding this comment

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

Windows development is harder than I thought... anyway, could you leave a mention of this fact in a comment (that dispatchSignalHandler does not need to be signal-safe on Windows)?

@DSpeichert DSpeichert merged commit b92d82a into otland:master Jul 5, 2018
@DSpeichert DSpeichert deleted the sigbreak branch July 5, 2018 01:10
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.

SetConsoleCtrlHandler
3 participants