Frontend Extension: Threading + while True loop


#1

Hey there!
I’m working on building my own mopidy extension for controlling playback with many buttons and potentiometers with the Raspberry Pi. Because the Raspberry Pi does not have analog input I’m using an analog-to-digital-converter, which I readout with SPI and this Adafruit Module.

My extension needs to continuously (in a while True loop) read values from the ADC. I set it up to work as follows: if a specific value of one ADC channel is bigger than X I want to call a function, e.g. self.core.playback.play(). This works in principle. I tried two options:

  1. running the loop in a simple while loop, which will block all other mopidy actions (Thought maybe Mopidy starts each frontend extension in its own thread),
  2. running the loop in a new thread will enable mopidy to work on its own and my extension also works on a separate thread - but if i want to stop mopidy with systemctl, it will not properly stop since the separate thread is not stopped and therefore the service will finally ‘fail’ after timeout.

Do I maybe need to run my extension as a pykka.ThreadingActor ? If so, how? Any ideas of how to solve this issue?
Thanks :slight_smile:


#2

Have you looked at the Pykka docs at https://www.pykka.org/en/latest/ ? I would definitely start there.

You probably want to create your GPIOManager instance in on_start() rather than __init__().

For ThreadingActor, this method is executed in the actor’s own thread, while init() is executed in the thread that created the actor.

(from https://www.pykka.org/en/latest/api/?highlight=start#pykka.Actor.on_start)


#3

This sounds what I was looking for. I’m quite new to mopidy, so I did not yet check out Pykka - will do now :slight_smile: Thanks


#4

Hi again. So what I did is the following:

class Tubipy(pykka.ThreadingActor, core.CoreListener):
    ...    
    def on_start(self):
        from .gpio_input_manager import GPIOManager
        self.gpio_manager = GPIOManager(self, self.config['tubipy'])

which does exactly what I wanted. The GPIOManager is executed in a separate thread.

Now, if for some reason, lets say KeyboardInterrupt, Mopidy stops, the while loop in my GPIOManager still does not stop.

I even set an additional condition to the while loop like

def deactivate(self):
    self.active = False

while self.active:
    # do something

and in the frontend I tried to stop the while loop by changing self.active from True to False by implementing the on_stop() and on_failure() functions, like so:

def on_stop(self):
    self.gpio_manager.deactivate()

def on_failure(self, exception_type, exception_value, traceback):
    self.gpio_manager.deactivate()

Check out the code on my github repo if you want. Do you know what I am doing wrong?