mirror of
https://github.com/ceph/go-ceph
synced 2025-01-09 15:29:53 +00:00
c8f26a841b
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>
146 lines
3.5 KiB
Go
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)
|
|
}
|