1

I have question is it possible to return from Go function pointer on C? For example main.c can be:

struct open_db_return db_ptr = open_db(db_path);
  GoSlice backet = {"DB", 2, 2};
  GoSlice key = {"CONFIG", 6, 6};
  struct get_value_return val = get_value(db_ptr.r0, backet, key);
  close_db(db_ptr.r0);

Go code is next:

//export open_db
func open_db(path string) (interface{}, error) {
    db, err := db.Open(path, 0600, nil)
    if err != nil {
        return nil, err
    }
    return db, nil
}

//export close_db
func close_db(db interface{}) {
    ldb := db.(*db.DB)
    ldb.Close()
}

//export get_value
func get_value(db interface{}, backet_name []byte, key_name []byte) ([]byte, error) {
    ldb := db.(*db.DB)
    var value []byte
    fn := func(tx *db.Tx) error {
        value = tx.Bucket(backet_name).Get(key_name)
        return nil
    }
    return value, ldb.View(fn)
}

After cgo command go build -buildmode=c-archive test.go i receive header file and trying to link it into my c project via command gcc -g -pthread main.c test.a -o main, all compiled and linked succsesful but in runtime i receive next error: panic: runtime error: cgo result has Go pointer The idea is receive pointer to DB in c, do some work and when DB not needed close DB.

John Weldon
  • 39,849
  • 11
  • 94
  • 127
Topilski Alexandr
  • 669
  • 1
  • 12
  • 34
  • 1
    You can't really do this. From Go's perspective, the `db` pointer no longer exists after the `open_db` call, and can be GC'ed right away, which is why that error exists. You're going to need to register the connection, and return some safe identifier to C. Alternatively, if there's only 1 db at a time, don't return it at all and handle it entire within Go. – JimB Oct 05 '17 at 18:01
  • Can i return uintptr_t from Go function? and cast it back in close_db function? Also is it possible add_ref to db manually? – Topilski Alexandr Oct 05 '17 at 19:02
  • 1
    No, changing the type has nothing to do with sending the pointer to C and not keeping a reference to it (plus the DB struct itself can contain many more pointers). You might be able to get around the cgo errors this way, but you're not going to be able to ensure that you're not going to crash or corrupt memory. – JimB Oct 05 '17 at 19:31

1 Answers1

0

As JimB says; this probably won't work (it's called unsafe for a reason). Even if you manage to cast the pointer to something that is consumable by C, there is major risk because of the garbage collection in Go potentially freeing the underlying resources because Go doesn't know about the usages in C

original answer

I think the signature for open_db should be: func open_db(path string) (unsafe.Pointer, error)

And the variable db should be converted to unsafe.Pointer; naively like this: return unsafe.Pointer(db), err but I don't know the specifics of the database implementation in this case.

Your close_db function will probably also need to accept an unsafe.Pointer instead of an interface{}

See: https://golang.org/cmd/cgo/

Community
  • 1
  • 1
John Weldon
  • 39,849
  • 11
  • 94
  • 127