Deref Mutex/RwLock to inner object with implicit locking - rust

I have a struct Obj with a large API that I would also like to use through an Arc<RwLock<Obj>>. I have defined a struct ObjRef(Arc<RwLock<Obj>>) and want to call functions of Obj on ObjRef without needing to call .read().unwrap() or .write().unwrap() every time. (ObjRef implements Deref<Target=Arc<RwLock<Obj>>, so I only have to call objRef.read().unwrap())
I could implement all of the functions of Obj again for ObjRef in a manner like this:
impl ObjRef {
fn foo(&self, arg: Arg) -> Ret {
self.read().unwrap().foo(arg)
}
}
But doing this for every function results in a lot of boilerplate.
Implementing Deref<Target=Obj> for ObjRef does not work, because the deref implementation would be returning a reference into the RwReadLockGuard object returned by .read().unwrap() which would be dropped after deref:
impl Deref for ObjRef {
type Target = Obj;
fn deref(&self) -> &Self::Target {
let guard: RwLockReadGuard<'_, Obj> = self.0.read().unwrap();
&*guard // <- return reference to Obj referenced by guard
// guard is dropped now, reference lifetime ends
}
}
Is there a way to call the Obj Api through a low boilerplate implementation doing the locking internally?
I am thinking about introducing a trait for the Api with a default implementation of the Api and a single function to be implemented by users of the trait for getting a generic Deref<Target=Obj>, but this puts the Api under some limitations, for example using generic parameters will become much more complicated.
Is there maybe a better way, something that lets me use any Obj through a lock without explicitly calling the locking mechanism every time?

It is not possible to do this through Deref, because Deref::deref always returns a plain reference — the validity of it must only depend on the argument continuing to exist (which the borrow checker understands), not on any additional state such as "locked".
Also, adding locking generically to an interface designed without it is dangerous, because it may accidentally cause deadlocks, or allow unwanted outcomes due to doing two operations in sequence that should have been done using a single locking.
I recommend considering making your type internally an Arc<RwLock, that is,
pub struct Obj {
lock: Arc<RwLock<ObjData>>,
}
// internal helper, not public
struct ObjData {
// whatever fields Obj would have had go here
}
Then, you can define your methods once, directly on Obj, and the caller never needs to deal with the lock explicitly, but your methods can handle it exactly as needed. This is a fairly common pattern in problem domains that benefit from it, like UI objects.
If there is some reason why Obj really needs to be usable with or without the lock, then define a trait.

Related

Is accessing an object through the same pointer from multiple threads for atomic operations undefined behavior?

So I am experimenting with some lock-free algorithms and wait-free algorithms in rust. I have found that the AtomicPtr class is quite useful here as it allows operations like cmpexchange or swapping etc on pointer values for implementations of lock-free data structures. However, I read something potentially concerning on Rust's rules about pointer aliasing :
Breaking the pointer aliasing rules. &mut T and &T follow LLVM’s scoped noalias model, except if the &T contains an UnsafeCell.
Mutating immutable data. All data inside a const item is immutable. Moreover, all data reached through a shared reference or data owned by an immutable binding is immutable, unless that data is contained within an UnsafeCell.
More so, the LLVM section they link to also sounds concerning
This indicates that memory locations accessed via pointer values based on the argument or return value are not also accessed, during the execution of the function, via pointer values not based on the argument or return value. This guarantee only holds for memory locations that are modified, by any means, during the execution of the function.
From this, it's not quite clear how atomics could be considered defined behavior, as an atomic operation does mutate the contents of the data a pointer points to. The Rust docs nor the LLVM list atomics as an exception to this.
Thus, I wanted to know if the following code would be considered undefined behavior in Rust:
struct Point {
x:AtomicUsize,
y:AtomicUsize
}
impl Point {
fn new_ptr() -> *mut Point {
Box::into_raw(Box::new(Point{x:AtomicUsize::new(0), y:AtomicUsize::new(0)}))
}
}
fn main() {
let point = Point::new_ptr();
let cont = AtomicPtr::new(point);
let handle = thread::spawn(move || {
let r = unsafe { cont.load(Ordering::SeqCst).as_ref().unwrap() };
for _ in 0..10000 {
r.x.fetch_add(1, Ordering::SeqCst);
}
});
unsafe { point.as_ref().unwrap().x.fetch_add(1, Ordering::SeqCst); }
handle.join().unwrap();
unsafe {
drop(Box::from_raw(point));
}
}
It compiles and executes as expected, along with slight variations. But unsure if what I am doing is undefined or not. I want to ensure this allowed behavior is not restricted or changed in the future.

Is it possible to return a `Keys<'_, K, V>` iterator as an more generic iterator of `&K`?

I would to write a method that returns self.hash_map.keys() while hiding from the caller the concrete type Keys<'_, K, V>.
A downside of the Keys return type is that it exposes to the caller that the K elements are coming from a HashMap. Intuitively, it seems as though it shouldn't be necessary to expose this information.
What is the cheapest way (with respect to CPU/allocations) to return an iterator of key references?
Can it be accomplished by some precise choice of return type? Is some form of type-erasure possible?
Or does it require an invocation in the function body? Is some transformation necessary?
Both of the options you speculated about are possible.
The simplest option is to use the impl type syntax, which is an “existential” type: “I am going to return a value which implements Iterator but I'm not telling you what the concrete type is”. In this case, the compiler knows what the type is (so the compiled code is exactly the same as if it wasn't hidden), but the user of your method cannot rely on anything but the specified trait, so you aren't leaking implementation details.
impl MyType {
fn keys(&self) -> impl Iterator<Item = &MyKeyType> {
self.hash_map.keys()
}
}
(Note that this resembles but is not the same as dyn Iterator; when you use dyn, you're using runtime dispatch and the same function can return different concrete types from different calls to it. With impl, the type is static, just hidden, and there is no overhead.)
The disadvantage of this option is that the type is entirely unnameable; for example, nobody can write a structure that holds your keys() iterator except by making it generic over all Iterators. (This is rarely a problem for iterators in particular, since iterator wrappers are usually generic anyway.)
Also, if your iterator implements any additional traits you want to allow the caller to use, like Debug or ExactSizeIterator, then you need to add them to the impl type or they won't be visible.
Another option is to wrap the iterator in your own struct. This allows you to hide the implementation type while still allowing callers to refer to it by name, so it's the most flexible. The disadvantage of this option is that you have to explicitly implement Iterator (and any other traits) for the wrapper:
impl MyType {
fn keys(&self) -> MyKeyIterator<'_> {
MyKeyIterator(self.hash_map.keys())
}
}
#[derive(Clone, Debug)]
struct MyKeyIterator<'a>(Keys<'a, MyKeyType, MyValueType>);
impl<'a> Iterator for MyKeyIterator<'a> {
type Item = &'a MyKeyType;
fn next(&mut self) -> Option<&'a MyKeyType> {
self.0.next()
}
}
Rust Playground link with supporting code
This wrapper should not add any performance cost (when compiled with optimization), except that by default the wrapper method will not be inlined if called from another crate. If you're writing a library and this method is performance-sensitive, you can either enable link-time optimization (LTO) in the build, or add #[inline] to the next method (which enables cross-crate inlining). Of course, don't do any such tweaking without checking whether it makes a difference to actual performance; otherwise you're just increasing compile time (and instruction cache thrashing).
Can it be accomplished by some precise choice of return type? Is some form of type-erasure possible?
Yes! You can return an impl Trait to indicate that you're returning a type that implements Trait but doesn't expose the concrete type:
fn keys(&self) -> impl Iterator<Item = &K> {
self.hash_map.keys()
}
See it working on the playground.

It is possible to have single-threaded zero-cost memoization without mutability in safe Rust?

I'm interested in finding or implementing a Rust data structure that provides a zero-cost way to memoize a single computation with an arbitrary output type T. Specifically I would like a generic type Cache<T> whose internal data takes up no more space than an Option<T>, with the following basic API:
impl<T> Cache<T> {
/// Return a new Cache with no value stored in it yet.
pub fn new() -> Self {
// ...
}
/// If the cache has a value stored in it, return a reference to the
/// stored value. Otherwise, compute `f()`, store its output
/// in the cache, and then return a reference to the stored value.
pub fn call<F: FnOnce() -> T>(&self, f: F) -> &T {
// ...
}
}
The goal here is to be able to share multiple immutable references to a Cache within a single thread, with any holder of such a reference being able to access the value (triggering the computation if it is the first time). Since we're only requiring to be able to share a Cache within a single thread, it is not necessary for it to be Sync.
Here is a way to implement the API safely (or at least I think it's safe) using unsafe under the hood:
use std::cell::UnsafeCell;
pub struct Cache<T> {
value: UnsafeCell<Option<T>>
}
impl<T> Cache<T> {
pub fn new() -> Self {
Cache { value: UnsafeCell::new(None) }
}
pub fn call<F: FnOnce() -> T>(&self, f: F) -> &T {
let ptr = self.value.get();
unsafe {
if (*ptr).is_none() {
let t = f();
// Since `f` potentially could have invoked `call` on this
// same cache, to be safe we must check again that *ptr
// is still None, before setting the value.
if (*ptr).is_none() {
*ptr = Some(t);
}
}
(*ptr).as_ref().unwrap()
}
}
}
Is it possible to implement such a type in safe Rust (i.e., not writing our own unsafe code, only indirectly relying on unsafe code in the standard library)?
Clearly, the call method requires mutating self, which means that Cache must use some form of interior mutability. However, it does not seem possible to do with a Cell, because Cell provides no way to retrieve a reference to the enclosed value, as is required by the desired API of call above. And this is for a good reason, as it would be unsound for Cell to provide such a reference, because it would have no way to ensure that the referenced value is not mutated over the lifetime of the reference. On the other hand, for the Cache type, after call is called once, the API above does not provide any way for the stored value to be mutated again, so it is safe for it to hand out a reference with a lifetime that can be a long as that of the Cache itself.
If Cell can't work, I'm curious if the Rust standard library might provide some other safe building blocks for interior mutability which could be used to implement this Cache.
Neither RefCell nor Mutex fulfill the goals here:
they are not zero-cost: they involve storing more data than that of an Option<T> and add unnecessary runtime checks.
they do not seem to provide any way to return a real reference with the lifetime that we want -- instead we could only return a Ref or MutexGuard which is not the same thing.
Using only an Option would not provide the same functionality: if we share immutable references to the Cache, any holder of such a reference can invoke call and obtain the desired value (and mutating the Cache in the process so that future calls will retrieve the same value); whereas, sharing immutable references to an Option, it would not be possible to mutate the Option, so it could not work.

How to return a Rust closure to JavaScript via WebAssembly?

The comments on closure.rs are pretty great, however I can't make it work for returning a closure from a WebAssembly library.
I have a function like this:
#[wasm_bindgen]
pub fn start_game(
start_time: f64,
screen_width: f32,
screen_height: f32,
on_render: &js_sys::Function,
on_collision: &js_sys::Function,
) -> ClosureTypeHere {
// ...
}
Inside that function I make a closure, assuming Closure::wrap is one piece of the puzzle, and copying from closure.rs):
let cb = Closure::wrap(Box::new(move |time| time * 4.2) as Box<FnMut(f64) -> f64>);
How do I return this callback from start_game and what should ClosureTypeHere be?
The idea is that start_game will create local mutable objects - like a camera, and the JavaScript side should be able to call the function Rust returns in order to update that camera.
This is a good question, and one that has some nuance too! It's worth calling out the closures example in the wasm-bindgen guide (and the section about passing closures to JavaScript) as well, and it'd be good to contribute back to that as well if necessary!
To get you started, though, you can do something like this:
use wasm_bindgen::{Closure, JsValue};
#[wasm_bindgen]
pub fn start_game(
start_time: f64,
screen_width: f32,
screen_height: f32,
on_render: &js_sys::Function,
on_collision: &js_sys::Function,
) -> JsValue {
let cb = Closure::wrap(Box::new(move |time| {
time * 4.2
}) as Box<FnMut(f64) -> f64>);
// Extract the `JsValue` from this `Closure`, the handle
// on a JS function representing the closure
let ret = cb.as_ref().clone();
// Once `cb` is dropped it'll "neuter" the closure and
// cause invocations to throw a JS exception. Memory
// management here will come later, so just leak it
// for now.
cb.forget();
return ret;
}
Above the return value is just a plain-old JS object (here as a JsValue) and we create that with the Closure type you've seen already. This will allow you to quickly return a closure to JS and you'll be able to call it from JS as well.
You've also asked about storing mutable objects and such, and that can all be done through normal Rust closures, capturing, etc. For example the declaration of FnMut(f64) -> f64 above is the signature of the JS function, and that can be any set of types such as FnMut(String, MyCustomWasmBindgenType, f64) ->
Vec<u8> if you really want. For capturing local objects you can do:
let mut camera = Camera::new();
let mut state = State::new();
let cb = Closure::wrap(Box::new(move |arg1, arg2| { // note the `move`
if arg1 {
camera.update(&arg2);
} else {
state.update(&arg2);
}
}) as Box<_>);
(or something like that)
Here the camera and state variables will be owned by the closure and dropped at the same time. More info about just closures can be found in the Rust book.
It's also worth briefly covering the memory management aspect here. In the
example above we're calling forget() which leaks memory and can be a problem if the Rust function is called many times (as it would leak a lot of memory). The fundamental problem here is that there's memory allocated on the WASM heap which the created JS function object references. This allocated memory in theory needs to be deallocated whenever the JS function object is GC'd, but we have no way of knowing when that happens (until WeakRef exists!).
In the meantime we've chosen an alternate strategy. The associated memory is
deallocated whenever the Closure type itself is dropped, providing
deterministic destruction. This, however, makes it difficult to work with as we need to figure out manually when to drop the Closure. If forget doesn't work for your use case, some ideas for dropping the Closure are:
First, if it's a JS closure only invoked once, then you can use Rc/RefCell
to drop the Closure inside the the closure itself (using some interior
mutability shenanigans). We should also eventually
provide native support
for FnOnce in wasm-bindgen as well!
Next, you can return an auxiliary JS object to Rust which has a manual free
method. For example a #[wasm_bindgen]-annotated wrapper. This wrapper would
then need to be manually freed in JS when appropriate.
If you can get by, forget is by far the easiest thing to do for
now, but this is definitely a pain point! We can't wait for WeakRef to exist :)
As far as I understand from documentation, it isn't supposed to export Rust closures, they only might be passed over as parameters to imported JS functions, but all this happens in Rust code.
https://rustwasm.github.io/wasm-bindgen/reference/passing-rust-closures-to-js.html#passing-rust-closures-to-imported-javascript-functions
I made couple of experiments, and when a Rust function returns the mentioned 'Closure' type, compiler throws exception: the trait wasm_bindgen::convert::IntoWasmAbi is not implemented for wasm_bindgen::prelude::Closure<(dyn std::ops::FnMut() -> u32 + 'static)>
In all examples, closures are wrapped into an arbitrary sctuct, but after that you already can't call this on JS side.

What are the performance implications of consuming self and returning it?

I've been reading questions like Why does a function that accepts a Box<MyType> complain of a value being moved when a function that accepts self works?, Preferable pattern for getting around the "moving out of borrowed self" checker, and How to capture self consuming variable in a struct?, and now I'm curious about the performance characteristics of consuming self but possibly returning it to the caller.
To make a simpler example, imagine I want to make a collection type that's guaranteed to be non-empty. To achieve this, the "remove" operation needs to consume the collection and optionally return itself.
struct NonEmptyCollection { ... }
impl NonEmptyCollection {
fn pop(mut self) -> Option<Self> {
if self.len() == 1 {
None
} else {
// really remove the element here
Some(self)
}
}
}
(I suppose it should return the value it removed from the list too, but it's just an example.) Now let's say I call this function:
let mut c = NonEmptyCollection::new(...);
if let Some(new_c) = c.pop() {
c = new_c
} else {
// never use c again
}
What actually happens to the memory of the object? What if I have some code like:
let mut opt: Option<NonEmptyCollection> = Some(NonEmptyCollection::new(...));
opt = opt.take().pop();
The function's signature can't guarantee that the returned object is actually the same one, so what optimizations are possible? Does something like the C++ return value optimization apply, allowing the returned object to be "constructed" in the same memory it was in before? If I have the choice between an interface like the above, and an interface where the caller has to deal with the lifetime:
enum PopResult {
StillValid,
Dead
};
impl NonEmptyCollection {
fn pop(&mut self) -> PopResult {
// really remove the element
if self.len() == 0 { PopResult::Dead } else { PopResult::StillValid }
}
}
is there ever a reason to choose this dirtier interface for performance reasons? In the answer to the second example I linked, trentcl recommends storing Options in a data structure to allow the caller to do a change in-place instead of doing remove followed by insert every time. Would this dirty interface be a faster alternative?
YMMV
Depending on the optimizer's whim, you may end up with:
close to a no-op,
a few register moves,
a number of bit-copies.
This will depend whether:
the call is inlined, or not,
the caller re-assigns to the original variable or creates a fresh variable (and how well LLVM handles reusing dead space),
the size_of::<Self>().
The only guarantees you get is that no deep-copy will occur, as there is no .clone() call.
For anything else, you need to check the LLVM IR or assembly.

Resources