CGO: how to free memory allocated in C using malloc from go to avoid memory leak

10,045

Solution 1

Fix the memory allocation bug in your echo function. For example,

algo.go:

//algo.go
package main

//#cgo LDFLAGS:
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//char* echo(char* s);
import "C"
import (
    "fmt"
    "unsafe"
)

func main() {
    cs := C.CString("Hello from stdio\n")
    defer C.free(unsafe.Pointer(cs))
    var echoOut *C.char = C.echo(cs)
    defer C.free(unsafe.Pointer(echoOut))
    fmt.Println(C.GoString(echoOut))
}

algo.cpp:

//algo.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

using namespace std;

extern "C" {
    char* echo(char* o) {
        char* out = (char*)malloc(strlen(o)+1);
        strcpy(out, o);
        return out;
    }
}

Output:

$ cd algo
$ go build && ./algo
Hello from stdio

$ 

Solution 2

I'm using the following go version go version go1.8 linux/amd64 and I have no problems running your code after uncommenting your deferred C.free.

I added a loop to allow me to track memory leaks via htop. Without the deferred free it does leak, but uncommenting it fixes the problem.

The code is below.

//algo.go
package main

//#cgo LDFLAGS:
//#include <stdio.h>
//#include <stdlib.h>
//#include <string.h>
//char* echo(char* s);
import "C"
import "unsafe"

func main() {
    for i := 0; i < 1000000000; i++ {
        allocateAndDeallocate()
    }
}

func allocateAndDeallocate() {
    cs := C.CString("Hello from stdio\n")
    defer C.free(unsafe.Pointer(cs))
    var echoOut *C.char = C.echo(cs)
    defer C.free(unsafe.Pointer(echoOut)) // no crash here
}

//algo.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <iostream>
#include <math.h>

using namespace std;

extern "C" {

    char* echo(char* o) {
        int len = sizeof(o) / sizeof(char);
        char* out = (char*)malloc(len * sizeof(char));
        strcpy(out, o);
        return out;
    }

}    
Share:
10,045
auxdx
Author by

auxdx

Updated on June 04, 2022

Comments

  • auxdx
    auxdx almost 2 years

    I am trying to use CGO to call an optimized C++ CPU-bound implementation of a complex algorithms from golang. Basically, it will pass a string into c++ function and get a string back. A simplified version of the code can be seen in the below:

    //algo.go
    package main
    
    //#cgo LDFLAGS:
    //#include <stdio.h>
    //#include <stdlib.h>
    //#include <string.h>
    //char* echo(char* s);
    import "C"
    import "unsafe"
    
    func main() {
        cs := C.CString("Hello from stdio\n")
        defer C.free(unsafe.Pointer(cs))
        var echoOut *C.char = C.echo(cs)
        //defer C.free(unsafe.Pointer(echoOut)); -> using this will crash the code
        fmt.Println(C.GoString(echoOut));
    }
    
    
    //algo.cpp
    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #include <iostream>
    #include <math.h>
    
    using namespace std;
    
    extern "C" {
        char* echo(char* o) {
            int len = sizeof(o) / sizeof(char);
            char* out = (char*)malloc(len * sizeof(char));
            strcpy(out, o);
            return out;
        }
    }    
    

    In this link, ppl mentions that C++ code should call "free" by itself to free the allocated memory: http://grokbase.com/t/gg/golang-nuts/149hxezftf/go-nuts-cgo-is-it-safe-to-malloc-and-free-in-seperate-c-functions. But then it's very tricky because my c++ function return an allocated pointer so that golang can get the result. I cannot call free in the c++ code? What should be the correct way to handle this? I have a webserver will call the c++ code per each request and want to make sure it doesn't introduce any memory leak.

    Thanks.

  • auxdx
    auxdx about 8 years
    Thanks. It works. But I am still wondering if i should release the memory in go using defer C.free(unsafe.Pointer(echoOut)) or it has to be somewhere within c++ code ?
  • auxdx
    auxdx about 8 years
    Btw, I just realized that my code is leaking memory. Seems that i would have to free c++ malloc'ed memory or pass go pointer to c++ code instead. Any suggestions?