Friday, 23 November 2012

Using the gcc cleanup attribute

Section 6.36 of the GCC manual describes the rather interesting cleanup variable attribute.   This allows one to specify a function to call when the variable goes out of scope.

The caveat is that the cleanup attribute can only be used with auto function scope variables, so it cannot be used on function parameters or static variables.

So what about an simple example?  How about automatic free'ing on variables that go out of scope?  This way we can be lazy and do automatic garbage collecting without having to remember to free() each allocation. Now, I'm not recommending this is good practice, I am just using this as an example.

Below I define a macro autofree that we use on the auto variables that we want to garbage collect on.   When the variable goes out of scope __autofree() is called and it is passing the address of the variable.  GCC insists on the helper function to be passed as a pointer to the type of the variable being cleaned. To handle any particular type I used a void * argument __autofree() and then I cast this to a void ** to allow me to free the memory that the variable pointed to, so a little bit of legitimate slight of hand being used here.

 #include <stdlib.h>  
 #include <stdio.h>  
   
 #define autofree __attribute((cleanup(__autofree)))  
   
 void __autofree(void *p)  
 {  
     void **_p = (void**)p;  
   
     printf("free -> %p\n", *_p);  
     free(*_p);  
 }  
   
 void *myalloc(size_t sz)  
 {  
     void *ptr;  
   
     if ((ptr = malloc(sz)) == NULL) {  
         fprintf(stderr, "malloc failed.\n");  
         exit(1);  
     }  
     printf("malloc -> %p\n", ptr);  
   
     return ptr;  
 }  
   
 int main(int argc, char **argv)  
 {  
     autofree char *x = myalloc(32);  
   
     {  
         autofree int *y = myalloc(64);  
   
         printf("y = %p\n", y);  
     }  
     printf("x = %p\n", x);  
   
     return 0;
 }  

In this example, I malloc memory for x and then y.  Then y and then x go out of scope and the cleaner function __autofree() frees the memory in that order:

 malloc -> 0x1504010  
 malloc -> 0x1504040  
 y = 0x1504040  
 free -> 0x1504040  
 x = 0x1504010  
 free -> 0x1504010  

I'm sure there are other ways that this can be creatively used (or abused...); as it stands it is a GCC extension so it is not portable C in any shape or form.

6 comments:

  1. Upstart's source makes heavy use of this

    ReplyDelete
    Replies
    1. Oh, yes I see, clearly a case of "Not Invented Here" so to speak...

      Delete
  2. This comment has been removed by the author.

    ReplyDelete
  3. avr-libc uses it for its ATOMIC_BLOCK macros.

    ReplyDelete
  4. for smaller chunks of memory that just needs to be valid during the execution of a function, alloca() is a good alternative to that malloc + hook combination:

    void f() {
    FILE *f = fopen("something.txt", "rt");
    assert(f != NULL);
    const int bufsize = 1000;
    char* buf = alloca(bufsize);
    setvbuf(f, buf, _IOFBF, bufsize);
    // ...
    fclose(f);
    }

    ReplyDelete
  5. systemd makes extensive use of this, although only recently and there is unconverted code

    ReplyDelete