Provide Rust wrapper for memory resouces - #406
Conversation
|
The created documentation from the pull request is available at: docu-html |
|
@arkjedrz have a look please |
5c9aae6 to
962297f
Compare
962297f to
2ba153f
Compare
2ba153f to
a16e296
Compare
There was a problem hiding this comment.
Pull request overview
This PR introduces a new score/language/rust/memres Rust crate and accompanying C++ FFI layer to let Rust code create/own score::cpp::pmr::memory_resource instances and pass them back into C++ so allocations can be controlled across the language boundary (including a “hermetic” no-heap-fallback mode).
Changes:
- Add a C++ handle hierarchy (
MemResHandle+ concrete implementations) andextern "C"API for creating/destroying PMR resources and retrieving the underlyingmemory_resource*. - Add a safe Rust wrapper API (
MemoryResource,ResourceKind,PoolOptions) plus unit tests and an example demonstrating hermetic monotonic behavior. - Integrate the crate into Bazel + Cargo workspace, and bump the Rust toolchains Bazel dep version.
Reviewed changes
Copilot reviewed 18 out of 20 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| score/language/rust/memres/test/memres_test.cpp | Adds C++ gtest coverage for handle creation, monotonic hermetic abort, and non-hermetic fallback. |
| score/language/rust/memres/test/memres_rust_test.rs | Adds Rust unit tests exercising the Rust API and end-to-end FFI path. |
| score/language/rust/memres/src/lib.rs | Implements the Rust API wrapper, FFI bindings, and lifetime-bound pointer wrapper. |
| score/language/rust/memres/README.md | Documents design, safety/lifetime invariants, hermetic behavior, and Bazel targets. |
| score/language/rust/memres/example/main.rs | Example binary demonstrating hermetic monotonic behavior and abort-on-overflow. |
| score/language/rust/memres/example/example_helpers.h | Declares C ABI helpers used by the example and Rust tests. |
| score/language/rust/memres/example/example_helpers.cpp | Implements the C ABI helpers for allocating/filling a PMR-backed C++ vector. |
| score/language/rust/memres/example/BUILD | Adds Bazel targets for the example and helper library. |
| score/language/rust/memres/cpp/memory_resource_handle.h | Declares the handle class hierarchy owning PMR resources and upstream guard. |
| score/language/rust/memres/cpp/memory_resource_handle.cpp | Implements handle constructors and resource accessors. |
| score/language/rust/memres/cpp/guarded_upstream.h | Declares the upstream memory_resource enforcing hermetic/heap-fallback policy. |
| score/language/rust/memres/cpp/guarded_upstream.cpp | Implements guarded upstream allocate/deallocate and logging/abort behavior. |
| score/language/rust/memres/cpp/ffi.h | Declares the plain-C API consumed by Rust and C++ tests. |
| score/language/rust/memres/cpp/ffi.cpp | Implements the C API and bridges to the handle classes. |
| score/language/rust/memres/Cargo.toml | Adds the new Rust crate manifest. |
| score/language/rust/memres/BUILD | Adds Bazel cc/rust targets for the library and unit tests. |
| MODULE.bazel.lock | Updates lockfile due to Rust toolchain dep bump (and related module resolution changes). |
| MODULE.bazel | Bumps score_toolchains_rust dev dependency version. |
| Cargo.toml | Adds memres to workspace members/default-members and workspace dependency map. |
| Cargo.lock | Records the new workspace crate and updates some transitive crate versions. |
Suppressed comments (1)
score/language/rust/memres/cpp/guarded_upstream.cpp:35
- The warning log message concatenates tokens without spaces (e.g. "requested10bytes"), reducing its usefulness for diagnostics.
score::mw::log::LogWarn("memres") << "Memory resource exhausted: requested" << bytes << "bytes with alignment"
<< alignment << ". Falling back to system heap.";
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| /// Marker for a single-threaded resource that must not cross thread | ||
| /// boundaries. | ||
| pub struct Local; |
There was a problem hiding this comment.
In principle, this would be a false positive for MemoryResource<Local>, which is Local's purpose of usage on this PR, because MemoryResource carries a mutable pointer and is thus not Send + Sync. But it's true that someone could have another type with threading information, and reuse these definitions, say:
struct Borrower<'a, S> {
_resource: core::marker::PhantomData<&'a S>,
}In this case, Borrower<'_, Local> would be Send + Sync and therefore the original semantic would not be achieved, as pointed out by the AI. Therefore, I followed its suggestion and updated the code to explicitly state that Local shall never be Send + Sync.
| impl MemoryResource<Shared> { | ||
| /// Returns a handle backed by the process-global default memory resource | ||
| /// (`score::cpp::pmr::get_default_resource()`). | ||
| /// | ||
| /// The default resource synchronises internally, so the returned handle is | ||
| /// `Send + Sync` and may be shared across threads. | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics if the C++ side fails to allocate the thin wrapper handle. | ||
| pub fn default_resource() -> Self { | ||
| // SAFETY: memres_new_default allocates a thin non-owning wrapper around | ||
| // the global default resource; ownership of the wrapper is transferred | ||
| // to this struct and released via memres_destroy on drop. | ||
| let handle = ffi::memres_new_default(); | ||
| assert!( | ||
| !handle.is_null(), | ||
| "memres: C++ failed to allocate default resource handle" | ||
| ); | ||
| Self { | ||
| handle, | ||
| _marker: PhantomData, | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
This is not the case. The interface allows for Rust programs to interface with C++ code that use the memory resources. As the Rust interface does not export set_default_resource, there's no way for a Rust application to overload the default behavior.
| /// - [`Shared`]: the backing resource performs its own internal | ||
| /// synchronisation (currently only the process-global default resource, | ||
| /// whose `allocate`/`deallocate` forward to the data-race-free global | ||
| /// `operator new`/`operator delete`). `MemoryResource<Shared>` is therefore | ||
| /// `Send + Sync`. | ||
| /// - [`Local`] (the default): the backing resource is single-threaded |
There was a problem hiding this comment.
This is not the case. The interface allows for Rust programs to interface with C++ code that use the memory resources. As the Rust interface does not export set_default_resource, there's no way for a Rust application to overload the default behavior.
| score::mw::log::LogFatal("memres") << "Memory resource exhausted in hermetic mode: requested" << bytes | ||
| << "bytes with alignment" << alignment << ". Aborting."; |
There was a problem hiding this comment.
This is not the case, the elements separator is added by the backend.
a16e296 to
bfbb2fe
Compare
|
The "Rust Coverage (Demo)" workflow fails in this PR, and it looks like an issue in the coverage toolchain and not the code. We disabled the workflow in baselibs for now. There is ongoing work to improve the coverage tooling, and I hope once it's done, we can reenable the coverage for rust. eclipse-score/tooling#394 |
This allows Rust applications that interface with C++ to obtain memory resources to pass back to C++, such that the C++ code will allocate from them, allowing for controlling the memory allocation between the languages.