-
Notifications
You must be signed in to change notification settings - Fork 34
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
Create a background R session and keep running code in it #56
Conversation
The first |
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.
Is there a way for the process to know who it's parent is?
#' has started, and the elapsed time since the current computation has | ||
#' started. The latter is NA if there is no active computation. | ||
#' | ||
#' `rs$get_state()` return the state of the R session. Possible values: |
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.
Do we need a way to get the status code if the process has finished?
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.
It inherits all methods from processx::process
, so you have rs$get_exit_status()
.
At the system level yes, but I don't think that we have an R API for it. Would that be useful? |
Codecov Report
@@ Coverage Diff @@
## master #56 +/- ##
=========================================
- Coverage 97.17% 90.98% -6.2%
=========================================
Files 15 16 +1
Lines 390 610 +220
=========================================
+ Hits 379 555 +176
- Misses 11 55 +44
Continue to review full report at Codecov.
|
One question here is what to do with standard output and standard error (by default). They probably should not be ignored, so we can write them to a file, or to a pipe. The pipe is easier to handle in the parent, but the parent needs to read it out, otherwise the child stops. And then it is easy to create a deadlock. |
Would it make sense to leave that up to the function passed to the process? e.g. it could be wrapped with |
while (1) { | ||
data <- processx::conn_write(con, data) | ||
if (!length(data)) break; | ||
Sys.sleep(.1) |
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 wonder if the sleep time here and on L274 should be a parameter of the object?
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.
Could be, but this sleep is actually not very important. It is a safety net that is almost never needed. Basically it is only needed if the write buffer is full, but the write buffer is at least 4k or 8k, and we don't send messages longer than that, and only ever keep at most one message in the buffer. So it is not really important to make this user configurable, I think.
|
||
rs_finish <- function(self, private, grace) { | ||
close(self$get_input_connection()) | ||
self$poll_io(grace) |
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.
Is 200 (ms?) enough time to ensure the everything closes gracefully?
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.
200ms, yes. Should be, unless the R session has .Last
which takes longer to run. For this odd case, the grace period is user configurable.
rs_finish <- function(self, private, grace) { | ||
close(self$get_input_connection()) | ||
self$poll_io(grace) | ||
self$kill() |
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.
What signal does this send to the child process? SIGKILL
, SIGTERM
, SIGINT
or SIGQUIT
?
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.
SIGKILL
On windows there is nothing else, and then it is better to behave the same way across platforms.
} | ||
|
||
rs__write_for_sure <- function(self, private, text) { | ||
while (1) { |
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.
Maybe this could be
while(length(self$write_input(text)) > 0) {
Sys.sleep(.1)
}
Although perhaps you feel that obscures what function is doing the work.
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.
You need to save the return value of write_input()
, though. That's how write_input()
works: it writes as much as it can, and returns the leftover that it could not write.
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.
Oh I see, so you would need to do something like
while(length(text <- self$write_input(text)) > 0) {
Sys.sleep(1)
}
Which isn't much of an improvement, so it is fine as is.
You need to select where the stdout and stderr go when you create the process. I would avoid using sinks, because that's just another weak link, it makes the child logic more difficult, and I don't think it buys you much. Ultimately you want to be able to get the stdout and stderr in the parent, and either a file or a pipe is a better solution for that. |
We already catch the errors and pass them to the parent, but we should probably catch the warnings as well. |
So, after some thinking, I think by default we could write stdout and stderr to temp files. Then after a The only glitch with this solution, is that the files will forever grow on the disk, until the session is done. But I don't think we can do much about that. In the (distant?) future, when we'll have a proper event loop running on a background thread, we can write stdout and stderr to pipes, and then the event loop thread will read them out regularly in the background, without bothering the main R thread. |
This does not really matter for the one-shot functions (except that their exit code is now different), but it does matter for r_session.
7851718
to
24cc242
Compare
About capturing conditions, I've been thinking about a
Maybe that could be the return value of process calls? |
@lionel- For this we need to change API, so we would need to introduce new functions. Which is fine, but I am wondering if the higher level utils belong to some other package, and if we should keep callr close to the system calls. E.g. callr could just return the result / error, the warnings, the stdout and strerr. |
OK, now we use separate temporary files for each |
There is one file for each call.
2554d31
to
b2f1c31
Compare
It has everything merged now.
When we read out the result.
When session is not in the right state for an operation.
In case it crashed, or was $kill()-ed.
I'll merge this now, can continue to improve.... |
This is basically ready, but it does need more testing, especially when sg fails, e.g. the remote R crashes.