Python threads and AbortRequested
#1
I have written a Python script that uses threads (as daemons), and I'd like to use the AbortRequested signal to clean those threads up when Kodi exits. Replacing my threads' while loop with
python:
while(not xbmc.abortRequested)
causes it to exit immediately. Checking the state of AbortRequested with
python:
print '\n\t KODI says that AbortRequested is: ' + str(xbmc.abortRequested)
returns 
python:
07:41:42.262 T:9028 DEBUG: KODI says that AbortRequested is: True

Can someone explain to me how I am misusing this? I had expected a False return until Kodi exits, and had intended to use the return as a basis for tearing down the threads. There is no critical state information at play here, so there's nothing to save...but I'd like to clean things up and allow Kodi to exit as it should in a timely manner.

The script in question

The result of the script


I'm quite certain that people that know how to code in Python will find the above script in many ways offensive. I've learned just enough Python to get this working, and intend to convert it to an addon as my understanding of how to do that improves, so if you'd like to provide feedback (shy of 'please, step away from the keyboard') I am certainly interested in your feedback.  

TLDR; Why would xbmc.abortRequested return True when Kodi is in operation?
Reply
#2
you should use xbmc.Monitor()
https://github.com/XBMC-Addons/service.x...on.py#L178
Reply
#3
(2017-12-01, 16:56)wsnipex Wrote: you should use xbmc.Monitor()
https://github.com/XBMC-Addons/service.x...on.py#L178
 Thank you for that.
Using the following code as found here, I am not seeing the expected result
python:
while not monitor.abortRequested():
    time.sleep(1)
    xbmc.log("RadioHD %s No Abort" % time.time(), level=xbmc.LOGNOTICE)    
    if monitor.waitForAbort(1) :
        xbmc.log("**************RadioHD*********************", level=xbmc.LOGNOTICE)
        xbmc.log("RadioHD %s" % time.time(), level=xbmc.LOGNOTICE)
        t.join()
        m.join()
        sys.exit(0)

produces this output so the abortRequestd code block isn't running.
html:

19:26:23.921 T:11040  NOTICE: destroy
19:26:23.921 T:11040  NOTICE: unload skin
19:26:25.675 T:7448  NOTICE: RadioHD 1512177985.67 No Abort
19:26:27.675 T:7448  NOTICE: RadioHD 1512177987.67 No Abort
19:26:28.937 T:11040   ERROR: CPythonInvoker(3, special://skin/scripts/radio.py): script didn't stop in 5 seconds - let's kill it
19:26:29.675 T:7448  NOTICE: RadioHD 1512177989.67 No Abort
19:26:31.676 T:7448  NOTICE: RadioHD 1512177991.68 No Abort
19:27:36.981 T:11040  NOTICE: unload sections

I know this isn't a 'how to write Python scripts' forum, and I'll keep plugging away at it... but I think I'm a bit lost here. Smile

Could this be due to the script not being run as an addon/service as it should?
Reply
#4
Quote:Could this be due to the script not being run as an addon/service as it should?
No, this shouldn't have any affect on how the script runs (as far as I know).

But the output does seem unexpected. Line 6 indicates that Kodi is indeed shutting down. Yet, Line 7 and Line 8 indicate that
python:
while not monitor.abortRequested():
is still returning True and 
python:
if monitor.waitForAbort(1):
is still returning False

I'm sure we're missing something obvious, but I can't see what it is at the moment. For testing purposes, try the following code instead, which does the same thing as your original code while foregoing the call to 'monitor.abortRequested'.

python:
while not monitor.waitForAbort(2):
    xbmc.log("RadioHD %s No Abort" % time.time(), level=xbmc.LOGNOTICE)

xbmc.log("**************RadioHD*********************", level=xbmc.LOGNOTICE)
xbmc.log("RadioHD %s" % time.time(), level=xbmc.LOGNOTICE)
t.join()
m.join()
# The following line has been commented out because it's unnecessary.
# sys.exit(0)


Some notes about 'Thread.join' that may be helpful:
  • In case you're not aware, a thread's 'join' method does not explicitly stop the thread e.g. if you have a thread object called 'thread_alpha' and 'thread_alpha' runs its own 'while' loop, calling 'thread_alpha.join()' does not magically break the loop and stop the thread. As stated in the Python docs, calling 'thread_alpha.join()' simply makes your script wait until thread_alpha finishes i.e. the code in 'thread_alpha' runs to completion.
  • The 'Thread.join' method takes a timeout argument. If you don't provide a timeout value, then the 'join' method blocks (i.e. waits) indefinitely until the code in 'Thread' finishes, so potentially forever. To ensure the call doesn't prevent Kodi from shutting down cleanly (and to avoid 'script didn't stop in 5 seconds' log messages), I suggest providing a timeout (I  usually use 4 or 4.5 seconds) for all calls to 'Thread.join' (as well as any other blocking methods).

I'm sorry I couldn't be more helpful with regards to your original problem, but I hope at least some of that was helpful.
Reply
#5
Quote:No, this shouldn't have any affect on how the script runs (as far as I know).
Good to know. I've done a lot of reading which helps, but it takes some time to understand the implications of the many choices that go into this stuff.
Quote:But the output does seem unexpected. 
Indeed. I've done quite a bit of reworking to try to understand why, but it really hasn't clarified anything. I agree there's probably something simple I'm doing wrong, so I've done a lot of searching and rewriting based on what I've read to try to understand what exactly is going on. TBH, the harder I try to kill these threads, the longer they seem to hang the shutdown process. 

This script is capturing an endless stream of text data from an HD radio receiver over a UDP socket, so the receiver() spends its time waiting for that data to arrive and fills the queue as it does. That data is handed to formatData(), then sent to label controls in Kodi. Another thread watches a few InfoLabels set in the skin based on user button presses and sends those presses as commands to the HD radio over the UDP connection. Both threads are infinite loops using the same socket. These are the only two threads started for the lifespan of the script. Given this design, multiprocessing seems just as viable a design strategy and should be easier to shut down gently. I reworked using mp but the processes never reported starting. I'll fiddle with that some more later to try to understand why, but it isn't a priority at this minute. 

Here is the current threaded script: radio.py
And the multiprocessing version, just because:   radioMP.py

When I reconfigure the monitor loop to the following:
python:
    while not monitor.waitForAbort(1) :
            xbmc.log("**************RadioHD*********************", level=xbmc.LOGNOTICE)
            xbmc.log("RadioHD %s" % time.time(), level=xbmc.LOGNOTICE)
            break
The result is a log entry shortly after the script starts indicating that waitForAbort returned True. Everything continues running normally. A subsequent shutdown results in the 'didn't stop' message, and Kodi tears things down on its own a few seconds later. 

Thanks for the information about thread.join(). Some information I read led me to believe that may be useful in stopping them, but I now understand why that would not help my situation.

I will keep playing around with things to try to better understand what I'm doing wrong. Next steps will be to package this into a service addon, and then look at pumping the audio through a socket to be brought into Player. That should be fun Smile

Thank you very much for the help!
Reply
#6
Why use "break" in your loop? That effectively ends your loop on the first iteration which can't be the plan.

How long do your threads take to finish once you've requested the shutdown? If they take longer than 5 seconds then that error message is going to appear.

Is there a way that you can cause the threads to end gracefully? e.g. if they are each running loops they should also be checking monitor.abortRequested(), if they don't then I think you end up with the warning in your log file.
Quote:But the output does seem unexpected. Line 6 indicates that Kodi is indeed shutting down. Yet, Line 7 and Line 8 indicate that
 
Code:
while not monitor.abortRequested():
is still returning True and 
 
Code:
if monitor.waitForAbort(1):
is still returning False.  
Those two outcomes are entirely consistent. While no shutdown has been requested monitor.abortRequested() returns False. You then use not to invert this so the result is a while True: loop that runs until a shutdown is requested. As no shutdown has been requested, monitor.waitForAbort(1) will return False so the commands in that if statement are not run. This means that you see the first log statements in the while loop, but not those in the if block.

If I've missed something obvious, please let me know.
BBC Live Football Scores: Football scores notifications service.
script.squeezeinfo: shows what's playing on your Logitech Media Server
Reply

Logout Mark Read Team Forum Stats Members Help
Python threads and AbortRequested0