sublimehq / sublime_text

Issue tracker for Sublime Text
https://www.sublimetext.com
801 stars 35 forks source link

Kill any plugin which hangs the Sublime Text (`sublime_text.exe`) for more than 30 seconds. #2979

Open evandrocoan opened 4 years ago

evandrocoan commented 4 years ago

A more abroad solution for plugins/packages hanging Sublime Text are discussed on Packages are allowed to hang Sublime Text Indefinitely #1463

Here, I just propose a small/lightweight solution while #1463 is not implemented or decided what to do about it.

The solution is quite simple, if some plugin callback is hanging the Sublime Text interface for more than 30 seconds. Kill this call, and throw an error on the Sublime Text console for this alarming plugin.

Also, immediately unload this plugin from memory because it could manage to immediately get itself hanging Sublime Text main thread again. Which invalidates this fix.

How to?

When Sublime Text dispatches some plugin command which can hang its main process, also dispatch some timer with a 30 seconds timeout. If the plugin did not completed/returned from this callback after 30 seconds (of hanging the User Interface). Triggers the callback kill and the immediate plugin unload.

FichteFoll commented 4 years ago

Seeing that this is a potential temporary solutioin for handling #1463, I don't understand why you opened this as a separate issue instead of adding a comment with this suggestion to the issue describing the problem?

evandrocoan commented 4 years ago

Because saw some real complicated solutions proposals on that issue, and I am more inclined to close that issue in favor of this new and simpler solution (so we can get a implementation for this sooner as this year, other than maybe 20 years from now). As I understand Sublime Text environment, no plugin ever should block the main Sublime Text UI for more than a few seconds. Any plugin doing this for more than 30 seconds is definitely broken/bugged or really bad designed (i.e., it should run in a thread instead).

And this my experience for faw, if something is blocking Sublime Text UI, it is because was bad designed and should be fixed. Sublime Text killing and unloading it, should be enough for my to figure out which plugin was doing so and fix its behavior. So far, if some plugin is randomly and rarely blocking the main Sublime Text UI indefinitely, I have a real no ideia of which plugin is doing so, unless I start a binary search, but I cannot work for months with half my stuff (plugins) disabled.

Now, let us suppose I found the plugin by binary search. Where exactly it was blocking Sublime Text main UI? Another great problem to solve if the plugin code base it real big. With this proposal, I hope Sublime Text would show the stack trace of the call which hanged its UI, then, figuring out how to fix the plugin should be a lot easier.

FichteFoll commented 4 years ago

Note that the only option for ST to actually "stop" a plugin is to kill the plugin_host process. You cannot kill threads, so the only thing the plugin_host itself could do would be launching a background thread that is used to track the runtime of event hooks and extracting a traceback from the thread this is running on (probably subject to races) and then kill the entire process.

evandrocoan commented 4 years ago

On this https://stackoverflow.com/questions/323972/is-there-any-way-to-kill-a-thread

I find this attempt at least to be promising:

  1. http://tomerfiliba.com/recipes/Thread2/

The thread2 module is an extension of the standard threading module, and provides the means to raise exceptions at the context of the given thread. You can use raise_exc() to raise an arbitrary exception, or call terminate() to raise SystemExit automatically.

It uses the unexposed PyThreadState_SetAsyncExc function (via ctypes) to raise an exception in the context of the given thread.

Anyways, killing the whole plugin_host and printing the threads stack trace should be more than enough. This is certainty 100% better than keeping Sublime Text hanging for ever or for a long time.

There no need to auto reload plugin_host because if Sublime Text hangs for more than 30 seconds, I am killing plugin_host anyways. The difference on this case would be the stack trace I should see on the Sublime Text console.

wbond commented 4 years ago

The page you linked to @evandrocoan makes it clear it will only work if not hung on a blocking call. Additionally, all blocking API calls happen in the main thread, and async calls in a single background thread.

However, trying to abort a thread without the thread using some sort of sentinel and exiting itself isn't really do-able. Which comes back to requiring the plugin to not be blocking on something. I would hazard to guess that most hung processes are do to blocking IO and not an infinite loop in Python.

Probably the closest thing would be for the primary Sublime Text process to kill a plugin_host if it hasn't responded to a message in a sane amount of time. This would be similar to your average browser when it pops up an alert indicating the page is unresponsive. You don't get a backtrace or anything, just the opportunity to kill the thing that is out of control.