pinnable/
lib.rs

1// reasonable clippy categories
2#![warn(clippy::pedantic, clippy::nursery, clippy::cargo)]
3// reasonable clippy::restriction lints
4#![warn(
5    clippy::as_conversions,
6    clippy::clone_on_ref_ptr,
7    clippy::create_dir,
8    clippy::dbg_macro,
9    clippy::decimal_literal_representation,
10    clippy::default_numeric_fallback,
11    clippy::else_if_without_else,
12    clippy::exhaustive_enums,
13    clippy::exhaustive_structs,
14    clippy::exit,
15    clippy::expect_used,
16    clippy::filetype_is_file,
17    clippy::float_arithmetic,
18    clippy::float_cmp_const,
19    clippy::get_unwrap,
20    clippy::if_then_some_else_none,
21    clippy::indexing_slicing,
22    clippy::integer_arithmetic,
23    clippy::integer_division,
24    clippy::let_underscore_must_use,
25    clippy::lossy_float_literal,
26    clippy::map_err_ignore,
27    clippy::mem_forget,
28    clippy::missing_docs_in_private_items,
29    clippy::modulo_arithmetic,
30    clippy::multiple_inherent_impl,
31    clippy::panic,
32    clippy::panic_in_result_fn,
33    clippy::pattern_type_mismatch,
34    clippy::print_stderr,
35    clippy::print_stdout,
36    clippy::rc_buffer,
37    clippy::rest_pat_in_fully_bound_structs,
38    clippy::str_to_string,
39    clippy::string_add,
40    clippy::string_to_string,
41    clippy::todo,
42    clippy::unimplemented,
43    clippy::unnecessary_self_imports,
44    clippy::unneeded_field_pattern,
45    clippy::unreachable,
46    clippy::unwrap_in_result,
47    clippy::unwrap_used,
48    clippy::use_debug,
49    clippy::verbose_file_reads,
50    clippy::wildcard_enum_match_arm
51)]
52// reasonable rustc lints
53#![warn(
54    elided_lifetimes_in_paths,
55    explicit_outlives_requirements,
56    macro_use_extern_crate,
57    meta_variable_misuse,
58    missing_abi,
59    missing_copy_implementations,
60    missing_debug_implementations,
61    missing_docs,
62    non_ascii_idents,
63    noop_method_call,
64    semicolon_in_expressions_from_macros,
65    single_use_lifetimes,
66    trivial_casts,
67    trivial_numeric_casts,
68    unreachable_pub,
69    unsafe_op_in_unsafe_fn,
70    unused_crate_dependencies,
71    unused_extern_crates,
72    unused_import_braces,
73    unused_lifetimes,
74    unused_qualifications,
75    unused_results,
76    variant_size_differences
77)]
78// reasonable rustdoc lints
79#![warn(
80    rustdoc::missing_crate_level_docs,
81    rustdoc::missing_doc_code_examples,
82    rustdoc::private_doc_tests,
83    rustdoc::invalid_html_tags
84)]
85
86//! [![crates.io]](https://crates.io/crates/pinnable)
87//! [![github]](https://github.com/steffahn/pinnable)
88//! [![MIT / Apache 2.0 licensed]](https://github.com/steffahn/pinnable#License)
89//!
90//! A wrapper for [`Mutex`](std::sync::Mutex "std::sync::Mutex")
91//! that supports obtaining `Pin<&mut T>` references to the contained value.
92//! It’s a trade-off though, because it can no longer provide mutable access _without_ being pinned.
93//!
94//! [github]: https://img.shields.io/badge/github-steffahn/pinnable-yellowgreen.svg
95//! [crates.io]: https://img.shields.io/crates/v/pinnable.svg
96//! [MIT / Apache 2.0 licensed]: https://img.shields.io/crates/l/pinnable.svg
97//! [docs.rs]: https://docs.rs/pinnable/badge.svg
98
99use std::{fmt, ops, pin::Pin, sync};
100
101/// Documentation still incomplete. API similar to [`std::sync::Mutex`].
102/// # Examples
103/// ```
104/// use std::future::Future;
105/// use std::pin::Pin;
106/// use std::sync::Arc;
107/// use std::task::{Context, Poll};
108/// 
109/// fn poll_shared_future<F: Future>(
110///     fut: &Pin<Arc<pinnable::Mutex<F>>>,
111///     ctx: &mut Context<'_>,
112/// ) -> Poll<F::Output> {
113///     fut.as_ref().lock().unwrap().as_mut().poll(ctx)
114/// }
115/// ```
116// FIXME: add docs
117#[allow(missing_docs, clippy::missing_docs_in_private_items)]
118pub struct Mutex<T: ?Sized>(sync::Mutex<T>);
119
120impl<T: ?Sized> fmt::Debug for Mutex<T>
121where
122    T: fmt::Debug,
123{
124    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
125        fmt::Debug::fmt(&self.0, f)
126    }
127}
128
129// FIXME: add docs
130#[allow(
131    missing_docs,
132    clippy::missing_docs_in_private_items,
133    clippy::missing_errors_doc
134)]
135impl<T> Mutex<T> {
136    pub fn new(t: T) -> Self {
137        Self(sync::Mutex::new(t))
138    }
139
140    pub fn into_inner(self) -> sync::LockResult<T> {
141        self.0.into_inner()
142    }
143}
144
145// FIXME: add docs
146#[allow(clippy::missing_docs_in_private_items)]
147fn wrap_result<S, T>(f: impl FnOnce(S) -> T, r: sync::LockResult<S>) -> sync::LockResult<T> {
148    match r {
149        Ok(x) => Ok(f(x)),
150        Err(e) => Err(sync::PoisonError::new(f(e.into_inner()))),
151    }
152}
153
154// FIXME: add docs
155#[allow(clippy::missing_docs_in_private_items)]
156fn wrap_result_try<S, T>(
157    f: impl FnOnce(S) -> T,
158    r: sync::TryLockResult<S>,
159) -> sync::TryLockResult<T> {
160    use sync::TryLockError::{Poisoned, WouldBlock};
161    match r {
162        Ok(x) => Ok(f(x)),
163        Err(Poisoned(e)) => Err(Poisoned(sync::PoisonError::new(f(e.into_inner())))),
164        Err(WouldBlock) => Err(WouldBlock),
165    }
166}
167
168// FIXME: add docs
169#[allow(
170    missing_docs,
171    clippy::missing_docs_in_private_items,
172    clippy::missing_errors_doc
173)]
174impl<T: ?Sized> Mutex<T> {
175    pub fn lock(self: Pin<&Self>) -> sync::LockResult<PinMutexGuard<'_, T>> {
176        wrap_result(
177            |x| unsafe { Pin::new_unchecked(x) },
178            self.get_ref().0.lock(),
179        )
180    }
181
182    pub fn lock_no_pin(&self) -> sync::LockResult<NoPinMutexGuard<'_, T>> {
183        wrap_result(NoPinMutexGuard, self.0.lock())
184    }
185
186    pub fn try_lock(self: Pin<&Self>) -> sync::TryLockResult<PinMutexGuard<'_, T>> {
187        wrap_result_try(
188            |x| unsafe { Pin::new_unchecked(x) },
189            self.get_ref().0.try_lock(),
190        )
191    }
192
193    pub fn try_lock_no_pin(&self) -> sync::TryLockResult<NoPinMutexGuard<'_, T>> {
194        wrap_result_try(NoPinMutexGuard, self.0.try_lock())
195    }
196
197    pub fn is_poisoned(&self) -> bool {
198        self.0.is_poisoned()
199    }
200
201    pub fn get_mut(self: Pin<&mut Self>) -> sync::LockResult<Pin<&mut T>> {
202        wrap_result(
203            |x| unsafe { Pin::new_unchecked(x) },
204            unsafe { Pin::into_inner_unchecked(self) }.0.get_mut(),
205        )
206    }
207
208    pub fn get_mut_no_pin(&mut self) -> sync::LockResult<&mut T> {
209        self.0.get_mut()
210    }
211}
212
213// FIXME: add docs
214#[allow(missing_docs, clippy::missing_docs_in_private_items)]
215pub type PinMutexGuard<'a, T> = Pin<sync::MutexGuard<'a, T>>;
216
217// FIXME: add docs
218#[allow(missing_docs, clippy::missing_docs_in_private_items)]
219pub struct NoPinMutexGuard<'a, T: ?Sized>(sync::MutexGuard<'a, T>);
220
221impl<T: ?Sized> fmt::Debug for NoPinMutexGuard<'_, T>
222where
223    T: fmt::Debug,
224{
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        fmt::Debug::fmt(&**self, f)
227    }
228}
229
230impl<T: ?Sized> fmt::Display for NoPinMutexGuard<'_, T>
231where
232    T: fmt::Display,
233{
234    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
235        fmt::Display::fmt(&**self, f)
236    }
237}
238
239impl<T: ?Sized> ops::Deref for NoPinMutexGuard<'_, T> {
240    type Target = T;
241
242    fn deref(&self) -> &T {
243        &self.0
244    }
245}
246
247impl<T: ?Sized> ops::DerefMut for NoPinMutexGuard<'_, T>
248where
249    T: Unpin,
250{
251    fn deref_mut(&mut self) -> &mut T {
252        &mut self.0
253    }
254}