Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# languageserver 0.3.18

- Retire persistent `callr` workers when cancelling tasks so a late interrupt
cannot leak into the next diagnostics run.
- Add preferred quick fixes for common `lintr` diagnostics and a conflict-aware
`source.fixAll` action, while making multi-line `nolint` actions apply to every
affected line.
Expand Down
8 changes: 5 additions & 3 deletions R/task.R
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,11 @@ Task <- R6::R6Class("Task",
private$cancelled <- TRUE
if (!is.null(private$session)) {
if (!identical(Sys.getenv("R_COVR"), "true")) {
# Do not close the session, it is persistent and managed by TaskManager.
# Just try to interrupt the ongoing computation.
private$session$interrupt()
# An interrupt can arrive after this call has completed and
# interrupt the next task on the persistent session. Retire
# the worker instead so cancellation cannot leak across tasks.
private$session$kill(
grace = 0, close_connections = FALSE)
}
} else if (!is.null(private$process) && private$process$is_alive()) {
if (identical(Sys.getenv("R_COVR"), "true")) {
Expand Down
26 changes: 26 additions & 0 deletions tests/testthat/test-task.R
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,32 @@ test_that("TaskManager refreshes pending task recency", {
tm$stop()
})

test_that("Task cancellation retires its persistent session", {
withr::local_envvar(R_COVR = "false")
state <- "idle"
killed <- FALSE
interrupted <- FALSE
session <- list(
call = function(...) state <<- "busy",
get_state = function() state,
read = function() NULL,
kill = function(grace, close_connections) {
expect_equal(grace, 0)
expect_false(close_connections)
killed <<- TRUE
state <<- "finished"
},
interrupt = function() interrupted <<- TRUE
)
task <- create_task(function() NULL, list())

task$start(session)
task$kill()

expect_true(killed)
expect_false(interrupted)
})

test_that("TaskManager does not overprovision while a session starts", {
tm <- TaskManager$new(
"starting", use_session = TRUE, min_idle_sessions = 0,
Expand Down
Loading