Skip to content

Commit

Permalink
examples(allocation): free memory after unmarshalling a result from t…
Browse files Browse the repository at this point in the history
…he guest (tetratelabs#1368)

Signed-off-by: Luca Burgazzoli <[email protected]>
  • Loading branch information
lburgazzoli committed Apr 21, 2023
1 parent 010f0a9 commit ca79698
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 9 deletions.
8 changes: 7 additions & 1 deletion examples/allocation/tinygo/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,15 @@ func main() {
if err != nil {
log.Panicln(err)
}
// Note: This pointer is still owned by TinyGo, so don't try to free it!

greetingPtr := uint32(ptrSize[0] >> 32)
greetingSize := uint32(ptrSize[0])

// We don't need the memory after deserialization: make sure it is freed.
if greetingPtr != 0 {
defer free.Call(ctx, uint64(greetingPtr))
}

// The pointer is a linear memory offset, which is where we write the name.
if bytes, ok := mod.Memory().Read(greetingPtr, greetingSize); !ok {
log.Panicf("Memory.Read(%d, %d) out of range of memory size %d",
Expand Down
20 changes: 15 additions & 5 deletions examples/allocation/tinygo/testdata/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ import (
"unsafe"
)

// #include <stdlib.h>
import "C"

// main is required for TinyGo to compile to Wasm.
func main() {}

Expand Down Expand Up @@ -70,10 +73,17 @@ func ptrToString(ptr uint32, size uint32) string {
}

// stringToPtr returns a pointer and size pair for the given string in a way
// compatible with WebAssembly numeric types.
// compatible with WebAssembly numeric types. The pointer is not automatically
// managed by tinygo but must be freed by the host.
func stringToPtr(s string) (uint32, uint32) {
buf := []byte(s)
ptr := &buf[0]
unsafePtr := uintptr(unsafe.Pointer(ptr))
return uint32(unsafePtr), uint32(len(buf))
if len(s) == 0 {
return 0, 0
}

size := C.ulong(len(s))
ptr := unsafe.Pointer(C.malloc(size))

copy(unsafe.Slice((*byte)(ptr), size), []byte(s))

return uint32(uintptr(ptr)), uint32(len(s))
}
Binary file modified examples/allocation/tinygo/testdata/greet.wasm
Binary file not shown.
17 changes: 14 additions & 3 deletions internal/integration_test/vs/bench_allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,13 +51,24 @@ func allocationCall(m Module, _ int) error {
}

// Now, we can call "greet", which reads the string we wrote to memory!
if err = m.CallI32I32_V(testCtx, "greet", namePtr, nameSize); err != nil {
return err
ptrSize, fnErr := m.CallI32I32_I64(testCtx, "greet", namePtr, nameSize)
if fnErr != nil {
return fnErr
}

// This pointer was allocated by Rust, but owned by Go, So, we have to
// deallocate it when finished
return m.CallI32_V(testCtx, "free", namePtr)
if err := m.CallI32_V(testCtx, "free", namePtr); err != nil {
return err
}

// This pointer was allocated with malloc. So, we have to deallocate it
// when finished
if err := m.CallI32_V(testCtx, "free", uint32(ptrSize>>32)); err != nil {
return err
}

return nil
}

func RunTestAllocation(t *testing.T, runtime func() Runtime) {
Expand Down
13 changes: 13 additions & 0 deletions internal/integration_test/vs/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ type Runtime interface {
type Module interface {
CallI32_I32(ctx context.Context, funcName string, param uint32) (uint32, error)
CallI32I32_V(ctx context.Context, funcName string, x, y uint32) error
CallI32I32_I64(ctx context.Context, funcName string, x, y uint32) (uint64, error)
CallI32_V(ctx context.Context, funcName string, param uint32) error
CallV_V(ctx context.Context, funcName string) error
CallI64_I64(ctx context.Context, funcName string, param uint64) (uint64, error)
Expand Down Expand Up @@ -183,6 +184,18 @@ func (m *wazeroModule) CallI32I32_V(ctx context.Context, funcName string, x, y u
return
}

func (m *wazeroModule) CallI32I32_I64(ctx context.Context, funcName string, x, y uint32) (uint64, error) {
results, err := m.funcs[funcName].Call(ctx, uint64(x), uint64(y))
if err != nil {
return 0, err
}
if len(results) > 0 {
return results[0], nil
}

return 0, nil
}

func (m *wazeroModule) CallI32_V(ctx context.Context, funcName string, param uint32) (err error) {
_, err = m.funcs[funcName].Call(ctx, uint64(param))
return
Expand Down

0 comments on commit ca79698

Please sign in to comment.