[R] How to properly finalize external pointers?
Duncan Murdoch
murdoch at stats.uwo.ca
Fri Aug 3 16:04:49 CEST 2007
On 8/3/2007 9:19 AM, Jens Oehlschlägel wrote:
> Dear R .Call() insiders,
>
> Can someone enlighten me how to properly finalize external pointers in C code (R-2.5.1 win)? What is the relation between R_ClearExternalPtr and the finalizer set in R_RegisterCFinalizer?
>
> I succeeded registering a finalizer that works when an R object containing an external pointer is garbage collected. However, I have some difficulties figuring out how to do that in an explicit closing function.
>
> I observed that
> - calling R_ClearExternalPtr does not trigger the finalizer and is dangerous because it removes the pointer before the finalizer needs it at garbage-collection-time (no finalization = memory leak)
> - calling the finalizer directly ensures finalization but now the finalizer is called twice (once again at garbage collection time, and I did not find documentation how to unregister the finalizer)
> - It works to delete the SEXP external pointer object but only if not calling R_ClearExternalPtr (but why then do we need it?) Furthermore it is unfortunate to delay freeing the external pointers memory if I know during runtime that it can be done immediately.
>
> Shouldn't R_ClearExternalPtr call the finalizer and then unregister it? (this would also work when removing the SEXP external pointer object is difficult because it was handed over to the closing function directly as a parameter)
I think we want R_ClearExternalPtr to work even if the finalizer would
fail (e.g. to clean up when there was an error when trying to build the
external object).
So I'd suggest that when you want to get rid of an external object
immediately, you call the finalizer explicitly, then call
R_ClearExternalPtr. The documentation doesn't address the question of
whether this will clear the registered finalizer so I don't know if
you'll get a second call to the finalizer during garbage collection, but
even if you do, isn't it easy enough to do nothing when you see the null
ptr, as you do below?
By the way, questions about programming at this level are better asked
in the R-devel group.
Duncan Murdoch
> Best regards
>
>
> Jens Oehlschlägel
>
>
> // C-code
>
> static void rindex_finalize(SEXP extPtr){
> pINT ptr = R_ExternalPtrAddr(extPtr);
> if(ptr){
> Free(ptr);
> Rprintf("finalized\n");
> }else{
> Rprintf("nothing to finalize\n");
> }
> return;
> }
>
>
> SEXP rindex_open(
> SEXP Sn
> ){
> int i,n = INTEGER(Sn)[0];
> pINT ptr = Calloc(sizeof(INT)*n, INT);
> SEXP extPtr, ret;
> for (i=0;i<n;i++){
> ptr[i] = i;
> }
> extPtr = R_MakeExternalPtr(ptr, install("Rindex_extPtr"), R_NilValue);
> R_RegisterCFinalizer(extPtr, rindex_finalize);
>
> PROTECT(ret = allocVector(VECSXP, 1));
> SET_VECTOR_ELT(ret,0,extPtr);
> UNPROTECT(1);
> return ret;
> }
>
> SEXP rindex_close(
> SEXP obj
> ){
> int i, n= 10;
> SEXP ret, extPtr=VECTOR_ELT(obj, 0);
> pINT p, ptr = R_ExternalPtrAddr(extPtr);
>
> PROTECT(ret = allocVector(INTSXP, n));
> p = INTEGER(ret);
> for (i=0;i<n;i++){
> Rprintf("ptri=%d\n",ptr[i]);
> p[i] = ptr[i];
> }
>
> /* this does finalize immediately but at next garbage collection again
> rindex_finalize(extPtr);
> */
>
> /* this must not called otherwise the pointer is gone at garbage collection time
> R_ClearExternalPtr(extPtr);
> */
>
> /* this triggers the finalizer but only at next garbage collection */
> SET_VECTOR_ELT(obj,0,R_NilValue);
>
> UNPROTECT(1);
> return ret;
> }
>
>
> # R-Code
> initRindex <- function(){
> dyn.load(file.path(.libPaths(), "rindex", "libs", paste("rindex", .Platform$dynlib.ext, sep = "")))
> }
>
> doneRindex <- function(){
> dyn.unload(file.path(.libPaths(), "rindex", "libs", paste("rindex", .Platform$dynlib.ext, sep = "")))
> }
>
>
> openRindex <- function(n=10){
> .Call("rindex_open", as.integer(n))
> }
>
> closeRindex <- function(extPtr){
> .Call("rindex_close", extPtr)
> }
>
> if (FALSE){
> # now try it
> require(rindex)
>
> initRindex()
>
> extPtr <- openRindex()
> extPtr
> extPtr2 <- closeRindex(extPtr)
> extPtr2
> gc()
>
> extPtr <- openRindex()
> extPtr2 <- extPtr
> extPtr2
> rm(extPtr)
> gc()
> extPtr2
> rm(extPtr2)
> gc()
> extPtr2
>
> doneRindex()
> }
>
>
>> version
> _
> platform i386-pc-mingw32
> arch i386
> os mingw32
> system i386, mingw32
> status
> major 2
> minor 5.1
> year 2007
> month 06
> day 27
> svn rev 42083
> language R
> version.string R version 2.5.1 (2007-06-27)
>
>
>
More information about the R-help
mailing list