Bug Python in Kodi does not support daemon threads
#1
A quote from the official documentation:
Quote:daemon

A boolean value indicating whether this thread is a daemon thread (True) or not (False). This must be set before start() is called, otherwise RuntimeError is raised. Its initial value is inherited from the creating thread; the main thread is not a daemon thread and therefore all threads created in the main thread default to daemon = False.

The entire Python program exits when no alive non-daemon threads are left.

However it does not work in Kodi Python. Let's take the following script:
Code:
from __future__ import print_function
from threading import Thread
from time import sleep


def worker():
    i = 1
    while True:
        print('Working {}.....'.format(i))
        i += 1
        sleep(1)


t = Thread(target=worker)
t.daemon = True
t.start()
sleep(5)

In desktop Python (2 or 3) the script will print "Working n..." 5 times and exit. But in Kodi the script will run forever and you will have to kill a Kodi process to terminate it. So obviously the daemon property is not respected.

I noticed this a while ago but forgot about it, and now I'm developing a remote Python debugger with a web-UI which runs in a separate thread, and this came as an unpleasant surprise. In Kodi I have had to add some additional code to guarantee that the web-UI will be terminated when a script being debugged is finished.
Reply
#2
sadly this cannot work. python is single-threaded only at its very core. 'Threading' in python works by forking multiple interpreters and then communicating through an ipc bus, and that interfers with being loaded as a shared library by the kodi process. it will only ever work if run under through the 'python' binary.
Reply
#3
(2016-09-21, 11:19)ironic_monkey Wrote: sadly this cannot work. python is single-threaded only at its very core. 'Threading' in python works by forking multiple interpreters and then communicating through an ipc bus, and that interfers with being loaded as a shared library by the kodi process. it will only ever work if run under through the 'python' binary.

That is not true. Python is multi-threaded, but the main problem is that the PyObject that is the base data structure in Python is not inherently thread-safe, so Python uses the infamous Global Interpreter Lock or GIL to protect its internal data from concurrent access. Effectively, this means that you cannot run several CPU-bound threads at the same time. However, a number of base functions, implemented at C level, including I/O operations, release GIL while they run. So you can have several I/O-bound threads in Python. And a WSGI web-server that supports the web-UI is I/O-bound so it effectively runs in a separate thread. You can take my word for that.Smile. And time.sleep() also releases GIL BTW.
Reply
#4
yes, i was a bit imprecise. that's what i meant, the GIL. python basically implements a multitasking environment like modern hardware exposes inside its vm, and only a single thread ever executes concurrently as long as it interfaces with the python interpreter. so usually you hack around it by running multiple interpreters to actually get concurrent execution on separate cpus. you can release the GIL in your cpython code as long as you do not call any python functions in there, and that's used with some success for stuff like I/O.
Reply
#5
Anyway, Python does have threading module and threads are useful in certain scenarios. The problem is, that in Kodi-Python threads behave differently than in "normal" Python, and you need to take special measures to terminate thread(-s) after your addon script finishes.
Reply
#6
Can you let me know what the "special measures" are? I have a script that likely needs daemonised threads so I'd be keen to include any tidy up code.
BBC Live Football Scores: Football scores notifications service.
script.squeezeinfo: shows what's playing on your Logitech Media Server
Reply
#7
(2016-09-22, 00:34)el_Paraguayo Wrote: Can you let me know what the "special measures" are? I have a script that likely needs daemonised threads so I'd be keen to include any tidy up code.

I have to explicitly shutdown a WSGI server that runs in a separate thread. Since I'm developing a Python debugger it's not a problem to detect when the main script finishes. In your case to detect when the main script finishes you need to attach your own debugger to your script. This is the simplest example:

Code:
import sys


def trace(frame, event, arg):
    """A simple debugger function"""
    if frame.f_back is None and event == 'return':
        # We are returning from the top-most frame,
        # that is, from the main script.
        # Do your cleanup here
        return
    return trace


def set_trace():
    """Attach a debugger to the running script"""
    frame = sys._getframe().f_back
    # Unwind the call stack up to the top
    # and attach the debugger to all upper-level frames
    while frame is not None:
        frame.f_trace = trace
        frame = frame.f_back
    # Attach the debugger to the current stack frame
    sys.settrace(trace)


set_trace()

There are 2 potential problems:
1) In case of uncaught exception the cleanup code will be called several times, so you have to make sure that the cleanup will be run only once (e.g. with a conditional flag).
2) If you use a real debugger, it will replace your trace function so it won't run.
Reply
#8
Ok. Thanks Roman.
BBC Live Football Scores: Football scores notifications service.
script.squeezeinfo: shows what's playing on your Logitech Media Server
Reply
#9
@Roman_V_M

Does it work if using the older API method: thread.setDaemon(True) rather than thread.daemon = True? I am simply being curious.
Reply
#10
(2016-11-28, 09:12)angelblue05 Wrote: @Roman_V_M

Does it work if using the older API method: thread.setDaemon(True) rather than thread.daemon = True? I am simply being curious.

In the current CPhthon implementation there is no difference between both options:
Code:
def setDaemon(self, daemonic):
    self.daemon = daemonic
Reply
#11
Ok, thanks for the quick reply. Well this is unfortunate...
Reply
#12
+1. daemon = True don't work ((( need external aborting for stopping thread. I use this method:

class Thrd(threading.Thread):
def __init__(self, t, *args):
threading.Thread.__init__(self, target=t, args=args)
self._stop = threading.Event()
self.start()

def Target(externalAbort):
#...
if externalAbort() : break

stopThrd = False
def isStopped () : return stopThrd

thr1 = Thrd(Target, isStopped)
#....
stopThrd = True
Reply
#13
Yes, threading.Event can be used to pass binary flags between threads. But if your thread does some long running task in a loop, this loop should periodically check for Monitor.abortRequested() condition to terminate properly on Kodi exit.
Reply
#14
Yes, I know about this. In real code I would write if monitor.waitForAbort(5) or externalAbort() : break or add monitor.waitForAbort in isStopped () function. In the post I suggested a method for external abort for stopping thread (manually) stopThrd = True.
Reply

Logout Mark Read Team Forum Stats Members Help
Python in Kodi does not support daemon threads0