Skip to content
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

Fix reactivePoll leak #2522

Merged
merged 6 commits into from Sep 5, 2019
Merged

Fix reactivePoll leak #2522

merged 6 commits into from Sep 5, 2019

Conversation

wch
Copy link
Collaborator

@wch wch commented Jul 5, 2019

This fixes #1548. It allows reactivePoll objects to clean up when there are no longer any references to them.

Note that there must no longer be any references to the reactivePoll object for it to be cleaned up. If there's still a reference to the object, even if that reference isn't consumed by any other reactives/observers, that's not sufficient for it to be cleaned up; in other words, it will keep firing.

Example app:

library(shiny)
shinyApp(
  fluidPage(
    p("Value of ", code("count")),
    verbatimTextOutput("count"),
    p("Value of ", code("i()")),
    verbatimTextOutput("i"),
    actionButton("replace", "Replace count()")
  ),
  function(input, output, session) {
    i <- reactiveVal(0)
    count <- reactivePoll(1000, session,
      checkFunc = function() {
        isolate({
          i(i() + 1)
          i()
        })
      },
      valueFunc = function() {
        isolate(i())
      }
    )

    output$count <- renderText({
      count()
    })
    output$i <- renderText({
      i()
    })

    observeEvent(input$replace, {
      count <<- reactive("count() has been replaced")
      gc()
    })
  }
)

Here's how it behaves before this PR:

2019-07-05 16 07 08

And after this PR:

2019-07-05 16 05 54

Note that before the change, the replacement reactive, which has the value "count() has been replaced", shows up in output$count, but after the change, it doesn't. This is because, before the change, the invalidation keeps happening on the timer and causes the renderText to re-execute; but after the change, the invalidation stops happening after count() is replaced, so it does not cause the renderText to re-execute.

Here's a test that works outside of a Shiny app:

library(shiny)
i <- 0
count <- reactivePoll(100, NULL,
  checkFunc = function() {
    i <<- i + 1
    i
  },
  valueFunc = function() i
)

observe({
  message(count())
})
while(i < 3) {
  Sys.sleep(0.1)
  shiny:::timerCallbacks$executeElapsed()
  shiny:::flushReact()
}

rm(count)
gc()

# If the reactivePoll was cleaned up, then running the following should no
# longer increment i.
Sys.sleep(0.2)
shiny:::timerCallbacks$executeElapsed()
shiny:::flushReact()
Sys.sleep(0.2)
shiny:::timerCallbacks$executeElapsed()
shiny:::flushReact()

# This is the crucial part
stopifnot(i == 3)

Update: With the final version of this PR, in test app, i() will go one number higher than count -- what's important is that i() stops at some point. Also, in the test code for outside of a Shiny app, it should check for stopifnot(i == 3) (this is in the unit tests in the PR).

@wch wch requested a review from jcheng5 July 5, 2019 21:15
R/reactives.R Outdated Show resolved Hide resolved
R/reactives.R Outdated Show resolved Hide resolved
@wch wch requested a review from jcheng5 August 20, 2019 18:22
tests/testthat/test-reactivity.r Outdated Show resolved Hide resolved
@jcheng5
Copy link
Member

jcheng5 commented Aug 26, 2019

After in-person discussion: Let's change the finalizer to merely set a boolean variable, that the observer will look at and destroy itself when it is set to TRUE.

@wch wch force-pushed the wch-fix-reactivepoll-leak branch from 2bd5fa9 to 6821972 Compare August 27, 2019 01:58
R/reactives.R Show resolved Hide resolved
Copy link
Member

@jcheng5 jcheng5 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The missing return()

@wch wch requested a review from jcheng5 August 28, 2019 15:00
tests/testthat/test-reactivity.r Outdated Show resolved Hide resolved
@jcheng5 jcheng5 changed the title [wip] Fix reactivePoll leak Fix reactivePoll leak Sep 5, 2019
@jcheng5 jcheng5 self-requested a review September 5, 2019 23:44
@jcheng5 jcheng5 merged commit c9d8b98 into master Sep 5, 2019
@jcheng5 jcheng5 deleted the wch-fix-reactivepoll-leak branch September 5, 2019 23:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

reactivePoll leaks the polling observer
2 participants