Add check to determine if a console window is present #2532
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Problem
Uvicorn hangs when launched under PyCharm general run configuration on Windows.
User use case: updating code that affects web page content.
Reason
The main Uvicorn process waits for its child process (
Server.run()
) to terminate.Why does it hang?
As we can see in the Uvicorn code above for terminating the process on Windows, Uvicorn explicitly sends
CTRL_C_EVENT
. The problem is thatCTRL_C_EVENT
may be silently ignored in a number of cases.Our case is that by default PyCharm runs programs as windowless, but it is not specific to PyCharm as it’s a default way to launch cmd line in Java (using
java.lang.ProcessBuilder
).Proposed solution
The basic idea is to add a check to determine if a console window is present. It is a sufficient flag whether
CTRL_C_EVENT
would be ignored by a child process or not.A few words about process creation on Windows
On Windows, processes are typically created using the Windows API or high-level abstractions provided by programming languages. The main function for process creation is CreateProcess(), which spawns a new process.
This function has
dwCreationFlags
arg. It contains three flags:CREATE_NEW_CONSOLE
,CREATE_NO_WINDOW
, andDETACHED_PROCESS
which determine whether the process will have a console. For example, when we run a Python script from a console, all the flags are set to 0. It means that a process has a console with a window (inherit).You can read more here
The Problem
If the process is launched with the
CREATE_NO_WINDOW
flag, its child processes lose their association with the console window and cannot process theCTRL_C_EVENT
signal. This prevents us from properly terminating child processes.java.lang.ProcessBuilder
(the default way to call CreateProcess()) runs CreateProcess() withCREATE_NO_WINDOW = 1
(link).Steps to Reproduce
Below is a Python script example to reproduce the process creation behavior. Also Java file
Main.java
that runsget_console_flag.py
usingProcessBuilder
.Run the script from the standard terminal (PowerShell). You’ll get: "Console state is ConsoleState.NORMAL_CONSOLE".
Run the script from PyCharm or using the
Main.java
file (the first arg is a path to python.exe, the second arg is a path toget_console_flag.py
file). You’ll get: "Console state is ConsoleState.CONSOLE_WITHOUT_WINDOW."The correct solution
It should be noted that if the parent process does not have a console, the child process will have a new console. This is a rare case, but for a perfect solution, this case must also be handled.
A hack is needed here: we check that there is a console but no window, and only in this case do we call
terminate
. (see the attached patch)Files:
get_console_flag.py
Main.java
Terminate_only_if_there_is_CREATE_NO_WINDOW.patch