Sublime Forum

Does a TextCommand prevent any other threads from running?

#1

I apologize if this is more about python threading than sublime, but I don’t know if sublime is somehow involved.

I have a job scheduled by threading.Timer to produce some data in 5 seconds. When the data is produced, it will call set on a threading.Event().

If the user issues a command that uses the data, it will wait for the event on the main thread. It should never take long, but the wait has a timeout of 20 sec just in case.

What I notice is that if the user issues a command during the 5 seconds (i.e., after the job that produces data has been scheduled but before it has been run), its wait will timeout. The reason for that is that the producer job (which has been scheduled to run within a few seconds) won’t run until just after the 20 sec timeout. It seems that the wait on the event is delaying the timer thread. I have all my threads loaded with prints, and I’m not doing any explicit locking that would explain this.

Is there something subtle going on that I’m not aware of?

Edit: The event wait() was a red herring. It seems that the problem is while my TextCommand’s run() method is running, all other threads are suspended. Is that true? I thought it was the wait() because that’s the only blocking call in my run() method, but even if I put a sleep in the run() method, that seems to prevent other threads from running. If it is true that TextCommands don’t allow a thread switch, then my idea of having the main thread wait for a result that is about to be produced by a different thread won’t work.

0 Likes

#2

I just re-titled the topic to reflect the real question. It seems that while a TextCommand is running, no other threads run. If that’s the case, then a command cannot try to acquire a lock to access any shared data structure because no other thread will be able to release the lock. That’s kind of a big deal, and it affects the whole structure of the plugin.

0 Likes

#3

Without having tested and verified this myself, I can only tell you that I haven’t heard of a problem like this before. It could be possible, but it seems unlikely not to have surfaced in over 3 years of ST3.

0 Likes

#4

Interesting experiment:

import sublime
import sublime_plugin
import threading
import time
import datetime


class ExampleCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        print(datetime.datetime.now(), "Start command")

        def callback():
            print(datetime.datetime.now(), "I am the callback")

        timer = threading.Timer(1.0, callback)
        timer.start()

        print(datetime.datetime.now(), "Sleeping")
        time.sleep(5)

        print(datetime.datetime.now(), "Command done")

Sample output is:

>>> view.run_command("example")
2019-05-19 19:52:42.068581 Start command
2019-05-19 19:52:42.068890 Sleeping
2019-05-19 19:52:47.073942 Command done
2019-05-19 19:52:43.068975 I am the callback

Note that the callback happens one second after the “Sleeping” message, as expected, but it isn’t printed until after the command completes. The timestamps are out of order. I might chalk that one up to buffered output, but if I put a sys.stdout.flush() after every print, the result is the same.

It looks to me that threads can run concurrently with a TextCommand (whew!), but there is some kind of synchronization in place for the print output, not just output buffering. I was debugging my own synchronization with prints, so that’s where I went astray.

Is it documented what exactly happens to the text you print? If there is some buffering/sychronization that’s beyond the user’s control, then it’s pretty hard to use prints to debug synchronization issues. I’m still somewhat new to python in general - if prints aren’t the right tool for this, is there a better one?

1 Like

#5

sys.stdout is a sublime._LogWriter object that calls sublime_api.log_message(). (This isn’t documented, but you can see it in the sublime.py source.) Presumably, the synchronization occurs within sublime_api.

To avoid synchronization, you could try writing to a log file instead.

2 Likes