cutil: allow passing free functions to command output type

The *_command functions in librados and libcephfs document the use
of specific free functions for data allocated. These functions are
currently just wrappers around C's free() function. However, to be
more strictly compliant this change adds a free-function callback
to the CommandOutput type and the specific free functions are now
used outside the unit tests.

Signed-off-by: John Mulligan <jmulligan@redhat.com>
This commit is contained in:
John Mulligan 2020-04-23 11:10:15 -04:00 committed by John Mulligan
parent f2d0bb4692
commit e2a78eec02
5 changed files with 50 additions and 7 deletions

View File

@ -14,6 +14,10 @@ import (
"github.com/ceph/go-ceph/internal/cutil"
)
func cephBufferFree(p unsafe.Pointer) {
C.ceph_buffer_free((*C.char)(p))
}
// MdsCommand sends commands to the specified MDS.
func (mount *MountInfo) MdsCommand(mdsSpec string, args [][]byte) ([]byte, string, error) {
return mount.mdsCommand(mdsSpec, args, nil)
@ -40,7 +44,7 @@ func (mount *MountInfo) mdsCommand(mdsSpec string, args [][]byte, inputBuffer []
defer C.free(unsafe.Pointer(spec))
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput()
co := cutil.NewCommandOutput().SetFreeFunc(cephBufferFree)
defer co.Free()
ret := C.ceph_mds_command(

View File

@ -12,6 +12,7 @@ import (
// CommandOutput can be used to manage the outputs of ceph's *_command
// functions.
type CommandOutput struct {
free FreeFunc
outBuf *C.char
outBufLen C.size_t
outs *C.char
@ -22,16 +23,28 @@ type CommandOutput struct {
// a CommandOutput provides can be used to get the results of ceph's
// *_command functions.
func NewCommandOutput() *CommandOutput {
return &CommandOutput{}
return &CommandOutput{
free: free,
}
}
// SetFreeFunc sets the function used to free memory held by CommandOutput.
// Not all uses of CommandOutput expect to use the basic C.free function
// and either require or prefer the use of a custom deallocation function.
// Use SetFreeFunc to change the free function and return the modified
// CommandOutput object.
func (co *CommandOutput) SetFreeFunc(f FreeFunc) *CommandOutput {
co.free = f
return co
}
// Free any C memory tracked by this object.
func (co *CommandOutput) Free() {
if co.outBuf != nil {
C.free(unsafe.Pointer(co.outBuf))
co.free(unsafe.Pointer(co.outBuf))
}
if co.outs != nil {
C.free(unsafe.Pointer(co.outs))
co.free(unsafe.Pointer(co.outs))
}
}
@ -79,3 +92,9 @@ func testSetString(strp CharPtrPtr, lenp SizeTPtr, s string) {
*sp = C.CString(s)
*lp = C.size_t(len(s))
}
// free wraps C.free.
// Required for unit tests that may not use cgo directly.
func free(p unsafe.Pointer) {
C.free(p)
}

View File

@ -2,6 +2,7 @@ package cutil
import (
"testing"
"unsafe"
"github.com/stretchr/testify/assert"
)
@ -40,4 +41,16 @@ func TestCommandOutput(t *testing.T) {
assert.Nil(t, b)
assert.EqualValues(t, "i got rhythm", s)
})
t.Run("customFreeFunc", func(t *testing.T) {
callCount := 0
co := NewCommandOutput().SetFreeFunc(func(p unsafe.Pointer) {
callCount++
free(p)
})
assert.NotNil(t, co)
testSetString(co.OutBuf(), co.OutBufLen(), "i got style")
testSetString(co.Outs(), co.OutsLen(), "i got rhythm")
co.Free()
assert.Equal(t, 2, callCount)
})
}

View File

@ -23,3 +23,6 @@ type CharPtr unsafe.Pointer
// SizeTPtr is an unsafe pointer wrapping C's `size_t*`.
type SizeTPtr unsafe.Pointer
// FreeFunc is a wrapper around calls to, or act like, C's free function.
type FreeFunc func(unsafe.Pointer)

View File

@ -11,6 +11,10 @@ import (
"github.com/ceph/go-ceph/internal/cutil"
)
func radosBufferFree(p unsafe.Pointer) {
C.rados_buffer_free((*C.char)(p))
}
// MonCommand sends a command to one of the monitors
func (c *Conn) MonCommand(args []byte) ([]byte, string, error) {
return c.monCommand(args, nil)
@ -24,7 +28,7 @@ func (c *Conn) MonCommandWithInputBuffer(args, inputBuffer []byte) ([]byte, stri
func (c *Conn) monCommand(args, inputBuffer []byte) ([]byte, string, error) {
ci := cutil.NewCommandInput([][]byte{args}, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_mon_command(
@ -70,7 +74,7 @@ func (c *Conn) pgCommand(pgid []byte, args [][]byte, inputBuffer []byte) ([]byte
defer C.free(unsafe.Pointer(name))
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_pg_command(
@ -107,7 +111,7 @@ func (c *Conn) MgrCommandWithInputBuffer(args [][]byte, inputBuffer []byte) ([]b
func (c *Conn) mgrCommand(args [][]byte, inputBuffer []byte) ([]byte, string, error) {
ci := cutil.NewCommandInput(args, inputBuffer)
defer ci.Free()
co := cutil.NewCommandOutput()
co := cutil.NewCommandOutput().SetFreeFunc(radosBufferFree)
defer co.Free()
ret := C.rados_mgr_command(