Skip to content

Commit

Permalink
Implemented slab storage that allocates handles (mozilla#1730)
Browse files Browse the repository at this point in the history
This will be used for passing handles across the FFI.  This have several
advantages as an FFI type:

* Generation counter to detect use-after-free bugs
* Slab ID to detect using a handle with the wrong type.
* The same data structures can be used on the foreign side, rather than us
  having to figure out how to leak references in all languages.
* Integers come with less gotchas.  For example, we use a bit to
  differentiate between foreign and Rust handles.  This would be
  possible with tagged pointers but there's a lot of details to worry
  about there.  See the `tagged_pointer` crate some.
* Our current code mixes actual pointers and usize integers.  For
  example, the callback continuation is a leaked pointer on Swift, but
  a usize map key on Kotlin.
* Constant width at 64 bits rather than using the platform word size.
  This will simplify some things, especially reading/writing them to
  `RustBuffer`
* Only the first 48 bits are significant which helps with languages like JS.

Performance should be pretty good.  Insert/get/remove are all lock-free
thanks to the `append_only_vec` crate and some atomic code:
  * For objects, this represents a small overhead over simply leaking
    the Arc.  The same is true for the Swift objects that we leak using
    `Unmanaged<>`.
  * For trait interfaces, this is probably a small gain compared to
    adding an extra box, then leaking it.
  * This is going to be way faster than the foreign code that uses a
    lock and a map.

The main disadvantage is the extra complexity, but it seems relatively
small to me.  The stress tests and loom tests give us good confidence
that the code is correct.

As mentioned above, I'm pretty sure that we can leverage this for
foreign handles as well, and should be able to remove some code on from
the bindings.
  • Loading branch information
bendk committed Oct 26, 2023
1 parent 72ad9fa commit d7f25b9
Show file tree
Hide file tree
Showing 4 changed files with 1,097 additions and 0 deletions.
61 changes: 61 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 12 additions & 0 deletions uniffi_core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,21 @@ once_cell = "1.10.0"
# Enable "async" so that receivers implement Future, no need for "std" since we don't block on them.
oneshot = { version = "0.1", features = ["async"] }
# Regular dependencies
append-only-vec = "0.1"
paste = "1.0"
static_assertions = "1.1.0"

[dev-dependencies]
rand = "0.8"

# We want to test the slab code with loom, but don't want to introduce it as a direct dependency
# because that would cause issue with the mozilla-central Rust vendoring. So, uncomment the `loom`
# dopendency before running the tests, then run:
#
# cargo test -p uniffi_core --release --config build.rustflags='"--cfg loom"' slab_loom_test
#
# loom = "0.7.1"

[features]
default = []
# `no_mangle` RustBuffer FFI functions
Expand Down
2 changes: 2 additions & 0 deletions uniffi_core/src/ffi/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ pub mod foreignexecutor;
pub mod rustbuffer;
pub mod rustcalls;
pub mod rustfuture;
pub mod slab;

pub use callbackinterface::*;
pub use ffidefault::FfiDefault;
Expand All @@ -21,3 +22,4 @@ pub use foreignexecutor::*;
pub use rustbuffer::*;
pub use rustcalls::*;
pub use rustfuture::*;
pub use slab::*;
Loading

0 comments on commit d7f25b9

Please sign in to comment.