Finalizer in R modifying objects - thread safe?

Viewed 39

In an R package, I manage some external objects. I want to release them after the corresponding R object (an environment) has been garbage collected. There is a reason why I cannot release the external resources right away. Therefore I need to somehow save the information that the object has been garbage collected and use this information at a later time. The approch is implemented basically like the following:

Environment objects are created with some ID. I have some environment managingEnv, where I collect the information about the finalized objects in a vector of IDs of objects. When the objects are finalized, the finalizer writes their IDs into the managingEnv.

managingEnv <- new.env(parent = emptyenv())
managingEnv$garbageCollectedIds <- c()

createExternalObject <- function(id) {
  ret <- new.env(parent = emptyenv())
  ret$id <- id
  reg.finalizer(ret, function(e) {
     managingEnv$garbageCollectedIds <- c(managingEnv$garbageCollectedIds, e$id)
  })
  ret
}

If I then create some objects and run the garbage collection, the approach seems to work and I have at the end all IDs collected in the managingEnv and could later perform my action to release all these objects.

> createExternalObject(1)
<environment: 0x000002307ff36920>
> createExternalObject(2)
<environment: 0x000002307e94d390>
> createExternalObject(3)
<environment: 0x000002307e94ac90>
> gc()
          used (Mb) gc trigger (Mb) max used (Mb)
Ncells  568005 30.4    1299461 69.4  1299461 69.4
Vcells 1519057 11.6    8388608 64.0  2044145 15.6
> managingEnv$garbageCollectedIds
[1] 2 1 3

Although the approach seems to work, I have encountered some instability in my package and R crashes sometimes randomly. After some research into the problem, I came to the conclusion that the approach that I use might be not safe.

The documentation of reg.finalizer says:

Note: R's interpreter is not re-entrant and the finalizer could be run in the middle of a computation. So there are many functions which it is potentially unsafe to call from ‘f’: one example which caused trouble is ‘options’. Finalizers are scheduled at garbage collection but only run at a relatively safe time thereafter.

Is my approach really safe? Can I change this code to make if safe(r) or find another solution to the problem described above?

EDIT: I found the reasons for the instability in other parts of the code. So it seems like the approach described above is safe. I would still be interested in more details about how to know which operations are "potentially unsafe" in finalizers, to be able to reason that the approach described here is safe.

0 Answers
Related