-
-
Notifications
You must be signed in to change notification settings - Fork 5.3k
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
Make it safe for klippy:ready handlers to execute UPDATE_DELAYED_GCODE #6631
Conversation
This actually happened to me with moonraker-timelapse, whose Moonraker plugin executes GCode once Klippy is ready. |
5afde75
to
c8ec9d2
Compare
This looks plausible, but it makes this branch do unnecessary work to calculate a value that is discarded. You can instead make the entire branch conditional:
|
When another klippy:ready handler executes UPDATE_DELAYED_GCODE before DelayedGcode's ready handler runs, then self.timer_handler is None, causing update_timer() to fail. Signed-off-by: Rolf Schäuble <[email protected]>
@flowerysong: you are right; making the whole branch conditional is better. I've changed the code accordingly. |
Thank you for your contribution to Klipper. Unfortunately, a reviewer has not assigned themselves to this GitHub Pull Request. All Pull Requests are reviewed before merging, and a reviewer will need to volunteer. Further information is available at: https://www.klipper3d.org/CONTRIBUTING.html There are some steps that you can take now:
Unfortunately, if a reviewer does not assign themselves to this GitHub Pull Request then it will be automatically closed. If this happens, then it is a good idea to move further discussion to the Klipper Discourse server. Reviewers can reach out on that forum to let you know if they are interested and when they are available. Best regards, PS: I'm just an automated script, not a human being. |
Alright, so here's a self-review based on the steps from https://www.klipper3d.org/CONTRIBUTING.html#what-to-expect-in-a-review:
If there's more that I can do to speed this up, please let me know. |
Thanks. Could you describe the problem being solved? In what situations would a 'klippy:ready' event run a g-code command? (If there are any cases - those cases should be fixed as it is not valid to run g-code from the "klippy:ready" callback.) -Kevin |
@KevinOConnor: I had this problem when using moonraker-timelapse. It's Moonraker plugin executes GCode once Klippy is ready. The executed GCode uses I don't know why, but it seems there is some amount of randomness involved. It seems to work for most people (and initially it worked for me), but for some reason sometimes the Moonraker plugin's ready-handler comes first, causing the issue. Is there another event moonraker-timelapse's plugin could listen for? One that is emitted after all |
The "klippy:ready" callback is internal to the Klipper code (that is, only relevant to code in this github repo). If you've found some way to create an error in the code, then I'd start by providing a Klipper log of the event. Make sure you are running the pristine klipper code from this repo, reproduce the event, issue an M112 command, and then attach the full unmodified log here - or on discourse as described at https://www.klipper3d.org/Contact.html#i-found-a-bug-in-the-klipper-software . Cheers, |
@KevinOConnor: your comments and questions made me look deeper into how Moonraker and Klipper interact. klippy.py contains the following code: self._set_state(message_ready)
for cb in self.event_handlers.get("klippy:ready", []):
if self.state_message is not message_ready:
return
cb() The first line sets the Now with a bit of bad luck, apparently Moonraker catches the Wouldn't it make sense to first run the |
My vague recollection is that some of the ready callbacks may inspect the current state and thus we need to transition prior to invoking the callbacks. That said, I don't think it should have been valid for a command to run between the set state and the completion of that loop. The main loop is single threaded and none of the ready callbacks should have "yielded" to allow a command handler to run. So, something seems wrong. It'll take some work to track down which internal "klippy:ready" callback is misbehaving. -Kevin |
I have to admit that I don't really understand how Klipper's reactor works, and what causes a "yield' and what doesn't. However, I have a suspicion: I'm using the klipper_tmc_autotune plugin. This plugin has a If I comment out this line, then the error goes away: https://github.com/andrewmcgr/klipper_tmc_autotune/blob/489a1789d5ddb5510dcdd953aaa7780892d63f0e/autotune_tmc.py#L241 Would using tmc_uart to send queries result in a "yield"? |
Klipper does not have a plugin system. Any changes to the repo (including adding of files) is a modification of the code. From what you describe, it does sound like those modifications are defective as it is not valid to issue commands like that from the "klippy:ready" handler. -Kevin |
I’ll try sending a PR to autotune_tmc that fixes this. It’s a nasty issue, quite hard to diagnose, and could trigger all kinds of issues in the future. Please allow me a few questions to make sure I got everything right:
def handle_ready(self): # the klippy:ready handler
reactor.register_timer(self._callback, reactor.NOW)
def _callback(self):
# do stuff that may switch to another greenlet This will allow the loop that calls all event handlers to finish before the timer callback is called.
|
Thanks. Some answers to your questions:
More generally, the "klippy:connect" event is a general "do what needs to be done to configure and establish communications with remote micro-controllers" and thus has a lot of flexibility. The "klippy:ready" callback is an instantaneous notification of a state transition that can't do anything complex nor raise errors, because that could leave the system in a "weird half way ready/not-ready" state. Cheers, |
Thank you for the answers, Kevin. Here's the PR for the documentation: #6702 And here's the PR for autotune_tmc, for those interested in that: andrewmcgr/klipper_tmc_autotune#203. |
When another klippy:ready handler executes UPDATE_DELAYED_GCODE before DelayedGcode's ready handler runs, then
self.timer_handler
is None, causing theupdate_timer
to fail.