2020-05-08 14:07:31 +00:00
|
|
|
package callbacks
|
|
|
|
|
|
|
|
import (
|
|
|
|
"sync"
|
|
|
|
)
|
|
|
|
|
|
|
|
// The logic of this file is largely adapted from:
|
|
|
|
// https://github.com/golang/go/wiki/cgo#function-variables
|
|
|
|
//
|
|
|
|
// Also helpful:
|
|
|
|
// https://eli.thegreenplace.net/2019/passing-callbacks-and-pointers-to-cgo/
|
|
|
|
|
|
|
|
// Callbacks provides a tracker for data that is to be passed between Go
|
|
|
|
// and C callback functions. The Go callback/object may not be passed
|
2020-12-09 05:46:45 +00:00
|
|
|
// by a pointer to C code and so instead integer IDs into an internal
|
2020-05-08 14:07:31 +00:00
|
|
|
// map are used.
|
|
|
|
// Typically the item being added will either be a callback function or
|
|
|
|
// a data structure containing a callback function. It is up to the caller
|
|
|
|
// to control and validate what "callbacks" get used.
|
|
|
|
type Callbacks struct {
|
2020-12-09 05:46:45 +00:00
|
|
|
mutex sync.RWMutex
|
|
|
|
cmap map[uintptr]interface{}
|
|
|
|
lastID uintptr
|
2020-05-08 14:07:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// New returns a new callbacks tracker.
|
|
|
|
func New() *Callbacks {
|
2020-10-13 15:22:31 +00:00
|
|
|
return &Callbacks{cmap: make(map[uintptr]interface{})}
|
2020-05-08 14:07:31 +00:00
|
|
|
}
|
|
|
|
|
2020-12-09 05:46:45 +00:00
|
|
|
// getID returns a unique ID.
|
|
|
|
// NOTE: cb.mutex must be locked already!
|
|
|
|
func (cb *Callbacks) getID() uintptr {
|
|
|
|
for exists := true; exists; {
|
|
|
|
cb.lastID++
|
|
|
|
// Sanity check for the very unlikely case of an integer overflow in long
|
|
|
|
// running processes.
|
|
|
|
_, exists = cb.cmap[cb.lastID]
|
|
|
|
}
|
|
|
|
return cb.lastID
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add a callback/object to the tracker and return a new ID
|
2020-05-08 14:07:31 +00:00
|
|
|
// for the object.
|
2020-10-13 15:22:31 +00:00
|
|
|
func (cb *Callbacks) Add(v interface{}) uintptr {
|
2020-05-08 14:07:31 +00:00
|
|
|
cb.mutex.Lock()
|
|
|
|
defer cb.mutex.Unlock()
|
2020-12-09 05:46:45 +00:00
|
|
|
id := cb.getID()
|
|
|
|
cb.cmap[id] = v
|
|
|
|
return id
|
2020-05-08 14:07:31 +00:00
|
|
|
}
|
|
|
|
|
2020-12-09 05:46:45 +00:00
|
|
|
// Remove a callback/object given it's ID.
|
|
|
|
func (cb *Callbacks) Remove(id uintptr) {
|
2020-05-08 14:07:31 +00:00
|
|
|
cb.mutex.Lock()
|
|
|
|
defer cb.mutex.Unlock()
|
2020-12-09 05:46:45 +00:00
|
|
|
delete(cb.cmap, id)
|
2020-05-08 14:07:31 +00:00
|
|
|
}
|
|
|
|
|
2020-12-09 05:46:45 +00:00
|
|
|
// Lookup returns a mapped callback/object given an ID.
|
|
|
|
func (cb *Callbacks) Lookup(id uintptr) interface{} {
|
2020-05-08 14:07:31 +00:00
|
|
|
cb.mutex.RLock()
|
|
|
|
defer cb.mutex.RUnlock()
|
2020-12-09 05:46:45 +00:00
|
|
|
return cb.cmap[id]
|
2020-05-08 14:07:31 +00:00
|
|
|
}
|