@PorkChop's comment is pointing in the right direction. However, I'd recommend using processx::process rather than run as it provides us with methods to control the started process from within R. See ?process. (run by the way is also based on the process class.)
The main problem here is, that running the process synchronously (wait=TRUE) blocks the R session. Accordingly onStop won't fire until R is back in control.
Therefore you can't trigger anything once the browser window was closed because the shiny-session continues to run until the external program is finished and R can close the shiny-session.
On session end, the below code checks if the asynchronously started process is still alive and kills it if necessary (tested on windows only).
library(shiny)
library(processx)
ui <- fluidPage(
actionButton("runBtn", label="Run a program that consumes many resources"),
actionButton("stopSession", "Stop session")
)
server <- function(input, output, session) {
myProcess <- NULL
observeEvent(input$stopSession, {
cat(sprintf("Closing session %s\n", session$token))
session$close()
})
observeEvent(input$runBtn,
{
if(Sys.info()[["sysname"]]=="Windows"){
writeLines(text = c("ping 127.0.0.1 -n 60 > nul"), con = "sleep.bat")
myProcess <<- process$new("cmd.exe", c("/c", "call", "sleep.bat"), supervise = TRUE, stdout = "")
} else {
myProcess <<- process$new("sleep", "60", supervise = TRUE, stdout = "")
}
# myProcess$wait() # wait for the process to finish
})
onStop(function(){
cat(sprintf("Session %s was closed\n", session$token))
if(!is.null(myProcess)){
if(myProcess$is_alive()){
myProcess$kill()
}
}
})
}
shinyApp(ui, server)
Regarding the different session callback functions see this related post.
As requested here the process is wrapped in a reactiveVal:
library(shiny)
library(processx)
ui <- fluidPage(
actionButton("runBtn", label="Run a program that consumes many resources"),
actionButton("stopSession", "Stop session")
)
server <- function(input, output, session) {
myProcess <- reactiveVal(NULL)
observeEvent(input$stopSession, {
cat(sprintf("Closing session %s\n", session$token))
session$close()
})
observeEvent(input$runBtn,
{
if(Sys.info()[["sysname"]]=="Windows"){
writeLines(text = c("ping 127.0.0.1 -n 60 > nul"), con = "sleep.bat")
myProcess(process$new("cmd.exe", c("/c", "call", "sleep.bat"), supervise = TRUE, stdout = ""))
} else {
myProcess(process$new("sleep", "60", supervise = TRUE, stdout = ""))
}
# myProcess()$wait() # wait for the process to finish
})
onStop(function(){
cat(sprintf("Session %s was closed\n", session$token))
if(!is.null(isolate(myProcess()))){
if(isolate(myProcess()$is_alive())){
isolate(myProcess()$kill())
}
}
})
}
shinyApp(ui, server)