Skip to content

Allow passing pre-allocated buffer to proxy_get_buffer_bytes #83

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

Open
leonm1 opened this issue May 2, 2025 · 2 comments
Open

Allow passing pre-allocated buffer to proxy_get_buffer_bytes #83

leonm1 opened this issue May 2, 2025 · 2 comments

Comments

@leonm1
Copy link
Contributor

leonm1 commented May 2, 2025

We've seen a common pattern emerging where plugin authors will call proxy_get_buffer_bytes with a specific length in mind, do something with the buffer, then clear all buffers when returning from the callback.

By allowing authors to pass a pre-allocated buffer, we'd permit plugin authors to stack-allocate this memory, which is cheaper to manage than heap allocating. In the case of garbage collected languages, such as Go, this would allow us to completely bypass the garbage collector.

Furthermore, this approach can provide plugin authors significantly more control over the memory usage when using garbage collected languages, since the contents of the buffer are guaranteed to be freed at the end of the callback (and individual callbacks in a thread local vm are guaranteed non-concurrent).

@leonm1
Copy link
Contributor Author

leonm1 commented May 2, 2025

FWIW this is currently under-specified in the spec (and is a non-breaking change), so this may be permissible to change without a major version bump.

@PiotrSikora
Copy link
Member

This is already possible, proxy_on_memory_allocate can return whatever memory block it wants.

It's not the cleanest API, but you can do either of those right now:

  • always return the same statically allocated memory block:
fn proxy_on_memory_allocate(size: usize) -> *mut u8 {
    STATIC_MEMORY.as_mut_ptr();
}
  • set memory block to return at the caller:
fn proxy_on_memory_allocate(size: usize) -> *mut u8 {
    MEMORY_TO_RETURN.as_mut_ptr();
}
impl HttpContext for HttpAuthRandom {
    fn on_http_request_headers(&mut self, _: usize, _: bool) -> Action {
        let mut foo = Vec::with_capacity(16 * 1024);
        MEMORY_TO_RETURN = foo;
        for (name, value) in &self.get_http_request_headers() {
            info!("#{} -> {}: {}", self.context_id, name, value);
        }
        Action::Continue
    }
}

(the code might not compile)

This works since the memory ownership is always immediately passed to the guest and never retained by the host (the pattern is: host requests memory from the guest, host copies some data to that memory, host returns that memory to the guest) and it never allocates multiple times within the same callback).

Having said that, since we're changing all the signatures as part of v0.3, I'm not against adding explicit memory pointer and size to the function calls, and "fallback" to calling proxy_on_memory_allocate only when those are empty.

Note that this would affect pretty much all functions that copy data, not only proxy_get_buffer_bytes.

What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants