diff --git a/cephfs/command.go b/cephfs/command.go index 3ef780c..9ed23bc 100644 --- a/cephfs/command.go +++ b/cephfs/command.go @@ -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( diff --git a/internal/cutil/command_output.go b/internal/cutil/command_output.go index b6863a7..8b0c6e5 100644 --- a/internal/cutil/command_output.go +++ b/internal/cutil/command_output.go @@ -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) +} diff --git a/internal/cutil/command_output_test.go b/internal/cutil/command_output_test.go index 41619a6..74278b1 100644 --- a/internal/cutil/command_output_test.go +++ b/internal/cutil/command_output_test.go @@ -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) + }) } diff --git a/internal/cutil/type_aliases.go b/internal/cutil/type_aliases.go index f566fda..79495ef 100644 --- a/internal/cutil/type_aliases.go +++ b/internal/cutil/type_aliases.go @@ -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) diff --git a/rados/command.go b/rados/command.go index 1d6a1b5..1ec0422 100644 --- a/rados/command.go +++ b/rados/command.go @@ -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(