borrow_mutex 0.1.1

Async Rust Mutex which does not require wrapping the target structure
Documentation
[![crates.io](https://img.shields.io/crates/v/borrow_mutex)][crates.io]
[![libs.rs](https://img.shields.io/badge/libs.rs-borrow_mutex-orange)][libs.rs]
[![documentation](https://img.shields.io/docsrs/borrow_mutex)][documentation]
[![rust-version](https://img.shields.io/static/v1?label=Rust&message=1.65.0)][rust-version]
[![license](https://img.shields.io/crates/l/borrow_mutex)][license]

[crates.io]: https://crates.io/crates/borrow_mutex
[libs.rs]: https://lib.rs/crates/borrow_mutex
[documentation]: https://docs.rs/borrow_mutex
[rust-version]: https://www.rust-lang.org
[license]: https://github.com/darsto/borrow_mutex/blob/master/LICENSE

# BorrowMutex

**Very initial version!** Use with caution

[`BorrowMutex`] is an async Mutex which does not require wrapping the target
structure. Instead, a `&mut T` can be lended to the mutex at any given time.

This lets any other side borrow the `&mut T`. The mutable ref is borrow-able
only while the lender awaits, and the lending side can await until someone
wants to borrow. The semantics enforce at most one side has a mutable reference
at any given time.

This lets us share any mutable object between distinct async contexts
without [`Arc`]<[`Mutex`]> over the object in question and without relying
on any kind of internal mutability. It's mostly aimed at single-threaded
executors where internal mutability is an unnecessary complication.
Still, the [`BorrowMutex`] is Send+Sync and can be safely used from
any number of threads.

The most common use case is having a state handled entirely in its own
async context, but occasionally having to be accessed from the outside -
another async context.

Since the shared data doesn't have to be wrapped inside an [`Arc`],
it doesn't have to be allocated on the heap. In fact, BorrowMutex does not
perform any allocations whatsoever. The
[`tests/borrow_basic.rs`](https://github.com/darsto/borrow_mutex/blob/master/tests/borrow_basic.rs)
presents a simple example where *everything* is stored on the stack.

## Safety

The API is unsound when futures are forgotten ([`core::mem::forget()`]).
For convenience, none of the API is marked unsafe.

See [`BorrowMutex::lend`] for details.

Hopefully the unsound code could be prohibited in future rust versions
with additional compiler annotations.

## Example

```rust
use borrow_mutex::BorrowMutex;
use futures::FutureExt;

struct TestObject {
    counter: usize,
}

let mutex = BorrowMutex::<16, TestObject>::new();

let f1 = async {
    // try to borrow, await, and repeat until we get an Err.
    // The Err can be either:
    // - the mutex has too many concurrent borrowers (in this example we
    //   have just 1, and the max was 16)
    // - the mutex was terminated - i.e. because the lending side knows it
    //   won't lend anymore
    // We eventually expect the latter here
    while let Ok(mut test) = mutex.request_borrow().await {
        test.counter += 1; // mutate the object!
        println!("f1: counter: {}", test.counter);
        drop(test);
        // `test` is dropped, and so the mutex.lend().await on the
        // other side returns and can use the object freely again.
        // we'll request another borrow in 100ms
        smol::Timer::after(std::time::Duration::from_millis(100)).await;
    }
};

let f2 = async {
    let mut test = TestObject { counter: 1 };
    // local object we'll be sharing

    loop {
        if test.counter >= 20 {
            break;
        }
        // either sleep 200ms or lend if needed in the meantime
        futures::select! {
            _ = smol::Timer::after(std::time::Duration::from_millis(200)).fuse() => {
                if test.counter < 10 {
                    test.counter += 1;
                }
                println!("f2: counter: {}", test.counter);
            }
            _ = mutex.wait_to_lend().fuse() => {
                // there's someone waiting to borrow, lend
                mutex.lend(&mut test).unwrap().await
            }
        }
    }

    mutex.terminate().await;
};

futures::executor::block_on(async {
    futures::join!(f1, f2);
});
```

Both futures should print interchangeably. See `tests/borrow_basic.rs` for
a full working example.

# What if Drop is not called?

Unfortunately, **Undefined Behavior**. With [`core::mem::forget()`] or similar
called on [`LendGuard`] we can make the borrow checker believe the lended
`&mut T` is no longer used, while in fact, it is:

```rust,should_panic
# use borrow_mutex::BorrowMutex;
# use futures::Future;
# use futures::task::Context;
# use futures::task::Poll;
# use core::pin::pin;
struct TestStruct {
    counter: usize,
}
let mutex = BorrowMutex::<16, TestStruct>::new();
let mut test = TestStruct { counter: 1 };

let mut test_borrow = pin!(mutex.request_borrow());
let _ = test_borrow
    .as_mut()
    .poll(&mut Context::from_waker(&futures::task::noop_waker()));

let mut t1 = Box::pin(async {
    mutex.lend(&mut test).unwrap().await;
});

let _ = t1
    .as_mut()
    .poll(&mut Context::from_waker(&futures::task::noop_waker()));
std::mem::forget(t1);
// the compiler thinks `test` is no longer borrowed, but in fact it is

let Poll::Ready(Ok(mut test_borrow)) = test_borrow
    .as_mut()
    .poll(&mut Context::from_waker(&futures::task::noop_waker()))
else {
    panic!();
};

// now we get two mutable references, this is strictly UB
test_borrow.counter = 2;
test.counter = 6;
assert_eq!(test_borrow.counter, 2); // this fails
```

Similar Rust libraries make their API unsafe exactly because of this reason -
it's the caller's responsibility to not call [`core::mem::forget()`] or similar
([async-scoped](https://docs.rs/async-scoped/0.9.0/async_scoped/struct.Scope.html#method.scope))

However, this Undefined Behavior is really difficult to trigger in regular
code. It's hardly useful to call [`core::mem::forget()`] on a future.

[`Arc`]: std::sync::Arc
[`Mutex`]: std::sync::Mutex