Skip to content

Commit 3b45071

Browse files
author
Ivan
committed
Merge branch '107-sync' into 'dev'
Sync Closes #107 See merge request objectbox/objectbox-go!68
2 parents 31bb4b9 + 8721f62 commit 3b45071

16 files changed

+1736
-2
lines changed

objectbox/c-arrays.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ package objectbox
1818

1919
/*
2020
#include <stdlib.h>
21-
#include "objectbox.h"
21+
#include "objectbox-sync.h"
2222
2323
char** newCharArray(int size) {
2424
return calloc(sizeof(char*), size);

objectbox/c-callbacks-c.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* Copyright 2019 ObjectBox Ltd. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package objectbox
18+
19+
// This file implements externs defined in c-callbacks.go.
20+
// It needs to be separate or it would cause duplicate symbol errors during linking.
21+
// See https://golang.org/cmd/cgo/#hdr-C_references_to_Go for more details.
22+
23+
/*
24+
#include <stdbool.h>
25+
#include <stdint.h>
26+
*/
27+
import "C"
28+
import (
29+
"unsafe"
30+
)
31+
32+
// These functions find the callback based on the pointer to the callbackId and call it.
33+
34+
//export cVoidCallbackDispatch
35+
func cVoidCallbackDispatch(callbackIdPtr unsafe.Pointer) {
36+
var callback = cCallbackLookup(callbackIdPtr)
37+
if callback != nil {
38+
callback.callVoid()
39+
}
40+
}
41+
42+
//export cVoidUint64CallbackDispatch
43+
func cVoidUint64CallbackDispatch(callbackIdPtr unsafe.Pointer, arg uint64) {
44+
var callback = cCallbackLookup(callbackIdPtr)
45+
if callback != nil {
46+
callback.callVoidUint64(arg)
47+
}
48+
}
49+
50+
//export cVoidInt64CallbackDispatch
51+
func cVoidInt64CallbackDispatch(callbackIdPtr unsafe.Pointer, arg int64) {
52+
var callback = cCallbackLookup(callbackIdPtr)
53+
if callback != nil {
54+
callback.callVoidInt64(arg)
55+
}
56+
}
57+
58+
//export cVoidConstVoidCallbackDispatch
59+
func cVoidConstVoidCallbackDispatch(callbackIdPtr unsafe.Pointer, arg unsafe.Pointer) {
60+
var callback = cCallbackLookup(callbackIdPtr)
61+
if callback != nil {
62+
callback.callVoidConstVoid(arg)
63+
}
64+
}

objectbox/c-callbacks.go

Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
/*
2+
* Copyright 2019 ObjectBox Ltd. All rights reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package objectbox
18+
19+
/*
20+
This file implements some universal formats of C callbacks forwarding to Go callbacks
21+
22+
Overview:
23+
* Register a callback, getting a callback ID.
24+
* Pass the registered callback ID together with a generic C callback (e.g. C.cVoidCallbackDispatch) to a C.obx_* function.
25+
* When ObjectBox calls C.cVoidCallbackDispatch, it finds the callback registered under that ID and calls it.
26+
* After there can be no more callbacks, the callback must be unregistered.
27+
28+
Code example:
29+
callbackId, err := cCallbackRegister(cVoidCallback(func() { // cVoidCallback() is a type-cast
30+
// do your thing here
31+
}))
32+
if err != nil {
33+
return err
34+
}
35+
36+
// don't forget to unregister the callback after it's no longer going to be called or you would fill the queue up quickly
37+
defer cCallbackUnregister(callbackId)
38+
39+
rc := C.obx_callback_taking_method(cStruct, (*C.actual_fn_type_required_by_c_method)(cVoidCallbackDispatch), unsafe.Pointer(&callbackId))
40+
*/
41+
42+
/*
43+
#include "objectbox.h"
44+
45+
// following functions implement forwarding and are passed to the c-api
46+
47+
// void return, no arguments
48+
extern void cVoidCallbackDispatch(void* callbackId);
49+
typedef void cVoidCallback(void* callbackId);
50+
51+
// void return, uint64 argument
52+
extern void cVoidUint64CallbackDispatch(void* callbackId);
53+
typedef void cVoidUint64Callback(void* callbackId, uint64_t arg);
54+
55+
// void return, int64 argument
56+
extern void cVoidInt64CallbackDispatch(void* callbackId);
57+
typedef void cVoidInt64Callback(void* callbackId, int64_t arg);
58+
59+
// void return, const void* argument
60+
extern void cVoidConstVoidCallbackDispatch(void* callbackId);
61+
typedef void cVoidConstVoidCallback(void* callbackId, const void* arg);
62+
*/
63+
import "C"
64+
import (
65+
"fmt"
66+
"sync"
67+
"unsafe"
68+
)
69+
70+
// cCallable allows us to avoid defining below register/unregister/lookup methods for all possible function signatures.
71+
// This interface is implemented by callback functions and each function signature should only implement a single method
72+
// and panic in all others. Method name format used below: "call<ReturnType><...ArgNType>()"
73+
type cCallable interface {
74+
callVoid()
75+
callVoidUint64(uint64)
76+
callVoidInt64(int64)
77+
callVoidConstVoid(unsafe.Pointer)
78+
}
79+
80+
// programming error - using an incorrect `cCallable` (arguments and return-type combination)
81+
const cCallablePanicMsg = "invalid callback signature"
82+
83+
type cVoidCallback func()
84+
85+
func (fn cVoidCallback) callVoid() { fn() }
86+
func (fn cVoidCallback) callVoidUint64(uint64) { panic(cCallablePanicMsg) }
87+
func (fn cVoidCallback) callVoidInt64(int64) { panic(cCallablePanicMsg) }
88+
func (fn cVoidCallback) callVoidConstVoid(unsafe.Pointer) { panic(cCallablePanicMsg) }
89+
90+
var cVoidCallbackDispatchPtr = (*C.cVoidCallback)(unsafe.Pointer(C.cVoidCallbackDispatch))
91+
92+
type cVoidUint64Callback func(uint64)
93+
94+
func (fn cVoidUint64Callback) callVoid() { panic(cCallablePanicMsg) }
95+
func (fn cVoidUint64Callback) callVoidUint64(arg uint64) { fn(arg) }
96+
func (fn cVoidUint64Callback) callVoidInt64(int64) { panic(cCallablePanicMsg) }
97+
func (fn cVoidUint64Callback) callVoidConstVoid(unsafe.Pointer) { panic(cCallablePanicMsg) }
98+
99+
var cVoidUint64CallbackDispatchPtr = (*C.cVoidUint64Callback)(unsafe.Pointer(C.cVoidUint64CallbackDispatch))
100+
101+
type cVoidInt64Callback func(int64)
102+
103+
func (fn cVoidInt64Callback) callVoid() { panic(cCallablePanicMsg) }
104+
func (fn cVoidInt64Callback) callVoidUint64(uint64) { panic(cCallablePanicMsg) }
105+
func (fn cVoidInt64Callback) callVoidInt64(arg int64) { fn(arg) }
106+
func (fn cVoidInt64Callback) callVoidConstVoid(unsafe.Pointer) { panic(cCallablePanicMsg) }
107+
108+
var cVoidInt64CallbackDispatchPtr = (*C.cVoidInt64Callback)(unsafe.Pointer(C.cVoidInt64CallbackDispatch))
109+
110+
type cVoidConstVoidCallback func(unsafe.Pointer)
111+
112+
func (fn cVoidConstVoidCallback) callVoid() { panic(cCallablePanicMsg) }
113+
func (fn cVoidConstVoidCallback) callVoidUint64(uint64) { panic(cCallablePanicMsg) }
114+
func (fn cVoidConstVoidCallback) callVoidInt64(int64) { panic(cCallablePanicMsg) }
115+
func (fn cVoidConstVoidCallback) callVoidConstVoid(arg unsafe.Pointer) { fn(arg) }
116+
117+
var cVoidConstVoidCallbackDispatchPtr = (*C.cVoidConstVoidCallback)(unsafe.Pointer(C.cVoidConstVoidCallbackDispatch))
118+
119+
type cCallbackId uintptr
120+
121+
var cCallbackLastId cCallbackId
122+
var cCallbackMutex sync.Mutex
123+
var cCallbackMap = make(map[cCallbackId]cCallable)
124+
125+
// The result is actually not a memory pointer, just a number. That's also how it's used in cCallbackLookup().
126+
func (cbId cCallbackId) cPtrArg() unsafe.Pointer {
127+
//goland:noinspection GoVetUnsafePointer
128+
return unsafe.Pointer(cbId)
129+
}
130+
131+
// Returns the next cCallbackId in a sequence (NOT checking its availability), skipping zero.
132+
func cCallbackNextId() cCallbackId {
133+
cCallbackLastId++
134+
if cCallbackLastId == 0 {
135+
cCallbackLastId++
136+
}
137+
return cCallbackLastId
138+
}
139+
140+
func cCallbackRegister(fn cCallable) (cCallbackId, error) {
141+
cCallbackMutex.Lock()
142+
defer cCallbackMutex.Unlock()
143+
144+
// cycle through ids until we find an empty slot
145+
var initialId = cCallbackNextId()
146+
for cCallbackMap[cCallbackLastId] != nil {
147+
cCallbackNextId()
148+
149+
if initialId == cCallbackLastId {
150+
return 0, fmt.Errorf("full queue of data-callback callbacks - can't allocate another")
151+
}
152+
}
153+
154+
cCallbackMap[cCallbackLastId] = fn
155+
return cCallbackLastId, nil
156+
}
157+
158+
func cCallbackLookup(id unsafe.Pointer) cCallable {
159+
cCallbackMutex.Lock()
160+
defer cCallbackMutex.Unlock()
161+
162+
fn, found := cCallbackMap[cCallbackId(id)]
163+
if !found {
164+
// this might happen in extraordinary circumstances, e.g. during shutdown if there are still some sync listeners
165+
fmt.Println(fmt.Errorf("invalid C-API callback ID %d", id))
166+
return nil
167+
}
168+
169+
return fn
170+
}
171+
172+
func cCallbackUnregister(id cCallbackId) {
173+
// special value - not registered
174+
if id == 0 {
175+
return
176+
}
177+
178+
cCallbackMutex.Lock()
179+
defer cCallbackMutex.Unlock()
180+
181+
delete(cCallbackMap, id)
182+
}

0 commit comments

Comments
 (0)