Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] No current event loop in thread "Dummy-1". #289

Open
1 task done
robbyfrt opened this issue Aug 30, 2023 · 7 comments
Open
1 task done

[BUG] No current event loop in thread "Dummy-1". #289

robbyfrt opened this issue Aug 30, 2023 · 7 comments
Assignees
Labels
bug Something isn't working

Comments

@robbyfrt
Copy link

robbyfrt commented Aug 30, 2023

Software Versions:

  • Operating System: Ubuntu 20.02 inside docker on arm64
  • Python version: 3.8.10
  • BusyLight version: 0.26.1

General Type of Problem

  • Integrating into another project

Describe the Problem

So a simple script works great:

from busylight.lights.kuando import Busylight_Alpha

class Alarm:
    def __init__(self):
        self.light = Busylight_Alpha.first_light(reset=True, exclusive=True)
        self.traffic_colors = [(255,0,0), (255,255,0), (0,255,0)]

    def on_for_25(self, color_id: int):
        self.light.on(self.traffic_colors[color_id])

if __name__ == "__main__":
    alarm = Alarm()
    alarm.on_for_25(color_id=0)

However, using this simple class in a more complex software however returns the error shown below.

I am calling light.on() inside a callback function of a gstreamer pipeline, which was written using python bindings. I have no idea how gstreamer itself generates its threads. I am using it inside a callback similar to this: deepstream_test_1.py -Line 71

It may be easier for me to use lower level functionality for simply turning the light on for some time, but I am unsure where to start in your repo.

Use Case: I simply want to turn my busylight on when my script hast detected some activity. Your api seemed quite straighforward for that.

Expected Behavior
What you expected to happen.

Error Output

Traceback (most recent call last):
  File "run.py", line 105, in osd_sink_pad_buffer_probe
    alarm.on_for_25(color_id=0)
  File "<rootdir>/pipeline/common/io.py", line 9, in on_for_25
    self.light.on(self.traffic_colors[color_id])
  File "/usr/local/lib/python3.8/dist-packages/busylight/lights/kuando/busylight_alpha.py", line 50, in on
    self.add_task("keepalive", _keepalive)
  File "/usr/local/lib/python3.8/dist-packages/busylight/lights/taskable.py", line 51, in add_task
    self.tasks[name] = self.event_loop.create_task(coroutine(self))
  File "/usr/local/lib/python3.8/dist-packages/busylight/lights/taskable.py", line 21, in event_loop
    self._event_loop = asyncio.get_event_loop()
  File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Dummy-1'.
@robbyfrt robbyfrt added the bug Something isn't working label Aug 30, 2023
@JnyJny
Copy link
Owner

JnyJny commented Aug 30, 2023

Thanks for the bug report!

The root of the problem is an unfortunate design choice made by the Kuando engineering team: the Busylight family of lights will quiesce if they don't receive a keep-alive packet. This means if you turn the light on and it doesn't receive a keep alive it will turn off. To normalize it's behavior with the rest of the supported lights (turn it on and it stays on until commanded off), I added asyncio support that would schedule a keep alive task to write to the device every seven seconds. The error makes sense, there isn't an async event loop running.

A workaround for this is using the busylight.manager.LightManager class to access the light. Another workaround is replacing the Kuando device with one of the other supported devices that doesn't require constant software supervision.

I'll revisit this code and see what I can do for the use-case you've presented. I can at least catch those exceptions and issue some better guidance to users.

@robbyfrt
Copy link
Author

robbyfrt commented Aug 30, 2023

Wow, I didn't expect a reply so fast, thanks! I somehow managed to get the script working using the Busylight_Alpha class by initializing and calling the function at specific points in my script and setting the object to a global parameter. Seems more like a hack tbh, but I'll take it for now :). I will try out invoking LightManager later.

Thank you for clarifying the asnycio functionality, I would like a bit more control over the duration of the on-signal (it is currently on for around 25sec), but this is rather a nice-to-have-feature atm.

@JnyJny
Copy link
Owner

JnyJny commented Aug 30, 2023

Ok, after coffee (and really reading the stack trace you so helpfully provided) I have a better idea of what broke but I'm not sure how to fix it yet. The class busylight.lights.light.Light is an abstract base class with a mixin class busylight.lights.light.taskable.TaskableMixin that provide support for managing asyncio tasks. The previous busylight implementation used a thread for each active light to control light animations (and keepalives in the case of the Kuando family). I switched to asyncio so busylight could integrate into other async-aware frameworks like fastapi and it seems to be a cleaner solution than the threaded one. That said, I'm not an expert on asyncio so it's quite possible I'm doing something wrong and the gstreamer use case is exposing it somehow.

  File "/usr/lib/python3.8/asyncio/events.py", line 639, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Dummy-1'.

This is telling me that gstreamer is running your callback code in a thread, called "Dummy-1", and my call to asyncio.get_event_loop() is failing. I've got some reading to do to figure out how this should be handled correctly. At the very least I could handle the RuntimeError so it doesn't bubble up and crash the caller, but that's sort of a bandaid rather than a fix.

JnyJny added a commit that referenced this issue Nov 25, 2023
…kableMixin

As I understand it, when lights are activated in a thread other than
the main thread there is not a default async event loop available and
asyncio.get_event_loop raises a RuntimeError. I've modified the
property TaskableMixin.event_loop to catch RuntimeError and create an
event loop.
@JnyJny
Copy link
Owner

JnyJny commented Nov 25, 2023

@robbyfrt I know this issue has languished for a while without any movement. I think I have a fix that addresses this bug and it would be great if you could test it to confirm that it works.

The branch with the fix can be installed via pip like this:

$ python3 -m pip install git+https://github.com/JnyJny/busylight@fix/async

I'll work on a test to add to the test suite to confirm that this works.

@isaaca26
Copy link

@JnyJny Hello. I am using the LightManager with the Kuando Omega and applying effects using the apply_effect method. When i use the LightManager is seems to block the main thread. Is there any way around this to apply an effect without blocking the main thread but keeping the effect alive? Any info would be appreciated. Thank you.

@JnyJny
Copy link
Owner

JnyJny commented Feb 27, 2024

@isaaca26 In a perfect world, it shouldn't be blocking the main thread. To be honest this is the first non-toy async code I've written and it could probably use a re-write. It's kind of on the back burner right now but I'm happy to accept pull requests :)

@JnyJny
Copy link
Owner

JnyJny commented Feb 27, 2024

@isaaca26 if you could provide a test case or example of the code where you see the hang that would be very helpful to me.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants