From bea415417e87fbb403095e8cd3fb8512a1a97af8 Mon Sep 17 00:00:00 2001 From: Vadim Markovtsev Date: Fri, 10 Jun 2016 18:49:50 +0200 Subject: Add cshared files to allow building wrappers in other languages --- cshared/README.md | 79 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 cshared/README.md (limited to 'cshared/README.md') diff --git a/cshared/README.md b/cshared/README.md new file mode 100644 index 0000000..51e424e --- /dev/null +++ b/cshared/README.md @@ -0,0 +1,79 @@ +cshared +======= + +Building +-------- +go 1.6+ +``` +go build -o libgogit.so -buildmode=c-shared github.com/src-d/go-git/cshared +``` +Two files must appear: libgogit.h and libgogit.so. The second must be +a shared library, not an ar archive (may happen when something goes wrong). +Check the exported symbols with `nm -g`. + +How it works +------------ + +Nearly every public Go function is mirrored in the corresponding *_cshared.go +file. struct fields are also mirrored with getters and setters. The functions +are marked with `//export ...` "magic" cgo comment so that they appear +in defined symbols of a shared library built with `-buildmode=c-shared`. + +Go pointers may not be passed out of cgo functions, so we maintain the +two-way registry of all active Go objects mapped to `Handle`-s (`uint64`). +Every time we need to return a reference to Go object outside, we call +`RegisterObject(interface{})` which returns a new `Handle` or reuses +an existing one if the object has already been registered. Then we +return the obtained `Handle`. When we need to receive a Go object reference +in cgo function parameters, we accept `uint64` and retrieve the `interface{}` +with `GetObject(Handle)` which can be casted to the underlying type with a +type assertion. When the object is no longer needed, we invoke +`UnregisterObject(Handle)`. + +Although `interface{]` is just two `uintptr`-s inside, it is not a hashable +type and we cannot use it a as key in our backward registry mapping. +We are using the data `uintptr` as the key there. Since several distinct +objects may exist with the same data pointer (e.g. struct and first field +of the struct), the value of that mapping is a slice of `Handle`-s. + +All the mentioned service functions are goroutine- and threadsafe. + +`std_cshared.go` contains the cgo wrappers for standard library objects. + +Debugging +--------- +`c_dump_object()` prints the current state of the two-way object registry +to stdout. `c_set_trace()` activates echoing of `RegisterObject()` and +`UnregisterObject()` invocations. + +Caveats +------- +Normally, we pass over a pointer to object as `interface{}` into `RegisterObject()` +so that it can be mutated later. It requires the corresponding +pointer-to-type type assertion in cgo functions. If you mess with this, +the cgo function will, of course, panic. + +A cgo function is allowed to take Go's `string` parameters. `string`'s +data must point to some memory and cgo does not copy the incoming foreign +memory into Go memory automatically. What's worse, `string`-s are immutable +and when you copy it, the copy points to the same memory. This means that +if you pass in a `string` which was constructed using `malloc()`, for example, +and later `free()` it, all Go strings created from the function parameter +will point to the invalid memory. Actually, this allowance violates the +cgo pointer passing rules stated just several blocks of texts +below the example of string parameters - this is crazy, but we have to live +with this, as usual in Go world. So, *all incoming `string`-s must be immediately +safely copied with `CopyString()` once they are used*. + +Returning strings and byte slices is also funny: you have to use `C.CString` -> `*C.char` +and additionally return the length as another result tuple member if needed. +`C.CString` copies the memory pointed by `string` to a `malloc()`-ed region +and it is the responsibility of the other side to `free()` it or it will leak +otherwise. + +Another tricky part is in `c_std_map_get_str_str` and similar places +where you need to return `*C.char` from an unaddressed array accessed under +a pseudonym type through reflection. The only way I've found working +is using `reflect.Copy` to byte slice (copy) and then conversion to +`string` (copy), then `C.CString` (copy) and finally another (copy) on the +receiving side because the latter must be `free()`-d. Extremely efficient. \ No newline at end of file -- cgit