go-ceph/rbd/watchers_mimic.go
Sven Anderson c8f26a841b rbd: eliminate invalid unsafe.Pointers
Although it seems to be stable with the current Go versions,
unsafe.Pointers that are created by applying arithmetic on nil
pointers are explicitly not allowed[1], and could potentially lead to
panics by the garbage collector.  To be on the safe side, this change
goes back to minimal inline wrappers for the callback registrations,
which should be zero-cost.  For the callbacks themselves fortunately
no wrappers are required, because CGo happily converts void* to
uintptr arguments.  The potentially problematic VoidPtr helper is
removed again.

[1] https://golang.org/pkg/unsafe/#Pointer

Signed-off-by: Sven Anderson <sven@redhat.com>
2020-11-09 08:58:22 +00:00

146 lines
3.5 KiB
Go

// +build !luminous
//
// Ceph Mimic is the first version that supports watchers through librbd.
package rbd
/*
#cgo LDFLAGS: -lrbd
#include <rbd/librbd.h>
extern void imageWatchCallback(uintptr_t);
// inline wrapper to cast uintptr_t to void*
static inline int wrap_rbd_update_watch(rbd_image_t image, uint64_t *handle,
uintptr_t arg) {
return rbd_update_watch(image, handle, (void*)imageWatchCallback, (void*)arg);
};
*/
import "C"
import (
"github.com/ceph/go-ceph/internal/callbacks"
"github.com/ceph/go-ceph/internal/retry"
)
// ImageWatcher is a representation of the rbd_image_watcher_t from librbd.h
type ImageWatcher struct {
Addr string
Id int64
Cookie uint64
}
// ListWatchers returns the watchers on an RBD image. In case of an error, nil
// and an error are returned.
//
// Note:
// Only supported in Ceph Mimic and newer.
//
// Implements:
// int rbd_watchers_list(rbd_image_t image,
// rbd_image_watcher_t *watchers, size_t *max_watchers)
func (image *Image) ListWatchers() ([]ImageWatcher, error) {
if err := image.validate(imageIsOpen); err != nil {
return nil, err
}
var (
err error
count C.size_t
watchers []C.rbd_image_watcher_t
)
retry.WithSizes(16, 4096, func(size int) retry.Hint {
count = C.size_t(size)
watchers = make([]C.rbd_image_watcher_t, count)
ret := C.rbd_watchers_list(image.image, &watchers[0], &count)
err = getErrorIfNegative(ret)
return retry.Size(int(count)).If(err == errRange)
})
if err != nil {
return nil, err
}
defer C.rbd_watchers_list_cleanup(&watchers[0], count)
imageWatchers := make([]ImageWatcher, count)
for i, watcher := range watchers[:count] {
imageWatchers[i].Addr = C.GoString(watcher.addr)
imageWatchers[i].Id = int64(watcher.id)
imageWatchers[i].Cookie = uint64(watcher.cookie)
}
return imageWatchers, nil
}
// watchCallbacks tracks the active callbacks for rbd watches
var watchCallbacks = callbacks.New()
// WatchCallback defines the function signature needed for the UpdateWatch
// callback.
type WatchCallback func(interface{})
type watchCallbackCtx struct {
callback WatchCallback
data interface{}
}
// Watch represents an ongoing image metadata watch.
type Watch struct {
image *Image
wcc watchCallbackCtx
handle C.uint64_t
cbIndex uintptr
}
// UpdateWatch updates the image object to watch metadata changes to the
// image, returning a Watch object.
//
// Implements:
// int rbd_update_watch(rbd_image_t image, uint64_t *handle,
// rbd_update_callback_t watch_cb, void *arg);
func (image *Image) UpdateWatch(cb WatchCallback, data interface{}) (*Watch, error) {
if err := image.validate(imageIsOpen); err != nil {
return nil, err
}
wcc := watchCallbackCtx{
callback: cb,
data: data,
}
w := &Watch{
image: image,
wcc: wcc,
cbIndex: watchCallbacks.Add(wcc),
}
ret := C.wrap_rbd_update_watch(
image.image,
&w.handle,
C.uintptr_t(w.cbIndex))
if ret != 0 {
return nil, getError(ret)
}
return w, nil
}
// Unwatch un-registers the image watch.
//
// Implements:
// int rbd_update_unwatch(rbd_image_t image, uint64_t handle);
func (w *Watch) Unwatch() error {
if w.image == nil {
return ErrImageNotOpen
}
if err := w.image.validate(imageIsOpen); err != nil {
return err
}
ret := C.rbd_update_unwatch(w.image.image, w.handle)
watchCallbacks.Remove(w.cbIndex)
return getError(ret)
}
//export imageWatchCallback
func imageWatchCallback(index uintptr) {
v := watchCallbacks.Lookup(index)
wcc := v.(watchCallbackCtx)
wcc.callback(wcc.data)
}