-
Notifications
You must be signed in to change notification settings - Fork 143
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
Fix direct fade not stopping running corutine #1645
base: dev
Are you sure you want to change the base?
Conversation
The direct fade class does not stop running corutines when a light update that is able to run in a single `set_brightness_and_fade` call is executed. This causes "race" conditions, ending up with unpredictable and wrong behavior. I've only tested it on my own machine and it works just like the mpf monitor suggests (which was not true previously).
Thanks for noticing and debugging this! Is there a way to unit/integration test this somehow? Might be tricky. I added one comment. Can you add that? Afterwards, we can safely merge this. |
Theoretically you can start a show with fading and then one without fading right afterwards (maybe even with higher priority).
@@ -62,12 +62,14 @@ class LightPlatformDirectFade(LightPlatformInterface, metaclass=abc.ABCMeta):
"""Implement a light which can set fade and brightness directly."""
- __slots__ = ["loop", "task"]
+ __slots__ = ["loop", "task", "double", "enable"]
def __init__(self, number, loop: AbstractEventLoop) -> None:
"""Initialise light."""
super().__init__(number)
self.loop = loop
+ self.double = False
+ self.enable = False
self.task = None # type: Optional[asyncio.Task]
@abc.abstractmethod
@@ -94,7 +96,14 @@ class LightPlatformDirectFade(LightPlatformInterface, metaclass=abc.ABCMeta):
self.task.cancel()
self.task = self.loop.create_task(self._fade(start_brightness, start_time, target_brightness, target_time))
self.task.add_done_callback(Util.raise_exceptions)
+ self.double = False
+ self.enable = True
else:
+ if self.task:
+ self.task.cancel()
+ if self.double and self.enable:
+ print("\n\n\nDOUBLE\n\n\n\n")
+ self.double = True
self.set_brightness_and_fade(target_brightness, max(fade_ms, 0))
async def _fade(self, start_brightness, start_time, target_brightness, target_time):
@@ -113,6 +122,7 @@ class LightPlatformDirectFade(LightPlatformInterface, metaclass=abc.ABCMeta):
brightness = target_brightness
self.set_brightness_and_fade(brightness, max(fade_ms, 0))
if target_fade_ms <= max_fade_ms:
+ self.enable = False
return
await asyncio.sleep(interval) Doing the check above fills my screen with a lot of "doubles", and it would be a bit strange for me to not have encountered this problem. However what you're saying is probably true, I can go ahead and add a set to None tomorrow. |
I just googled it looks like you can cancel a task multiple times but you should not: https://stackoverflow.com/questions/39688070/asyncio-prevent-task-from-being-cancelled-twice. I did not find anything about done tasks. I think I have seen exceptions with non-coroutine futures. Lets just set the task to None and be done with it ;-). |
Kudos, SonarCloud Quality Gate passed! 0 Bugs No Coverage information |
I've added the set to None. Checking for done should be on the task creation side as well, so i would say that is an unrelated bug and should have it's own issue/pull request. (Maybe this isn't the only place that should have this check?) Here is a git diff if you want to add it yourself, but I can push my changes to this branch or create a new pull request if you want that. |
I did some more testing with the diff I mentioned in my previous message adding a check for |
The direct fade class does not stop running corutines when a light update that is able to run in a single
set_brightness_and_fade
call is executed.This causes "race" conditions, ending up with unpredictable and wrong behavior.
I've only tested it on my own machine and it works just like the mpf monitor suggests (which was not true previously).