-
Notifications
You must be signed in to change notification settings - Fork 29
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
Implement autorunning of child loops #119
Conversation
return registries[id]; | ||
} | ||
|
||
bool remove(int id) { |
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.
Add ASSERT_MAIN_THREAD()
|
I've implemented the changes we discussed. |
I don't think the finalizer semantics are correct anymore. The finalizer causes the loop to be immediately destroyed. But I think if work is already scheduled on the loop, and its parent still exists, it shouldn't be destroyed. callback <- function() {
message("hello")
}
local({
loop <- later::create_loop()
later::later(callback, delay = 2, loop = loop)
})
gc(verbose = FALSE) I think this example should print "hello" after 2 seconds. |
I've been thinking about this, and I'm not sure that I agree with the proposed semantics. One way of looking at it is that, if you manually create a loop and do weird stuff with it, you're responsible for keeping the reference to the loop until you're done with it. Anyone who is doing stuff like this should be able to grasp the issue, though we will have to document it. For example, here's the problematic case: callback <- function() {
message("hello")
}
f <- function() {
loop <- create_loop()
later(callback, 1, loop = loop)
}
f()
gc()
# Wait for 1 second - nothing happens In order to fix it, the user can wrap the callback with a function that captures the loop: g <- function() {
loop <- create_loop()
later(function() callback(), 1, loop = loop)
}
g()
gc()
# Wait for 1 second - prints "hello" Also, in cases like WebSocket and Chromote, if someone If you still think that we should keep a loop alive when there are callbacks, here's what I think the logic should look like.
|
I agree with those bullet points. The "it might or might not still execute depending on when a gc happens to occur" seems like it will be a source of bugs that is both surprising and easy to miss during testing. |
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.
Looks good other than comments
This PR makes it so that child loops are run automatically when a parent loop is run.
When running an event loop, this checks if the event loop has any children, and runs them as well. It also modifies
nextTimestamp()
anddue()
methods so that, when they are called on a registry (AKA event loop), they recurse into children and find the nearest time stamp, or whether any callbacks are due, respectively.All of this happens in purely C++ code; if we had to call back into R to check for parent-child relationships each time we did this stuff, it would be expensive.
Update: This previously was implemented with external pointers, but it turns out to be much simpler to pass around loop IDs, like we did previously.