Repository: rotki/rotki

Issue:
Killing tasks on Startup fails on Windows (PC)
## Problem Definition

After an update Rotki would not restart on its own.

Closing the interface and restarting got no response at all (twice)

Logs showed an attempt to kill the tasks but recorded the failure.

## Logs

> 2024-04-19T13:56:55.600Z: (info): Checking for update
> 2024-04-19T13:56:57.360Z: (info): Update for version 1.32.2 is not available (latest version: 1.32.2, downgrade is disallowed).
> 2024-04-19T13:57:03.347Z: Restarting rotki-core> > 
> 2024-04-19T13:57:03.350Z: Starting windows process termination
> 2024-04-19T13:57:07.274Z: Currently running: 246 tasks
> 2024-04-19T13:57:07.280Z: Detected the following running rotki-core processes: 2880, 7092
> 2024-04-19T13:57:07.282Z: Preparing to call "taskill /f /t /PID 2880 /PID 7092" on the rotki-core processes
> 2024-04-19T13:57:07.752Z: Call to taskkill exited
> 2024-04-19T13:57:07.754Z: The 2 processes are still running. Waiting for 2 seconds
> 2024-04-19T15:18:54.047Z: 

Investigation and Remediation

Look for any running instances 

> C:\Windows\system32>tasklist | find "rotki"
> rotki.exe                    16380 Console                    1     17,608 K
> rotki.exe                    13204 Console                    1     10,436 K
> rotki.exe                    14164 Console                    1      8,596 K

Kill the running instances:

> C:\Windows\system32>taskkill /f /t /PID 16380 /PID 13204 /PID 14164
> SUCCESS: The process with PID 13204 (child process of PID 16380) has been terminated.
> SUCCESS: The process with PID 14164 (child process of PID 16380) has been terminated.
> SUCCESS: The process with PID 16380 (child process of PID 7124) has been terminated.

Could it be the reason It can't kill the running instances is because there is a typo in the code?

As per the log;

2024-04-19T13:57:07.282Z: Preparing to call "taskill /f /t /PID 2880 /PID 7092" on the rotki-core processes
----------------------------------------------------^^^^------------

The command is taskkill (two k's) NOT taskill (one k). 


-->


### System Description


Operating system: 

`systeminfo | findstr /C:"OS"`

OS Name:                   Microsoft Windows 10 Home
OS Version:                10.0.19045 N/A Build 19045
OS Manufacturer:           Microsoft Corporation
OS Configuration:          Standalone Workstation
OS Build Type:             Multiprocessor Free
BIOS Version:              LENOVO DUCN33WW, 2021-08-10


Rotki version: 1.32.2



Discussion:
BTW Successfully killing the tasks allowed Rotki to start.

There should be a message informing the user (instead of what they currently see which is nothing) and the failure to open the interface needs to be logged since Rotki the process technically *did* restart.

Possibly using [PyGetWindow](https://pygetwindow.readthedocs.io/en/latest/)?
This is the underlying issue for https://github.com/rotki/rotki/issues/7382. So they can close together.

> There should be a message informing the user (instead of what they currently see which is nothing) and the failure to open the interface needs to be logged since Rotki the process technically did restart.

Yes there should be. I am not sure how complex it would be. This is a question for @rotki/frontend .
> BTW Successfully killing the tasks allowed Rotki to start.
> 
> There should be a message informing the user (instead of what they currently see which is nothing) and the failure to open the interface needs to be logged since Rotki the process technically _did_ restart.
> 
> Possibly using [PyGetWindow](https://pygetwindow.readthedocs.io/en/latest/)?

There is supposed to be a message that the electron process shows if you normally open the application with left-over processes running, but because of #7382 interfering, the old instance of electron reaches an almost terminated state and the new one is terminated and instead focuses on the old one that is still running.

I have a few ideas on how we are going to try to fix this:
- See if there is something we can do in PyInstaller itself to make sure that terminating the bootstrap process terminates the python process
- Change the order of actions when updating and restarting so that the backend termination happens before that to make sure that there is nothing that can interfere with the restart.

Resolving patch:
diff --git a/rotkehlchen/server.py b/rotkehlchen/server.py
--- a/rotkehlchen/server.py
+++ b/rotkehlchen/server.py
@@ -56,9 +56,17 @@ def main(self) -> None:
         hub.threadpool_size = 2
         hub.threadpool.maxsize = 2
         if os.name != 'nt':
-            gevent.hub.signal(signal.SIGQUIT, self.shutdown)  # type: ignore[attr-defined,unused-ignore]  # pylint: disable=no-member  # linters don't understand the os.name check
+            gevent.hub.signal(signal.SIGQUIT, self.shutdown)
+            gevent.hub.signal(signal.SIGTERM, self.shutdown)
+        else:
+            # Handle the window control signal as stated here https://pyinstaller.org/en/stable/feature-notes.html#signal-handling-in-console-windows-applications-and-onefile-application-cleanup  # noqa: E501
+            # This logic handles the signal sent from the bootloader equivalent to sigterm in
+            # addition to the signals sent by windows's taskkill.
+            # Research documented in https://github.com/yabirgb/rotki-python-research
+            import win32api  # pylint: disable=import-outside-toplevel  # isort:skip
+            win32api.SetConsoleCtrlHandler(self.shutdown, True)
+
         gevent.hub.signal(signal.SIGINT, self.shutdown)
-        gevent.hub.signal(signal.SIGTERM, self.shutdown)
         # The api server's RestAPI starts rotki main loop
         self.api_server.start(
             host=self.args.api_host,


Test patch: