[R-pkg-devel] reg.finalizer and .onUnload

William Dunlap wdunlap at tibco.com
Wed Mar 21 21:39:49 CET 2018


I have a package that lets a user log into another system
and I want it to be sure to log the user out, at least by the
time the R session is over.

My Login(username,password) function stashes a token
(the username in my simplfied code) in an environment
and my Logout() function needs to use that stored token
to logout.  Logout() sets the stored token to NULL when it is
done and the stored token is NULL when the package is loaded.

I first tried calling the logout function from a finalizer registered
in .onLoad.  This basicly worked:
  > testFinalize::Login("Bill")
  [Loading testFinalize from /homes/bill/packages/tmp/lib]
  *** Logging in user Bill ***
  > q("no")
  [invoking registered finalizer]
  *** Logging out user Bill ***

However, it failed when the user manually unloaded the
package's namespace.  In that case, running
the registered finalizer, either because the session ended or
because of a garbage collection, caused the namespace to be
reloaded, so the stored token is NULL, making it look like
no logout is required:
  > testFinalize::Login("Bill")
  [Loading testFinalize from /homes/bill/packages/tmp/lib]
  *** Logging in user Bill ***
  > unloadNamespace("testFinalize")
  [Unloading /homes/bill/packages/tmp/lib/testFinalize]
  --- Not calling Logout at unload time ---
  > gc() # or q()
  [invoking registered finalizer]
  [Loading testFinalize from /homes/bill/packages/tmp/lib]
  *** No need to log out ***
           used (Mb) gc trigger (Mb) max used (Mb)
  Ncells 255820 13.7     460000 24.6   350000 18.7
  Vcells 535039  4.1    1023718  7.9   786426  6.0

If I only call Logout() from .onUnload, and not from the registered
finalizer, nothing happens when the R session is done, since packages are
not unloaded then.

It looks like I need to call Logout from both .onUnload and from
the registered finalizer to make sure it gets called.  Is that
right or is there a standard way to make sure some package-specific
cleanup gets done?

Since the finalizer will still cause the package's namespace to be
loaded we waste some time after q().  Is there a way to unregister
a finalizer so we can avoid the reloading?

I've appended the R code in the package below.  The package exports
Login, Logout, and LogoutAtUnload.  The last lets you force .unLoad to
call Logout() or not.

Reply privately if you want the tar.gz file for the test package.

Bill Dunlap
TIBCO Software
wdunlap tibco.com

.loginInfo <- list2env(
    list(token = NULL,
         logoutAtUnload = FALSE)
)
Login <- function(username, password) {
    if (is.null(.loginInfo$token)) {
        message("*** Logging in user ", username, " ***")
        .loginInfo$token <- username
    } else {
        message("*** Cannot log in user ", username, ", ",
.loginInfo$token, " is already logged in ***")
    }
}
Logout <- function() {
    if (!is.null(.loginInfo$token)) {
        message("*** Logging out user ", .loginInfo$token, " ***")
        .loginInfo$token <- NULL
    } else {
        message("*** No need to log out ***")
    }
}
LogoutAtUnload <- function(flag = NA) {
    stopifnot(is.logical(flag), length(flag)==1)
    old <- isTRUE(.loginInfo$LogoutAtUnload)
    if (!is.na(flag)) {
        .loginInfo$LogoutAtUnload <- flag
    }
    old
}
.onLoad <- function(libname, pkgname) {
    message("[Loading ", pkgname, " from ", libname, "]")
    reg.finalizer(getNamespace(pkgname), function(envir) {
message("[invoking registered finalizer]"); Logout()}, onexit=TRUE)
}
.onUnload <- function(libpath) {
    message("[Unloading ", libpath, "]")
    if (LogoutAtUnload()) {
        Logout()
    } else {
        message("--- Not calling Logout at unload time ---")
    }
}

	[[alternative HTML version deleted]]



More information about the R-package-devel mailing list