diff --git a/loader/libclang.go b/loader/libclang.go index 4a236ba0..0b18dbcd 100644 --- a/loader/libclang.go +++ b/loader/libclang.go @@ -21,7 +21,8 @@ int tinygo_clang_visitor(CXCursor c, CXCursor parent, CXClientData client_data); */ import "C" -var globalFileInfo *fileInfo +// refMap stores references to types, used for clang_visitChildren. +var refMap RefMap var diagnosticSeverity = [...]string{ C.CXDiagnostic_Ignored: "ignored", @@ -119,25 +120,17 @@ func (info *fileInfo) parseFragment(fragment string, cflags []string, posFilenam return errs } - if globalFileInfo != nil { - // There is a race condition here but that doesn't really matter as it - // is a sanity check anyway. - panic("libclang.go cannot be used concurrently yet") - } - globalFileInfo = info - defer func() { - globalFileInfo = nil - }() - + ref := refMap.Put(info) + defer refMap.Remove(ref) cursor := C.clang_getTranslationUnitCursor(unit) - C.clang_visitChildren(cursor, (*[0]byte)(unsafe.Pointer(C.tinygo_clang_visitor)), C.CXClientData(uintptr(0))) + C.clang_visitChildren(cursor, C.CXCursorVisitor(C.tinygo_clang_visitor), C.CXClientData(ref)) return nil } //export tinygo_clang_visitor func tinygo_clang_visitor(c, parent C.CXCursor, client_data C.CXClientData) C.int { - info := globalFileInfo + info := refMap.Get(unsafe.Pointer(client_data)).(*fileInfo) kind := C.clang_getCursorKind(c) switch kind { case C.CXCursor_FunctionDecl: diff --git a/loader/sync.go b/loader/sync.go new file mode 100644 index 00000000..c0b3ebc9 --- /dev/null +++ b/loader/sync.go @@ -0,0 +1,46 @@ +package loader + +import ( + "sync" + "unsafe" +) + +// #include +import "C" + +// RefMap is a convenient way to store opaque references that can be passed to +// C. It is useful if an API uses function pointers and you cannot pass a Go +// pointer but only a C pointer. +type RefMap struct { + refs map[unsafe.Pointer]interface{} + lock sync.Mutex +} + +// Put stores a value in the map. It can later be retrieved using Get. It must +// be removed using Remove to avoid memory leaks. +func (m *RefMap) Put(v interface{}) unsafe.Pointer { + m.lock.Lock() + defer m.lock.Unlock() + if m.refs == nil { + m.refs = make(map[unsafe.Pointer]interface{}, 1) + } + ref := C.malloc(1) + m.refs[ref] = v + return ref +} + +// Get returns a stored value previously inserted with Put. Use the same +// reference as you got from Put. +func (m *RefMap) Get(ref unsafe.Pointer) interface{} { + m.lock.Lock() + defer m.lock.Unlock() + return m.refs[ref] +} + +// Remove deletes a single reference from the map. +func (m *RefMap) Remove(ref unsafe.Pointer) { + m.lock.Lock() + defer m.lock.Unlock() + delete(m.refs, ref) + C.free(ref) +}