1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
use std::ops::Deref;
use std::ops::DerefMut;
use std::sync::Arc;
#[cfg(debug_assertions)]
use std::time::Duration;
#[cfg(debug_assertions)]
use std::time::Instant;
/// You can use `Extensions` to pass data between plugins that is not serializable. Such data is not accessible from Rhai or co-processoers.
///
/// This can be accessed at any point in the request lifecycle and is useful for passing data between services.
/// Extensions are thread safe, and must be locked for mutation.
///
/// For example:
/// `context.extensions().lock().insert::<MyData>(data);`
#[derive(Default, Clone, Debug)]
pub struct ExtensionsMutex {
extensions: Arc<parking_lot::Mutex<super::Extensions>>,
}
impl ExtensionsMutex {
/// Locks the extensions for mutation.
///
/// It is CRITICAL to avoid holding on to the mutex guard for too long, particularly across async calls.
/// Doing so may cause performance degradation or even deadlocks.
///
/// See related clippy lint for examples: <https://rust-lang.github.io/rust-clippy/master/index.html#/await_holding_lock>
pub fn lock(&self) -> ExtensionsGuard {
ExtensionsGuard {
#[cfg(debug_assertions)]
start: Instant::now(),
guard: self.extensions.lock(),
}
}
}
pub struct ExtensionsGuard<'a> {
#[cfg(debug_assertions)]
start: Instant,
guard: parking_lot::MutexGuard<'a, super::Extensions>,
}
impl<'a> Deref for ExtensionsGuard<'a> {
type Target = super::Extensions;
fn deref(&self) -> &super::Extensions {
&self.guard
}
}
impl DerefMut for ExtensionsGuard<'_> {
fn deref_mut(&mut self) -> &mut super::Extensions {
&mut self.guard
}
}
#[cfg(debug_assertions)]
impl Drop for ExtensionsGuard<'_> {
fn drop(&mut self) {
// In debug builds we check that extensions is never held for too long.
// We only check if the current runtime is multi-threaded, because a bunch of unit tests fail the assertion and these need to be investigated separately.
if let Ok(runtime) = tokio::runtime::Handle::try_current() {
if runtime.runtime_flavor() == tokio::runtime::RuntimeFlavor::MultiThread {
let elapsed = self.start.elapsed();
if elapsed > Duration::from_millis(10) {
panic!("ExtensionsGuard held for {}ms. This is probably a bug that will stall the Router and cause performance problems. Run with `RUST_BACKTRACE=1` environment variable to display a backtrace", elapsed.as_millis());
}
}
}
}
}