Това изисква скок в дълбокото, защото ще трябва да се имплементира един Future на ръка. Но от друга страна е добро упражнение, ако човек иска да добие интуиция как работят future-ите в езика.
За по-лесно, ще искаме въпросния мутекс да не е thread safe, т.е. да може да се използва само от single threaded runtime.
use std::cell::RefCell;
use std::future::Future;
use std::ops::{Deref, DerefMut};
use std::pin::Pin;
use std::task::{Context, Poll};
/// Неблокиращ мутекс, предназначен да се използва от асинхронен код.
pub struct MyMutex<T> {
value: RefCell<T>,
/* todo: other fields */
}
impl<T> MyMutex<T> {
pub fn new(value: T) -> Self {
todo!()
}
// Забележете, че `lock` не е маркирана като `async fn`, защото си имплементираме future-а
// на ръка (тук компилатора няма как да ни помогне).
//
// Бихме могли да я декларираме `fn lock() -> impl Future<Output = MyMytexGuard<'_, T>>`,
// ако искаме да не правим структурата публична, но пак ще трябва да си напишем и върнем
// наша структура.
pub fn lock(&self) -> MyMutexLockFuture<'_, T> {
todo!()
}
fn unlock(&self) {
todo!()
}
}
pub struct MyMutexLockFuture<'a, T> {
/* todo: fields */
}
impl<'a, T> Future for MyMutexLockFuture<'a, T> {
type Output = MyMutexGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
todo!()
}
}
pub struct MyMutexGuard<'a, T> {
/* todo: fields */
}
impl<'a, T> Deref for MyMutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
todo!()
}
}
impl<'a, T> DerefMut for MyMutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
todo!()
}
}
impl<'a, T> Drop for MyMutexGuard<'a, T> {
fn drop(&mut self) {
// hint: извикайте MyMytex::unlock
todo!()
}
}
Зашо използваме RefCell за value? Защото ни дава всичката необходима функционалност. За да имплементираме мутекса е нужно да можем от &MyMutex<T> да вземем &mut T, за което е нужен някакъв вид internal mutability.
Бихме могли да използваме std::sync::Mutex, но няма да го използваме пълноценно. Или дори UnsafeCell, но това изисква unsafe код и просто ще преимплементираме RefCell (но би имало смисъл, ако правим thread safe вариант).
Изискването за мутекса е когато две задачи (task-а) се опитат да го заключат едновременно, т.е. да извикат my_mutex.lock().await, едната задача ще получи MyMutexGuard и ще продължи изпълнението си, докато другата ще бъде "блокирана" и ще трябва да изчака, докато мутекса не се освободи.
За да се получи това, при poll-ването на future-а, върнат от my_mutex.lock(), във втората задача трябва да се върне Poll::Pending, което означава, че задачата за момента не може да продължи работата си. Съответно async runtime-а повече няма да schedule-ва тази задача, но е свободен да изпълнява други задачи. Когато обаче мутекса се освободи, runtime-а трябва да бъде уведомен, че втората задача вече може да направи прогрес. За целта предварително трябва да се вземе Waker обекта за съответната задача, който може да бъде получен от Context параметъра на poll, и да се запази до момента, в който задачата трябва да бъде събудена.
Updating crates.io index
Locking 18 packages to latest compatible versions
Compiling proc-macro2 v1.0.93
Compiling unicode-ident v1.0.14
Compiling autocfg v1.4.0
Compiling slab v0.4.9
Compiling futures-core v0.3.31
Compiling quote v1.0.38
Compiling syn v2.0.96
Compiling futures-sink v0.3.31
Compiling futures-channel v0.3.31
Compiling pin-project-lite v0.2.16
Compiling pin-utils v0.1.0
Compiling futures-task v0.3.31
Compiling futures-io v0.3.31
Compiling memchr v2.7.4
Compiling solution v0.1.0 (/tmp/d20250120-1137348-55tcli/solution)
warning: field `value` is never read
--> src/lib.rs:7:5
|
6 | pub struct MyMutex<T> {
| ------- field in this struct
7 | value: RefCell<T>,
| ^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: fields `mutex` and `was_pooled` are never read
--> src/lib.rs:39:5
|
38 | pub struct MyMutexLockFuture<'a, T> {
| ----------------- fields in this struct
39 | mutex: &'a MyMutex<T>,
| ^^^^^
40 | was_pooled: bool,
| ^^^^^^^^^^
warning: `solution` (lib) generated 2 warnings
Compiling futures-macro v0.3.31
Compiling futures-util v0.3.31
Compiling futures-executor v0.3.31
Compiling futures v0.3.31
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:28:48
|
28 | let mut lock = resource.lock().await;
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:37:48
|
37 | let mut lock = resource.lock().await;
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:46:48
|
46 | let mut lock = resource.lock().await;
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:58:38
|
58 | assert_eq!(&*resource.lock().await, &["one", "two", "three"]);
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:71:48
|
71 | let mut lock = resource.lock().await;
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:80:48
|
80 | let mut lock = resource.lock().await;
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:89:48
|
89 | let mut lock = resource.lock().await;
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
error[E0277]: `MyMutexLockFuture<'_, Vec<_>>` is not a future
--> tests/solution_test.rs:103:50
|
103 | let mut final_resource = resource.lock().await.clone();
| -^^^^^
| ||
| |`MyMutexLockFuture<'_, Vec<_>>` is not a future
| help: remove the `.await`
|
= help: the trait `futures::Future` is not implemented for `MyMutexLockFuture<'_, Vec<_>>`
= note: MyMutexLockFuture<'_, Vec<_>> must be a future or must implement `IntoFuture` to be awaited
= note: required for `MyMutexLockFuture<'_, Vec<_>>` to implement `std::future::IntoFuture`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `solution` (test "solution_test") due to 8 previous errors