[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