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 (ref)
  • Loading branch information
lburgazzoli committed Apr 21, 2023
1 parent 85d6e04 commit 3671d22
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 13 deletions.
3 changes: 2 additions & 1 deletion examples/allocation/tinygo/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ func main() {
// These are undocumented, but exported. See tinygo-org/tinygo#2788
malloc := mod.ExportedFunction("malloc")
free := mod.ExportedFunction("free")
deallocate := mod.ExportedFunction("deallocate")

// Let's use the argument to this main function in Wasm.
name := os.Args[1]
Expand Down Expand Up @@ -96,7 +97,7 @@ func main() {

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

// The pointer is a linear memory offset, which is where we write the name.
Expand Down
21 changes: 14 additions & 7 deletions examples/allocation/tinygo/testdata/greet.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,18 +72,25 @@ func ptrToString(ptr uint32, size uint32) string {
}))
}

var alivePointers = map[uintptr]interface{}{}

// stringToPtr returns a pointer and size pair for the given string in a way
// 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) {
if len(s) == 0 {
return 0, 0
}
buf := []byte(s)
ptr := &buf[0]
unsafePtr := uintptr(unsafe.Pointer(ptr))

size := C.ulong(len(s))
ptr := unsafe.Pointer(C.malloc(size))
alivePointers[unsafePtr] = buf

copy(unsafe.Slice((*byte)(ptr), size), []byte(s))
return uint32(unsafePtr), uint32(len(buf))
}

return uint32(uintptr(ptr)), uint32(len(s))
// deallocate frees a uintptr returned by keepaliveBuf or allocate, allowing it
// to be garbage collected.
//
//export deallocate
func _deallocate(ptr uint32) {
delete(alivePointers, uintptr(ptr))
}
Binary file modified examples/allocation/tinygo/testdata/greet.wasm
Binary file not shown.
10 changes: 5 additions & 5 deletions internal/integration_test/vs/bench_allocation.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func init() {

func allocationCall(m Module, _ int) error {
nameSize := uint32(len(allocationParam))
// Instead of an arbitrary memory offset, use Rust's allocator. Notice
// Instead of an arbitrary memory offset, use guest's allocator. Notice
// there is nothing string-specific in this allocation function. The same
// function could be used to pass binary serialized data to Wasm.
namePtr, err := m.CallI32_I32(testCtx, "malloc", nameSize)
Expand All @@ -56,15 +56,15 @@ func allocationCall(m Module, _ int) error {
return fnErr
}

// This pointer was allocated by Rust, but owned by Go, So, we have to
// This pointer was allocated by guest, but owned by Go, So, we have to
// deallocate it when finished
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 {
// deallocate removes a uintptr from a map which stores a reference to the
// buffer ptrSize points to, allowing it to be garbage collected.
if err := m.CallI32_V(testCtx, "deallocate", uint32(ptrSize>>32)); err != nil {
return err
}

Expand Down

0 comments on commit 3671d22

Please sign in to comment.