-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
cabi: add Canonical ABI realloc (cabi_realloc) #3
Conversation
This is WIP, but probably safe to merge |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! Asked a few questions for myself to understand the implementation.
abi/realloc.go
Outdated
//go:wasmexport cabi_realloc | ||
func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer { | ||
if newsize <= size { | ||
return unsafe.Add(ptr, offset(uintptr(ptr), align)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that when newsize <= size
, it returns the original pointer with additional offset. But is this really working without reallocating and copying the old memory block to a new memory block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That’s an interesting example. If you realloc with a different alignment than the original allocation, presumably you’re storing a different type, in which case the copy shouldn’t happen. What does the spec say here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a spec for this realloc above and beyond what the C specification has? (If so, should we link to it?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea.
https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md#canonical-abi
The (realloc ...) option specifies a core function that is validated to have the following core function type:
(func (param $originalPtr i32)
(param $originalSize i32)
(param $alignment i32)
(param $newSize i32)
(result i32))
The Canonical ABI will use realloc both to allocate (passing 0 for the first two parameters) and reallocate. If the Canonical ABI needs realloc, validation requires this option to be present (there is no default).
abi/realloc.go
Outdated
} | ||
newptr := alloc(newsize, align) | ||
if size > 0 { | ||
copy(unsafe.Slice((*byte)(newptr), newsize), unsafe.Slice((*byte)(ptr), size)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not deallocating the old memory block after copying?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While standard Go doesn't have a the equivalent of free()
, since we're deferring to Go's runtime in both cases we should allow them to be garbage collected if possible. Unfortunately, there's no easy way to null out all references to the old pointer -- we just have to trust the client doesn't hold on to it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Mossaka does the spec confirm that once the memory is allocated with realloc
and the pointer is passed to a guest func, the memory is owned by the guest?
@dgryski will using unsafe.Pointer
in go:wasmexport
function signatures be sufficient to signal to the compiler that the value passed in is a pointer that can be GCed if not used?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For garbage collection, if there are no references to the pointer in the guest address space, then it will be collected. As with other uses of realloc, we'll need to be careful to keep track of the pointer in the guest if we don't want it accidentally reclaimed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i didn't find in the CM spec where it says the memory allocated with realloc
will be owned by the guest...
abi/realloc.go
Outdated
// | ||
//go:export cabi_realloc | ||
//go:wasmexport cabi_realloc | ||
func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What happens if allocation failed due to out of memory issue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I assume the runtime will panic? What do you think is best way to handle this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both Go and TinyGo panic with out of memory if they can't allocate a block.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is a runtime panic acceptable to the Component Model if it can't allocate memory?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ydnar There is no way to capture that allocation failure and retry or notify the caller. It's an immediate, uncatchable, runtime panic(). We don't have a choice in this case.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A runtime failure seems reasonable to me.
abi/realloc.go
Outdated
s := make([]uint64, min(size/align, 1)) | ||
return unsafe.Pointer(unsafe.SliceData(s)) | ||
default: | ||
s := make([][16]uint8, min(size/align, 1)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thinking this should be a struct or complex128
.
abi/realloc.go
Outdated
//go:export cabi_realloc | ||
//go:wasmexport cabi_realloc | ||
func realloc(ptr unsafe.Pointer, size, align, newsize uintptr) unsafe.Pointer { | ||
if newsize <= size { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't you want to check if ptr
is not nil
, or uintptr(ptr) != 0
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If newsize is 0, and ptr is 0, then it will always return 0. This is somewhat based on the Rust implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
This PR adds a function exported from the Wasm module named
cabi_realloc
used by the Component Model to allocate memory in the guest for host → guest function calls.Fixes #2.