diff --git a/src/calls.rs b/src/calls.rs index 2c0402fbc0..c3861e5efe 100644 --- a/src/calls.rs +++ b/src/calls.rs @@ -6,7 +6,7 @@ use crate::chat::ChatIdBlocked; use crate::chat::{Chat, ChatId, send_msg}; use crate::constants::{Blocked, Chattype}; use crate::contact::ContactId; -use crate::context::Context; +use crate::context::{Context, WeakContext}; use crate::events::EventType; use crate::headerdef::HeaderDef; use crate::log::warn; @@ -199,8 +199,9 @@ impl Context { call.id = send_msg(self, chat_id, &mut call).await?; let wait = RINGING_SECONDS; + let context = self.get_weak_context(); task::spawn(Context::emit_end_call_if_unaccepted( - self.clone(), + context, wait.try_into()?, call.id, )); @@ -291,11 +292,12 @@ impl Context { } async fn emit_end_call_if_unaccepted( - context: Context, + context: WeakContext, wait: u64, call_id: MsgId, ) -> Result<()> { sleep(Duration::from_secs(wait)).await; + let context = context.upgrade()?; let Some(mut call) = context.load_call_by_id(call_id).await? else { warn!( context, @@ -368,8 +370,9 @@ impl Context { } } let wait = call.remaining_ring_seconds(); + let context = self.get_weak_context(); task::spawn(Context::emit_end_call_if_unaccepted( - self.clone(), + context, wait.try_into()?, call.msg.id, )); diff --git a/src/context.rs b/src/context.rs index d149c7446b..c2e9ede7ea 100644 --- a/src/context.rs +++ b/src/context.rs @@ -5,7 +5,7 @@ use std::ffi::OsString; use std::ops::Deref; use std::path::{Path, PathBuf}; use std::sync::atomic::AtomicBool; -use std::sync::{Arc, OnceLock}; +use std::sync::{Arc, OnceLock, Weak}; use std::time::Duration; use anyhow::{Context as _, Result, bail, ensure}; @@ -201,6 +201,25 @@ impl Deref for Context { } } +/// A weak reference to a [`Context`] +/// +/// Can be used to obtain a [`Context`]. An existing weak reference does not prevent the corresponding [`Context`] from being dropped. +#[derive(Clone, Debug)] +pub(crate) struct WeakContext { + inner: Weak, +} + +impl WeakContext { + /// Returns the [`Context`] if it is still available. + pub(crate) fn upgrade(&self) -> Result { + let inner = self + .inner + .upgrade() + .ok_or_else(|| anyhow::anyhow!("Inner struct has been dropped"))?; + Ok(Context { inner }) + } +} + /// Actual context, expensive to clone. #[derive(Debug)] pub struct InnerContext { @@ -385,6 +404,13 @@ impl Context { Ok(context) } + /// Returns a weak reference to this [`Context`]. + pub(crate) fn get_weak_context(&self) -> WeakContext { + WeakContext { + inner: Arc::downgrade(&self.inner), + } + } + /// Opens the database with the given passphrase. /// NB: Db encryption is deprecated, so `passphrase` should be empty normally. See /// [`ContextBuilder::with_password()`] for reasoning.