aboutsummaryrefslogtreecommitdiffstats
path: root/cshared/README.md
diff options
context:
space:
mode:
authorVadim Markovtsev <vadim@sourced.tech>2016-06-10 18:49:50 +0200
committerVadim Markovtsev <vadim@sourced.tech>2016-06-15 10:29:46 +0200
commitbea415417e87fbb403095e8cd3fb8512a1a97af8 (patch)
treee34cbd4d5dc894be5d6f9cc9570366b1cbc5f5ed /cshared/README.md
parent08f9e7015aad2ca768638b446fb8632f11601899 (diff)
downloadgo-git-bea415417e87fbb403095e8cd3fb8512a1a97af8.tar.gz
Add cshared files to allow building wrappers in other languages
Diffstat (limited to 'cshared/README.md')
-rw-r--r--cshared/README.md79
1 files changed, 79 insertions, 0 deletions
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