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.
* 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.  `get` and `inc_ref` are basically
lock-free thanks to the `append_only_vec` crate and some atomic code.
`insert` and `remove` that frees an entry use a shared lock. We could
speed that up by keeping thread-local free lists, but that seems like
overkill since the lock should rarely be contended.

* 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.

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 27, 2023
1 parent 1e5d9b5 commit 65ba9aa
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 65ba9aa

Please sign in to comment.