callr icon indicating copy to clipboard operation
callr copied to clipboard

Running new background process from a parent process that dies before background process terminate

Open diegoperoni opened this issue 8 months ago • 9 comments

Dear Gábor,

I'm using your beautiful callr package and I'm having trouble because I can't figure out how to execute a background process invoked by a parent process that must terminate its execution immediately after invoking the next one.

In other words, if the parent process ‘dies’ before the child process has finished, the child process is also terminated prematurely.

The only way I have found to make this work is to put a Sys.sleep() into the parent after the callr::r_bg or callr::r_session$call to maintain alive it the time needed by the child process.

(I'm testing it on CentOS7 and R 4.1.2)

Thanks in advance for your help!

Diego

I report 2 functions I wrote invoked by the parent process

# First attempt
runAsync = function(fun_name=NULL, fun_args=list(), src_file=NULL, stdout='|', stderr='|', cmdargs=c('--slave', '--no-save', '--no-restore')) {

 wrapper_func = function(src_file, fun_name, fun_args) {
   source(src_file)
   do.call(get(fun_name), fun_args)
 }

 callr::r_bg(
   func = wrapper_func,
   args = list(
     src_file = src_file,
     fun_name = fun_name,
     fun_args = fun_args
   ),
   supervise = FALSE,
   stdout    = stdout,
   stderr    = stderr,
   cmdargs   = cmdargs
 )

}

runAsync(
 fun_name='my_bg_func',
 fun_args=list(par='value1'),
 src_file='~/functions_source.R')

#Sys.sleep(35) # only with this does it work

-------------------------------------------------------

# Second attempt
runProcess = function(fun_name=NULL, fun_args=list(), src_file=NULL) {

 options = callr::r_session_options()
 options$supervise = FALSE
 rs = callr::r_session$new(wait=TRUE, options=options)
 rs$call(function(fun_name, fun_args, src_file) {
   source(src_file)
   do.call(get(fun_name), fun_args)
 }, list(fun_name, fun_args, src_file))

}


runProcess(
 fun_name='my_bg_func',
 fun_args=list(par='value1'),
 src_file='~/functions_source.R')

#Sys.sleep(35) # only with this does it work

diegoperoni avatar Mar 18 '25 11:03 diegoperoni

Can you try to create a reproducible example that I can actually run?

callr::r_bg() returns a handle that you can use to manipulate the process and get its output. If you don't have a reference to this handle, then callr (processx) will kill the subprocess. If you want to keep the subprocess alive while the main process is running, then assign the handle to a variable:

proc <- runAsync(...)

If you want to keep the subprocess alive even after the main process has quit, then use cleanup = FALSE, e.g.

callr::r_bg(function()Sys.sleep(100), cleanup = FALSE)

gaborcsardi avatar Mar 18 '25 11:03 gaborcsardi

Hello Gábor,

thanks for your rapid response and support!

I've prepared a minimal working example that you can run on your machine.

  1. A few line of code to schedule a CRON task that runs every 1 minute.
  2. An attached R script (rename it with .R extension) that invokes CRON (parent process) which triggers the background process (child process)

cron.test.txt

library(cronR)
setwd('/home/peroni/test_folder')
cmd = cronR::cron_rscript(rscript='./cron.test.R', rscript_log='./cron.test.log', log_timestamp=TRUE, log_append=TRUE, workdir=getwd())
cronR::cron_add(command=cmd, frequency='* * * * 1-5', id='cron.test', ask=FALSE)

diegoperoni avatar Mar 18 '25 14:03 diegoperoni

Dear Gábor, have you had a chance to test my minimal working example? Unfortunately, the solution: callr::r_bg(function()Sys.sleep(100), cleanup = FALSE) I can't get it to work because the parent process always gets terminated prematurely. Thanks

diegoperoni avatar Apr 18 '25 11:04 diegoperoni

Sorry, I meant an example that I can run without any setup and changes. Ideally it would use the callr package only, not extra packages that I am not familiar with. Thanks!

gaborcsardi avatar Apr 28 '25 08:04 gaborcsardi

Sure, just run cron.test.R from command line.

Thanks

diegoperoni avatar Apr 28 '25 10:04 diegoperoni

~/works/callr upkeep-2025-04
❯ Rscript ~/Downloads/cron.test.txt
12:32:56 CRON STARTED

~/works/callr upkeep-2025-04*
❯

What am I supposed to see here?

gaborcsardi avatar Apr 28 '25 10:04 gaborcsardi

My problem is that the subprocess started within callr is killed prematurely when the main process ends. I need the subprocess remain alive after the main process ends. It appends as desided just when I run the main process from RStudio and it doesn’t append when I run main process from command line indipendent process (as a scheduled cron process). It looks to me that parameter cleanup doesn’t work as desidered. Is it the same for you? Thanks

diegoperoni avatar Apr 28 '25 14:04 diegoperoni

I am sorry for being repetitive here, but what is supposed to happen when I run your code? What happens instead?

gaborcsardi avatar Apr 29 '25 07:04 gaborcsardi

I can not see printed “Nested Function END” after 10 seconds sleep because the subprocess is killed by main process before it append. Instead when I decomment Sys.sleep(20) in main process I can see “Nested Function END “

diegoperoni avatar Apr 29 '25 10:04 diegoperoni