Cancel ephemeral task in Context.stop_io()

ephemeral_task holds a reference to Context, preventing event emitter
from returning NULL and terminating event loop. Prior to this change,
there was no way to quickly terminate pending ephemeral_task.
This commit is contained in:
Alexander Krotov
2020-10-27 23:10:46 +03:00
committed by link2xt
parent f30c319fbf
commit 5c1bbc5d6a
2 changed files with 18 additions and 15 deletions

View File

@@ -562,9 +562,12 @@ void dc_start_io (dc_context_t* context);
int dc_is_io_running(const dc_context_t* context); int dc_is_io_running(const dc_context_t* context);
/** /**
* Stop job and IMAP/SMTP tasks and return when they are finished. * Stop job, IMAP, SMTP and other tasks and return when they
* If IO is not running, nothing happens. * are finished.
* To check the current IO state, use dc_is_io_running(). * To check the current IO state, use dc_is_io_running().
* Even if IO is not running, there may be pending tasks,
* so this function should always be called before releasing
* context to ensure clean termination of event loop.
* *
* If the context was created by the dc_accounts_t account manager, * If the context was created by the dc_accounts_t account manager,
* use dc_accounts_stop_io() instead of this function. * use dc_accounts_stop_io() instead of this function.
@@ -2373,7 +2376,6 @@ void dc_accounts_start_io (dc_accounts_t* accounts);
/** /**
* Stop job and IMAP/SMTP tasks for all accounts and return when they are finished. * Stop job and IMAP/SMTP tasks for all accounts and return when they are finished.
* If IO is not running, nothing happens.
* This is similar to dc_stop_io(), which, however, * This is similar to dc_stop_io(), which, however,
* must not be called for accounts handled by the account manager. * must not be called for accounts handled by the account manager.
* *

View File

@@ -163,10 +163,6 @@ impl Context {
/// Stops the IO scheduler. /// Stops the IO scheduler.
pub async fn stop_io(&self) { pub async fn stop_io(&self) {
info!(self, "stopping IO"); info!(self, "stopping IO");
if !self.is_io_running().await {
info!(self, "IO is not running");
return;
}
self.inner.stop_io().await; self.inner.stop_io().await;
} }
@@ -483,14 +479,19 @@ impl InnerContext {
} }
async fn stop_io(&self) { async fn stop_io(&self) {
assert!(self.is_io_running().await, "context is already stopped"); if self.is_io_running().await {
let token = { let token = {
let lock = &*self.scheduler.read().await; let lock = &*self.scheduler.read().await;
lock.pre_stop().await lock.pre_stop().await
}; };
{ {
let lock = &mut *self.scheduler.write().await; let lock = &mut *self.scheduler.write().await;
lock.stop(token).await; lock.stop(token).await;
}
}
if let Some(ephemeral_task) = self.ephemeral_task.write().await.take() {
ephemeral_task.cancel().await;
} }
} }
} }