Let’s start by considering the next minimal scenario:
-
Install automatic package reloader go to
Preferences\Package Settings\Automatic Package Reloader\Settings
and add this:{“reload_on_save”: true}
-
Create a foo folder in
Data\Packages
with the next 3 files:
start.py:
from foo.a import A
a.py:
from foo.b import global_b
class A():
def __init__(self):
print("A 1")
somekey = global_b.load().get("somekey", [])
print("A 2")
if not somekey:
global_b.set_value("somekey", somekey)
print("A 3")
global_a = A()
b.py:
import sublime
class SettingsManager():
def __init__(self, name="foo.sublime-settings"):
self.name = name
def load(self):
return sublime.load_settings(self.name)
def set_value(self, key, value):
instance = sublime.load_settings(self.name)
instance.set(key, value)
sublime.save_settings(self.name)
return value
global_b = SettingsManager()
- Open
start.py
and start pressing save till Sublime hangs (do it as fast as possible…), sometimes it will hang fast other times it’ll take longer.
Here’s the thing, after a bit ammount of researching I’ve figured out the problem here basically a deadlock will be created between the next threads:
- The one that spawns
sublime_api.settings_set(self.settings_id, key, value)
- And the one that calls https://github.com/randy3k/AutomaticPackageReloader/blob/master/reloader/reloader.py#L138
Few questions:
- In order to fix the deadlock I’d like to be able to find a way to reproduce it faster, right now it can take me quite a lot of time… any idea to force it to appear faster?
- The sublime settings family functions… what are they doing behind the curtains? Are they thread safe?
- What’s the explanation of this deadlock and what’s the proper way to fix it (in code)? I mean, in the above minimal code it can take quite a lot of effort to make it appear but with different larger code using the same pattern the deadlock will appear much more easily… which it’s really disturbing while you’re coding plugins as basically you need to kill the plugin_hosts processes or restart sublime.
If you’re a windows user you can check the deadlock by using process explorer and checking the threads stack frames… on linux I guess using stuff gdb or similars would help. Also… there is this https://docs.python.org/3.7/library/sys.html#sys._current_frames that’s really helpful to know more about this interesting stuff.
Anyway, the above case tries to be a mcve to learn more about deadlocks, unfortunately it isn’t fast to reproduce… The whole point of this thread is mainly to know how to catch deadlocks and knowing the best practices to avoid them. I’ve created this little testcase after spending a lot of time debugging and learning a bit about this stuff.
Thanks in advance and sorry for the long thread