-
-
Notifications
You must be signed in to change notification settings - Fork 47
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
Refactor IPC API implementation #116
Refactor IPC API implementation #116
Conversation
This was the last remaining thing on my todo list to handle properly. This is actually the kind of implementation I was looking for, but didn't get around to yet. I would prefer a subscription object instead, but yeah, this is overall looking quite good! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I love where this concept is going. This implementation looks good. Thanks for the contribution!
P.S. The build is failing because of the python bindings. You can ignore the build failure.
…nders are dropped when receiver task finishes, so that every receiver gets closed.
I was thinking about the possibility to easily revert the drivers state to the persistant state. There could be a method |
That's a good suggestion. What do you think about having the method fetch and convert the persistent state to |
In any case, there could be a method on the Then, there can be a method on the But |
That sounds perfect. I'll add that after this pr is merged |
I have implemented this now. One thing to note, there is no good way to cancel the subscription from the callback itself now. Doing this will involve creating some shared state where the subscription is moved into after subscribing, so something like Example: let shared_sub = Arc::new(Mutex::new(None::<EventsSubscription>));
let sub = client.add_event_receiver({
let shared_sub = shared_sub.clone();
move |event| {
println!("{:?}", event);
// ...
shared_sub.lock().unwrap().as_mut().unwrap().cancel();
}
});
*shared_sub.lock().unwrap() = Some(sub); Also: What should happen if the callback panics? Currently the task would just stop without notice. Maybe |
I don't feel it matters much here if the callback panics. It does print a message, and the task exiting on panic is expected (similar to what would happen if a thread panicked). Though a note could be added if there's anything we want the user to be clear about. Do you feel it would be useful to catch unwind, mark the panic, resume unwind, and notify on cancel? Perhaps it might be better to just catch it and re-raise it on the main thread instead. This way it becomes a lot clearer since everything crashes, instead of silently failing. I just did some testing and found that the |
This is wired, because all unit tests pass and they do check if the callback is run or not. I need a bit more context to make sense of the situation here. Maybe the runtime was blocked somehow in your tests? Did you call the API from within the runtime? You can try |
So far from what I've been able to gather, the test let sub = client.add_event_receiver({
let shared_sub = shared_sub.clone();
move |event| {
panic!();
}
}); |
Yes, this is correct. This makes sense, because when the callback panics, the receiver used to cancel the callback is dropped and closed. If you now call But when introducing a print statement, I can confirm that it is run. |
Give me a moment to track this original problem down. I'll reply back when I have more context. On a side note though, I do agree that it might be best to handle panics somehow to make it more transparent. You asked earlier:
We could just use |
Edit: Since the before steps were a bit roundabout, use this instead. This triggers it just the same. (The async version works fine) use driver_ipc::sync::DriverClient;
fn main() {
let driver = DriverClient::new().unwrap();
driver.add_event_receiver(|event| {
// this does not print
println!("{event:?}");
});
// go and send some events on the driver
std::thread::sleep(std::time::Duration::from_secs(10));
println!("shutting down");
} Edit 2: Okay I think we got it now. Not exactly surprising (obviously, give the behavior of receivers), but we need to add a comment explaining that the returned value needs to be saved and not dropped until done, otherwise the callback will stop firing. Not binding the value to something will also cause a drop before end of scope. Though obvious, it's an easy gotcha people may accidentally hit. Everything looking good! 👍🏻 |
This is not the behavior I expected nor wanted. I tracked that issue down and fixed it. If the subscription gets dropped, the callback will still run. This behavior is in line with the behavior of |
The last remaining unresolved comment is basically already taken care of, isn't it?
As far as this, I can take care of handling the error and a callback notification system of sorts / letting user know they got disconnected.
This is a non-issue unrelated to this PR (not sure if it matters to bother handling this, but even if I do, it's unrelated to this PR)
Iirc, this is already fixed after you changed the So I think the previous one can be marked as resolved and we can merge this if you feel it's ready. (If you still have some api input about the design of it, your thoughts are welcome, but I don't see this last one as a blocker) |
* Improve Errors in async Client * Improve Errors in async DriverClient * Improve Errors in sync Client variants
Lgtm! If you feel it's good to go, I'm fine merging it right now as-is. If you want to do any touch-ups, it can be done later in smaller PR's (this would make it much easier to keep track of things too). |
Merged now. Thank you for the time you put into this contribution! <3 |
This is my go on the IPC API.
The driver events are now expressed by a
Stream
. This also allowes for multiple listeners.Using the sync API, one can now add many receiver callbacks. The termination is now expressed using a boolean return value.
Maybe a subscription object is better?
(Changes are not fully tested)