mirror of
https://github.com/chatmail/core.git
synced 2026-05-17 05:46:30 +03:00
Replace location jobs with async location loop
Locations are now sent in the background regardless of whether SMTP loop is interrupted or not.
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
|
|
||||||
|
### Changes
|
||||||
|
- Send locations in the background regardless of SMTP loop activity #3247
|
||||||
|
|
||||||
### Fixes
|
### Fixes
|
||||||
|
|
||||||
- simplify `dc_stop_io()` and remove potential panics and race conditions #3273
|
- simplify `dc_stop_io()` and remove potential panics and race conditions #3273
|
||||||
|
|||||||
11
src/job.rs
11
src/job.rs
@@ -14,7 +14,6 @@ use crate::context::Context;
|
|||||||
use crate::dc_tools::time;
|
use crate::dc_tools::time;
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::imap::Imap;
|
use crate::imap::Imap;
|
||||||
use crate::location;
|
|
||||||
use crate::message::{Message, MsgId};
|
use crate::message::{Message, MsgId};
|
||||||
use crate::mimefactory::MimeFactory;
|
use crate::mimefactory::MimeFactory;
|
||||||
use crate::param::{Param, Params};
|
use crate::param::{Param, Params};
|
||||||
@@ -90,8 +89,6 @@ pub enum Action {
|
|||||||
ResyncFolders = 300,
|
ResyncFolders = 300,
|
||||||
|
|
||||||
// Jobs in the SMTP-thread, range from DC_SMTP_THREAD..DC_SMTP_THREAD+999
|
// Jobs in the SMTP-thread, range from DC_SMTP_THREAD..DC_SMTP_THREAD+999
|
||||||
MaybeSendLocations = 5005, // low priority ...
|
|
||||||
MaybeSendLocationsEnded = 5007,
|
|
||||||
SendMdn = 5010,
|
SendMdn = 5010,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,8 +102,6 @@ impl From<Action> for Thread {
|
|||||||
UpdateRecentQuota => Thread::Imap,
|
UpdateRecentQuota => Thread::Imap,
|
||||||
DownloadMsg => Thread::Imap,
|
DownloadMsg => Thread::Imap,
|
||||||
|
|
||||||
MaybeSendLocations => Thread::Smtp,
|
|
||||||
MaybeSendLocationsEnded => Thread::Smtp,
|
|
||||||
SendMdn => Thread::Smtp,
|
SendMdn => Thread::Smtp,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -571,10 +566,6 @@ async fn perform_job_action(
|
|||||||
|
|
||||||
let try_res = match job.action {
|
let try_res = match job.action {
|
||||||
Action::SendMdn => job.send_mdn(context, connection.smtp()).await,
|
Action::SendMdn => job.send_mdn(context, connection.smtp()).await,
|
||||||
Action::MaybeSendLocations => location::job_maybe_send_locations(context, job).await,
|
|
||||||
Action::MaybeSendLocationsEnded => {
|
|
||||||
location::job_maybe_send_locations_ended(context, job).await
|
|
||||||
}
|
|
||||||
Action::ResyncFolders => job.resync_folders(context, connection.inbox()).await,
|
Action::ResyncFolders => job.resync_folders(context, connection.inbox()).await,
|
||||||
Action::FetchExistingMsgs => job.fetch_existing_msgs(context, connection.inbox()).await,
|
Action::FetchExistingMsgs => job.fetch_existing_msgs(context, connection.inbox()).await,
|
||||||
Action::UpdateRecentQuota => match context.update_recent_quota(connection.inbox()).await {
|
Action::UpdateRecentQuota => match context.update_recent_quota(connection.inbox()).await {
|
||||||
@@ -647,7 +638,7 @@ pub async fn add(context: &Context, job: Job) -> Result<()> {
|
|||||||
info!(context, "interrupt: imap");
|
info!(context, "interrupt: imap");
|
||||||
context.interrupt_inbox(InterruptInfo::new(false)).await;
|
context.interrupt_inbox(InterruptInfo::new(false)).await;
|
||||||
}
|
}
|
||||||
Action::MaybeSendLocations | Action::MaybeSendLocationsEnded | Action::SendMdn => {
|
Action::SendMdn => {
|
||||||
info!(context, "interrupt: smtp");
|
info!(context, "interrupt: smtp");
|
||||||
context.interrupt_smtp(InterruptInfo::new(false)).await;
|
context.interrupt_smtp(InterruptInfo::new(false)).await;
|
||||||
}
|
}
|
||||||
|
|||||||
249
src/location.rs
249
src/location.rs
@@ -1,19 +1,20 @@
|
|||||||
//! Location handling.
|
//! Location handling.
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
|
|
||||||
use anyhow::{ensure, Result};
|
use anyhow::{ensure, Context as _, Result};
|
||||||
|
use async_std::channel::Receiver;
|
||||||
|
use async_std::future::timeout;
|
||||||
use bitflags::bitflags;
|
use bitflags::bitflags;
|
||||||
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
use quick_xml::events::{BytesEnd, BytesStart, BytesText};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use crate::chat::{self, ChatId};
|
use crate::chat::{self, ChatId};
|
||||||
use crate::contact::ContactId;
|
use crate::contact::ContactId;
|
||||||
use crate::context::Context;
|
use crate::context::Context;
|
||||||
use crate::dc_tools::time;
|
use crate::dc_tools::{duration_to_str, time};
|
||||||
use crate::events::EventType;
|
use crate::events::EventType;
|
||||||
use crate::job::{self, Job};
|
|
||||||
use crate::message::{Message, MsgId, Viewtype};
|
use crate::message::{Message, MsgId, Viewtype};
|
||||||
use crate::mimeparser::SystemMessage;
|
use crate::mimeparser::SystemMessage;
|
||||||
use crate::param::Params;
|
|
||||||
use crate::stock_str;
|
use crate::stock_str;
|
||||||
|
|
||||||
/// Location record
|
/// Location record
|
||||||
@@ -226,32 +227,11 @@ pub async fn send_locations_to_chat(
|
|||||||
}
|
}
|
||||||
context.emit_event(EventType::ChatModified(chat_id));
|
context.emit_event(EventType::ChatModified(chat_id));
|
||||||
if 0 != seconds {
|
if 0 != seconds {
|
||||||
schedule_maybe_send_locations(context, false).await?;
|
context.interrupt_location().await;
|
||||||
job::add(
|
|
||||||
context,
|
|
||||||
job::Job::new(
|
|
||||||
job::Action::MaybeSendLocationsEnded,
|
|
||||||
chat_id.to_u32(),
|
|
||||||
Params::new(),
|
|
||||||
seconds + 1,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn schedule_maybe_send_locations(context: &Context, force_schedule: bool) -> Result<()> {
|
|
||||||
if force_schedule || !job::action_exists(context, job::Action::MaybeSendLocations).await? {
|
|
||||||
job::add(
|
|
||||||
context,
|
|
||||||
job::Job::new(job::Action::MaybeSendLocations, 0, Params::new(), 60),
|
|
||||||
)
|
|
||||||
.await?;
|
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns whether `chat_id` or any chat is sending locations.
|
/// Returns whether `chat_id` or any chat is sending locations.
|
||||||
///
|
///
|
||||||
/// If `chat_id` is `Some` only that chat is checked, otherwise returns `true` if any chat
|
/// If `chat_id` is `Some` only that chat is checked, otherwise returns `true` if any chat
|
||||||
@@ -318,13 +298,13 @@ pub async fn set(context: &Context, latitude: f64, longitude: f64, accuracy: f64
|
|||||||
).await {
|
).await {
|
||||||
warn!(context, "failed to store location {:?}", err);
|
warn!(context, "failed to store location {:?}", err);
|
||||||
} else {
|
} else {
|
||||||
|
info!(context, "stored location for chat {}", chat_id);
|
||||||
continue_streaming = true;
|
continue_streaming = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if continue_streaming {
|
if continue_streaming {
|
||||||
context.emit_event(EventType::LocationChanged(Some(ContactId::SELF)));
|
context.emit_event(EventType::LocationChanged(Some(ContactId::SELF)));
|
||||||
};
|
};
|
||||||
schedule_maybe_send_locations(context, false).await.ok();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue_streaming
|
continue_streaming
|
||||||
@@ -607,147 +587,140 @@ pub(crate) async fn save(
|
|||||||
Ok(newest_location_id)
|
Ok(newest_location_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn job_maybe_send_locations(context: &Context, _job: &Job) -> job::Status {
|
pub(crate) async fn location_loop(context: &Context, interrupt_receiver: Receiver<()>) {
|
||||||
let now = time();
|
loop {
|
||||||
let mut continue_streaming = false;
|
let next_event = match maybe_send_locations(context).await {
|
||||||
info!(
|
Err(err) => {
|
||||||
context,
|
warn!(context, "maybe_send_locations failed: {}", err);
|
||||||
" ----------------- MAYBE_SEND_LOCATIONS -------------- ",
|
Some(60) // Retry one minute later.
|
||||||
);
|
}
|
||||||
|
Ok(next_event) => next_event,
|
||||||
|
};
|
||||||
|
|
||||||
|
let duration = if let Some(next_event) = next_event {
|
||||||
|
Duration::from_secs(next_event)
|
||||||
|
} else {
|
||||||
|
Duration::from_secs(86400)
|
||||||
|
};
|
||||||
|
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"Location loop is waiting for {} or interrupt",
|
||||||
|
duration_to_str(duration)
|
||||||
|
);
|
||||||
|
timeout(duration, interrupt_receiver.recv()).await.ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns number of seconds until the next time location streaming for some chat ends
|
||||||
|
/// automatically.
|
||||||
|
async fn maybe_send_locations(context: &Context) -> Result<Option<u64>> {
|
||||||
|
let mut next_event: Option<u64> = None;
|
||||||
|
|
||||||
|
let now = time();
|
||||||
let rows = context
|
let rows = context
|
||||||
.sql
|
.sql
|
||||||
.query_map(
|
.query_map(
|
||||||
"SELECT id, locations_send_begin, locations_last_sent \
|
"SELECT id, locations_send_begin, locations_send_until, locations_last_sent
|
||||||
FROM chats \
|
FROM chats
|
||||||
WHERE locations_send_until>?;",
|
WHERE locations_send_until>0",
|
||||||
paramsv![now],
|
[],
|
||||||
|row| {
|
|row| {
|
||||||
let chat_id: ChatId = row.get(0)?;
|
let chat_id: ChatId = row.get(0)?;
|
||||||
let locations_send_begin: i64 = row.get(1)?;
|
let locations_send_begin: i64 = row.get(1)?;
|
||||||
let locations_last_sent: i64 = row.get(2)?;
|
let locations_send_until: i64 = row.get(2)?;
|
||||||
continue_streaming = true;
|
let locations_last_sent: i64 = row.get(3)?;
|
||||||
|
Ok((
|
||||||
// be a bit tolerant as the timer may not align exactly with time(NULL)
|
chat_id,
|
||||||
if now - locations_last_sent < (60 - 3) {
|
locations_send_begin,
|
||||||
Ok(None)
|
locations_send_until,
|
||||||
} else {
|
locations_last_sent,
|
||||||
Ok(Some((chat_id, locations_send_begin, locations_last_sent)))
|
))
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|rows| {
|
|rows| {
|
||||||
rows.filter_map(|v| v.transpose())
|
rows.collect::<std::result::Result<Vec<_>, _>>()
|
||||||
.collect::<std::result::Result<Vec<_>, _>>()
|
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.await;
|
.await
|
||||||
|
.context("failed to query location streaming chats")?;
|
||||||
|
|
||||||
if let Ok(rows) = rows {
|
for (chat_id, locations_send_begin, locations_send_until, locations_last_sent) in rows {
|
||||||
let mut msgs = Vec::new();
|
if locations_send_begin > 0 && locations_send_until > now {
|
||||||
|
let can_send = now > locations_last_sent + 60;
|
||||||
{
|
let has_locations = context
|
||||||
let conn = job_try!(context.sql.get_conn().await);
|
.sql
|
||||||
|
.exists(
|
||||||
let mut stmt_locations = job_try!(conn.prepare_cached(
|
"SELECT COUNT(id) \
|
||||||
"SELECT id \
|
|
||||||
FROM locations \
|
FROM locations \
|
||||||
WHERE from_id=? \
|
WHERE from_id=? \
|
||||||
AND timestamp>=? \
|
AND timestamp>=? \
|
||||||
AND timestamp>? \
|
AND timestamp>? \
|
||||||
AND independent=0 \
|
AND independent=0",
|
||||||
ORDER BY timestamp;",
|
paramsv![ContactId::SELF, locations_send_begin, locations_last_sent,],
|
||||||
));
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
for (chat_id, locations_send_begin, locations_last_sent) in &rows {
|
next_event = next_event
|
||||||
if !stmt_locations
|
.into_iter()
|
||||||
.exists(paramsv![
|
.chain(u64::try_from(locations_send_until - now).into_iter())
|
||||||
ContactId::SELF,
|
.min();
|
||||||
*locations_send_begin,
|
|
||||||
*locations_last_sent,
|
if has_locations {
|
||||||
])
|
if can_send {
|
||||||
.unwrap_or_default()
|
// Send location-only message.
|
||||||
{
|
// Pending locations are attached automatically to every message,
|
||||||
// if there is no new location, there's nothing to send.
|
|
||||||
// however, maybe we want to bypass this test eg. 15 minutes
|
|
||||||
} else {
|
|
||||||
// pending locations are attached automatically to every message,
|
|
||||||
// so also to this empty text message.
|
// so also to this empty text message.
|
||||||
// DC_CMD_LOCATION is only needed to create a nicer subject.
|
info!(
|
||||||
//
|
context,
|
||||||
// for optimisation and to avoid flooding the sending queue,
|
"Chat {} has pending locations, sending them.", chat_id
|
||||||
// we could sending these messages only if we're really online.
|
);
|
||||||
// the easiest way to determine this, is to check for an empty message queue.
|
|
||||||
// (might not be 100%, however, as positions are sent combined later
|
|
||||||
// and dc_set_location() is typically called periodically, this is ok)
|
|
||||||
let mut msg = Message::new(Viewtype::Text);
|
let mut msg = Message::new(Viewtype::Text);
|
||||||
msg.hidden = true;
|
msg.hidden = true;
|
||||||
msg.param.set_cmd(SystemMessage::LocationOnly);
|
msg.param.set_cmd(SystemMessage::LocationOnly);
|
||||||
msgs.push((*chat_id, msg));
|
chat::send_msg(context, chat_id, &mut msg).await?;
|
||||||
|
} else {
|
||||||
|
// Wait until pending locations can be sent.
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"Chat {} has pending locations, but they can't be sent yet.", chat_id
|
||||||
|
);
|
||||||
|
next_event = next_event
|
||||||
|
.into_iter()
|
||||||
|
.chain(u64::try_from(locations_last_sent + 61 - now).into_iter())
|
||||||
|
.min();
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
info!(
|
||||||
|
context,
|
||||||
|
"Chat {} has location streaming enabled, but no pending locations.", chat_id
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
|
// Location streaming was either explicitly disabled (locations_send_begin = 0) or
|
||||||
for (chat_id, mut msg) in msgs.into_iter() {
|
// locations_send_until is in the past.
|
||||||
// TODO: better error handling
|
info!(
|
||||||
chat::send_msg(context, chat_id, &mut msg)
|
context,
|
||||||
.await
|
"Disabling location streaming for chat {}.", chat_id
|
||||||
.unwrap_or_default();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if continue_streaming {
|
|
||||||
job_try!(schedule_maybe_send_locations(context, true).await);
|
|
||||||
}
|
|
||||||
job::Status::Finished(Ok(()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) async fn job_maybe_send_locations_ended(
|
|
||||||
context: &Context,
|
|
||||||
job: &mut Job,
|
|
||||||
) -> job::Status {
|
|
||||||
// this function is called when location-streaming _might_ have ended for a chat.
|
|
||||||
// the function checks, if location-streaming is really ended;
|
|
||||||
// if so, a device-message is added if not yet done.
|
|
||||||
|
|
||||||
let chat_id = ChatId::new(job.foreign_id);
|
|
||||||
|
|
||||||
let (send_begin, send_until) = job_try!(
|
|
||||||
context
|
|
||||||
.sql
|
|
||||||
.query_row(
|
|
||||||
"SELECT locations_send_begin, locations_send_until FROM chats WHERE id=?",
|
|
||||||
paramsv![chat_id],
|
|
||||||
|row| Ok((row.get::<_, i64>(0)?, row.get::<_, i64>(1)?)),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
);
|
|
||||||
|
|
||||||
let now = time();
|
|
||||||
if !(send_begin != 0 && now <= send_until) {
|
|
||||||
// still streaming -
|
|
||||||
// may happen as several calls to dc_send_locations_to_chat()
|
|
||||||
// do not un-schedule pending DC_MAYBE_SEND_LOC_ENDED jobs
|
|
||||||
if !(send_begin == 0 && send_until == 0) {
|
|
||||||
// not streaming, device-message already sent
|
|
||||||
job_try!(
|
|
||||||
context
|
|
||||||
.sql
|
|
||||||
.execute(
|
|
||||||
"UPDATE chats \
|
|
||||||
SET locations_send_begin=0, locations_send_until=0 \
|
|
||||||
WHERE id=?",
|
|
||||||
paramsv![chat_id],
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
);
|
);
|
||||||
|
context
|
||||||
|
.sql
|
||||||
|
.execute(
|
||||||
|
"UPDATE chats \
|
||||||
|
SET locations_send_begin=0, locations_send_until=0 \
|
||||||
|
WHERE id=?",
|
||||||
|
paramsv![chat_id],
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.context("failed to disable location streaming")?;
|
||||||
|
|
||||||
let stock_str = stock_str::msg_location_disabled(context).await;
|
let stock_str = stock_str::msg_location_disabled(context).await;
|
||||||
job_try!(chat::add_info_msg(context, chat_id, &stock_str, now).await);
|
chat::add_info_msg(context, chat_id, &stock_str, now).await?;
|
||||||
context.emit_event(EventType::ChatModified(chat_id));
|
context.emit_event(EventType::ChatModified(chat_id));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
job::Status::Finished(Ok(()))
|
|
||||||
|
Ok(next_event)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ use crate::dc_tools::time;
|
|||||||
use crate::ephemeral::{self, delete_expired_imap_messages};
|
use crate::ephemeral::{self, delete_expired_imap_messages};
|
||||||
use crate::imap::Imap;
|
use crate::imap::Imap;
|
||||||
use crate::job::{self, Thread};
|
use crate::job::{self, Thread};
|
||||||
|
use crate::location;
|
||||||
use crate::log::LogExt;
|
use crate::log::LogExt;
|
||||||
use crate::smtp::{send_smtp_messages, Smtp};
|
use crate::smtp::{send_smtp_messages, Smtp};
|
||||||
use crate::sql;
|
use crate::sql;
|
||||||
@@ -36,6 +37,8 @@ pub(crate) enum Scheduler {
|
|||||||
smtp_handle: Option<task::JoinHandle<()>>,
|
smtp_handle: Option<task::JoinHandle<()>>,
|
||||||
ephemeral_handle: Option<task::JoinHandle<()>>,
|
ephemeral_handle: Option<task::JoinHandle<()>>,
|
||||||
ephemeral_interrupt_send: Sender<()>,
|
ephemeral_interrupt_send: Sender<()>,
|
||||||
|
location_handle: Option<task::JoinHandle<()>>,
|
||||||
|
location_interrupt_send: Sender<()>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,6 +68,10 @@ impl Context {
|
|||||||
pub(crate) async fn interrupt_ephemeral_task(&self) {
|
pub(crate) async fn interrupt_ephemeral_task(&self) {
|
||||||
self.scheduler.read().await.interrupt_ephemeral_task().await;
|
self.scheduler.read().await.interrupt_ephemeral_task().await;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) async fn interrupt_location(&self) {
|
||||||
|
self.scheduler.read().await.interrupt_location().await;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConnectionHandlers) {
|
async fn inbox_loop(ctx: Context, started: Sender<()>, inbox_handlers: ImapConnectionHandlers) {
|
||||||
@@ -386,6 +393,7 @@ impl Scheduler {
|
|||||||
let mut sentbox_handle = None;
|
let mut sentbox_handle = None;
|
||||||
let (smtp_start_send, smtp_start_recv) = channel::bounded(1);
|
let (smtp_start_send, smtp_start_recv) = channel::bounded(1);
|
||||||
let (ephemeral_interrupt_send, ephemeral_interrupt_recv) = channel::bounded(1);
|
let (ephemeral_interrupt_send, ephemeral_interrupt_recv) = channel::bounded(1);
|
||||||
|
let (location_interrupt_send, location_interrupt_recv) = channel::bounded(1);
|
||||||
|
|
||||||
let inbox_handle = {
|
let inbox_handle = {
|
||||||
let ctx = ctx.clone();
|
let ctx = ctx.clone();
|
||||||
@@ -454,6 +462,13 @@ impl Scheduler {
|
|||||||
}))
|
}))
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let location_handle = {
|
||||||
|
let ctx = ctx.clone();
|
||||||
|
Some(task::spawn(async move {
|
||||||
|
location::location_loop(&ctx, location_interrupt_recv).await;
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
|
||||||
*self = Scheduler::Running {
|
*self = Scheduler::Running {
|
||||||
inbox,
|
inbox,
|
||||||
mvbox,
|
mvbox,
|
||||||
@@ -465,6 +480,8 @@ impl Scheduler {
|
|||||||
smtp_handle,
|
smtp_handle,
|
||||||
ephemeral_handle,
|
ephemeral_handle,
|
||||||
ephemeral_interrupt_send,
|
ephemeral_interrupt_send,
|
||||||
|
location_handle,
|
||||||
|
location_interrupt_send,
|
||||||
};
|
};
|
||||||
|
|
||||||
// wait for all loops to be started
|
// wait for all loops to be started
|
||||||
@@ -540,6 +557,16 @@ impl Scheduler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn interrupt_location(&self) {
|
||||||
|
if let Scheduler::Running {
|
||||||
|
ref location_interrupt_send,
|
||||||
|
..
|
||||||
|
} = self
|
||||||
|
{
|
||||||
|
location_interrupt_send.try_send(()).ok();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Halt the scheduler.
|
/// Halt the scheduler.
|
||||||
pub(crate) async fn stop(&mut self) -> Result<()> {
|
pub(crate) async fn stop(&mut self) -> Result<()> {
|
||||||
match self {
|
match self {
|
||||||
@@ -556,6 +583,7 @@ impl Scheduler {
|
|||||||
smtp,
|
smtp,
|
||||||
smtp_handle,
|
smtp_handle,
|
||||||
ephemeral_handle,
|
ephemeral_handle,
|
||||||
|
location_handle,
|
||||||
..
|
..
|
||||||
} => {
|
} => {
|
||||||
if inbox_handle.is_some() {
|
if inbox_handle.is_some() {
|
||||||
@@ -586,6 +614,9 @@ impl Scheduler {
|
|||||||
if let Some(handle) = ephemeral_handle.take() {
|
if let Some(handle) = ephemeral_handle.take() {
|
||||||
handle.cancel().await;
|
handle.cancel().await;
|
||||||
}
|
}
|
||||||
|
if let Some(handle) = location_handle.take() {
|
||||||
|
handle.cancel().await;
|
||||||
|
}
|
||||||
|
|
||||||
*self = Scheduler::Stopped;
|
*self = Scheduler::Stopped;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
Reference in New Issue
Block a user