Skip to content

Commit b5f2c74

Browse files
committed
feat: Context::set_config(): Restart IO scheduler if needed (#5111)
Restart the IO scheduler if needed to make the new config value effective (for `MvboxMove, OnlyFetchMvbox, SentboxWatch` currently). Also add `set_config_internal()` which doesn't affect running the IO scheduler. The reason is that `Scheduler::start()` itself calls `set_config()`, although not for the mentioned keys, but still, and also Rust complains about recursive async calls.
1 parent ba35e83 commit b5f2c74

File tree

20 files changed

+180
-93
lines changed

20 files changed

+180
-93
lines changed

deltachat-ffi/deltachat.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -423,19 +423,16 @@ char* dc_get_blobdir (const dc_context_t* context);
423423
* Sending messages to self is needed for a proper multi-account setup,
424424
* however, on the other hand, may lead to unwanted notifications in non-delta clients.
425425
* - `sentbox_watch`= 1=watch `Sent`-folder for changes,
426-
* 0=do not watch the `Sent`-folder (default),
427-
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
426+
* 0=do not watch the `Sent`-folder (default).
428427
* - `mvbox_move` = 1=detect chat messages,
429428
* move them to the `DeltaChat` folder,
430429
* and watch the `DeltaChat` folder for updates (default),
431430
* 0=do not move chat-messages
432-
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
433431
* - `only_fetch_mvbox` = 1=Do not fetch messages from folders other than the
434432
* `DeltaChat` folder. Messages will still be fetched from the
435433
* spam folder and `sendbox_watch` will also still be respected
436434
* if enabled.
437435
* 0=watch all folders normally (default)
438-
* changes require restarting IO by calling dc_stop_io() and then dc_start_io().
439436
* - `show_emails` = DC_SHOW_EMAILS_OFF (0)=
440437
* show direct replies to chats only,
441438
* DC_SHOW_EMAILS_ACCEPTED_CONTACTS (1)=

deltachat-jsonrpc/src/api.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ impl CommandApi {
251251

252252
/// Starts background tasks for a single account.
253253
async fn start_io(&self, account_id: u32) -> Result<()> {
254-
let mut ctx = self.get_context(account_id).await?;
254+
let ctx = self.get_context(account_id).await?;
255255
ctx.start_io().await;
256256
Ok(())
257257
}
@@ -402,7 +402,7 @@ impl CommandApi {
402402
/// Configures this account with the currently set parameters.
403403
/// Setup the credential config before calling this.
404404
async fn configure(&self, account_id: u32) -> Result<()> {
405-
let mut ctx = self.get_context(account_id).await?;
405+
let ctx = self.get_context(account_id).await?;
406406
ctx.stop_io().await;
407407
let result = ctx.configure().await;
408408
if result.is_err() {
@@ -2125,13 +2125,6 @@ async fn set_config(
21252125
value,
21262126
)
21272127
.await?;
2128-
2129-
match key {
2130-
"sentbox_watch" | "mvbox_move" | "only_fetch_mvbox" => {
2131-
ctx.restart_io_if_running().await;
2132-
}
2133-
_ => {}
2134-
}
21352128
}
21362129
Ok(())
21372130
}

deltachat-repl/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,7 @@ enum ExitResult {
401401

402402
async fn handle_cmd(
403403
line: &str,
404-
mut ctx: Context,
404+
ctx: Context,
405405
selected_chat: &mut ChatId,
406406
) -> Result<ExitResult, Error> {
407407
let mut args = line.splitn(2, ' ');

python/tests/test_1_online.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,21 @@ def test_webxdc_download_on_demand(acfactory, data, lp):
382382
assert msgs_changed_event.data2 == 0
383383

384384

385+
def test_enable_mvbox_move(acfactory, lp):
386+
(ac1,) = acfactory.get_online_accounts(1)
387+
388+
lp.sec("ac2: start without mvbox thread")
389+
ac2 = acfactory.new_online_configuring_account(mvbox_move=False)
390+
acfactory.bring_accounts_online()
391+
392+
lp.sec("ac2: configuring mvbox")
393+
ac2.set_config("mvbox_move", "1")
394+
395+
lp.sec("ac1: send message and wait for ac2 to receive it")
396+
acfactory.get_accepted_chat(ac1, ac2).send_text("message1")
397+
assert ac2._evtracker.wait_next_incoming_message().text == "message1"
398+
399+
385400
def test_mvbox_sentbox_threads(acfactory, lp):
386401
lp.sec("ac1: start with mvbox thread")
387402
ac1 = acfactory.new_online_configuring_account(mvbox_move=True, sentbox_watch=True)

src/authres.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@ async fn update_authservid_candidates(
216216
if old_ids != new_ids {
217217
let new_config = new_ids.into_iter().collect::<Vec<_>>().join(" ");
218218
context
219-
.set_config(Config::AuthservIdCandidates, Some(&new_config))
219+
.set_config_internal(Config::AuthservIdCandidates, Some(&new_config))
220220
.await?;
221221
// Updating the authservid candidates may mean that we now consider
222222
// emails as "failed" which "passed" previously, so we need to

src/chat.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -783,7 +783,9 @@ impl ChatId {
783783

784784
context.emit_msgs_changed_without_ids();
785785

786-
context.set_config(Config::LastHousekeeping, None).await?;
786+
context
787+
.set_config_internal(Config::LastHousekeeping, None)
788+
.await?;
787789
context.scheduler.interrupt_inbox().await;
788790

789791
if chat.is_self_talk() {
@@ -4266,7 +4268,9 @@ pub(crate) async fn delete_and_reset_all_device_msgs(context: &Context) -> Resul
42664268
(),
42674269
)
42684270
.await?;
4269-
context.set_config(Config::QuotaExceeding, None).await?;
4271+
context
4272+
.set_config_internal(Config::QuotaExceeding, None)
4273+
.await?;
42704274
Ok(())
42714275
}
42724276

src/config.rs

Lines changed: 67 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,23 @@ impl Config {
367367
/// multiple users are sharing an account. Another example is `Self::SyncMsgs` itself which
368368
/// mustn't be controlled by other devices.
369369
pub(crate) fn is_synced(&self) -> bool {
370+
// We don't restart IO from the synchronisation code, so this is to be on the safe side.
371+
if self.needs_io_restart() {
372+
return false;
373+
}
370374
matches!(
371375
self,
372376
Self::Displayname | Self::MdnsEnabled | Self::ShowEmails
373377
)
374378
}
379+
380+
/// Whether the config option needs an IO scheduler restart to take effect.
381+
pub(crate) fn needs_io_restart(&self) -> bool {
382+
matches!(
383+
self,
384+
Config::MvboxMove | Config::OnlyFetchMvbox | Config::SentboxWatch
385+
)
386+
}
375387
}
376388

377389
impl Context {
@@ -491,10 +503,50 @@ impl Context {
491503
}
492504
}
493505

494-
/// Set the given config key.
495-
/// If `None` is passed as a value the value is cleared and set to the default if there is one.
506+
fn check_config(key: Config, value: Option<&str>) -> Result<()> {
507+
match key {
508+
Config::Socks5Enabled
509+
| Config::BccSelf
510+
| Config::E2eeEnabled
511+
| Config::MdnsEnabled
512+
| Config::SentboxWatch
513+
| Config::MvboxMove
514+
| Config::OnlyFetchMvbox
515+
| Config::FetchExistingMsgs
516+
| Config::DeleteToTrash
517+
| Config::SaveMimeHeaders
518+
| Config::Configured
519+
| Config::Bot
520+
| Config::NotifyAboutWrongPw
521+
| Config::SyncMsgs
522+
| Config::SignUnencrypted
523+
| Config::DisableIdle => {
524+
ensure!(
525+
matches!(value, None | Some("0") | Some("1")),
526+
"Boolean value must be either 0 or 1"
527+
);
528+
}
529+
_ => (),
530+
}
531+
Ok(())
532+
}
533+
534+
/// Set the given config key and make it effective.
535+
/// This may restart the IO scheduler. If `None` is passed as a value the value is cleared and
536+
/// set to the default if there is one.
496537
pub async fn set_config(&self, key: Config, value: Option<&str>) -> Result<()> {
497-
self.set_config_ex(key.is_synced().into(), key, value).await
538+
Self::check_config(key, value)?;
539+
540+
let _pause = match key.needs_io_restart() {
541+
true => self.scheduler.pause(self.clone()).await?,
542+
_ => Default::default(),
543+
};
544+
self.set_config_internal(key, value).await?;
545+
Ok(())
546+
}
547+
548+
pub(crate) async fn set_config_internal(&self, key: Config, value: Option<&str>) -> Result<()> {
549+
self.set_config_ex(Sync, key, value).await
498550
}
499551

500552
pub(crate) async fn set_config_ex(
@@ -503,7 +555,10 @@ impl Context {
503555
key: Config,
504556
mut value: Option<&str>,
505557
) -> Result<()> {
558+
Self::check_config(key, value)?;
559+
let sync = sync == Sync && key.is_synced();
506560
let better_value;
561+
507562
match key {
508563
Config::Selfavatar => {
509564
self.sql
@@ -536,28 +591,6 @@ impl Context {
536591
}
537592
self.sql.set_raw_config(key.as_ref(), value).await?;
538593
}
539-
Config::Socks5Enabled
540-
| Config::BccSelf
541-
| Config::E2eeEnabled
542-
| Config::MdnsEnabled
543-
| Config::SentboxWatch
544-
| Config::MvboxMove
545-
| Config::OnlyFetchMvbox
546-
| Config::FetchExistingMsgs
547-
| Config::DeleteToTrash
548-
| Config::SaveMimeHeaders
549-
| Config::Configured
550-
| Config::Bot
551-
| Config::NotifyAboutWrongPw
552-
| Config::SyncMsgs
553-
| Config::SignUnencrypted
554-
| Config::DisableIdle => {
555-
ensure!(
556-
matches!(value, None | Some("0") | Some("1")),
557-
"Boolean value must be either 0 or 1"
558-
);
559-
self.sql.set_raw_config(key.as_ref(), value).await?;
560-
}
561594
Config::Addr => {
562595
self.sql
563596
.set_raw_config(key.as_ref(), value.map(|s| s.to_lowercase()).as_deref())
@@ -570,7 +603,7 @@ impl Context {
570603
if key.is_synced() {
571604
self.emit_event(EventType::ConfigSynced { key });
572605
}
573-
if sync != Sync {
606+
if !sync {
574607
return Ok(());
575608
}
576609
let Some(val) = value else {
@@ -597,8 +630,7 @@ impl Context {
597630

598631
/// Set the given config to a boolean value.
599632
pub async fn set_config_bool(&self, key: Config, value: bool) -> Result<()> {
600-
self.set_config(key, if value { Some("1") } else { Some("0") })
601-
.await?;
633+
self.set_config(key, from_bool(value)).await?;
602634
Ok(())
603635
}
604636

@@ -618,6 +650,11 @@ impl Context {
618650
}
619651
}
620652

653+
/// Returns a value for use in `Context::set_config_*()` for the given `bool`.
654+
pub(crate) fn from_bool(val: bool) -> Option<&'static str> {
655+
Some(if val { "1" } else { "0" })
656+
}
657+
621658
// Separate impl block for self address handling
622659
impl Context {
623660
/// Determine whether the specified addr maps to the/a self addr.
@@ -644,13 +681,13 @@ impl Context {
644681
let mut secondary_addrs = self.get_all_self_addrs().await?;
645682
// never store a primary address also as a secondary
646683
secondary_addrs.retain(|a| !addr_cmp(a, primary_new));
647-
self.set_config(
684+
self.set_config_internal(
648685
Config::SecondaryAddrs,
649686
Some(secondary_addrs.join(" ").as_str()),
650687
)
651688
.await?;
652689

653-
self.set_config(Config::ConfiguredAddr, Some(primary_new))
690+
self.set_config_internal(Config::ConfiguredAddr, Some(primary_new))
654691
.await?;
655692

656693
Ok(())

src/configure.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
2222
use server_params::{expand_param_vector, ServerParams};
2323
use tokio::task;
2424

25-
use crate::config::Config;
25+
use crate::config::{self, Config};
2626
use crate::contact::addr_cmp;
2727
use crate::context::Context;
2828
use crate::imap::Imap;
@@ -112,12 +112,13 @@ impl Context {
112112
let mut param = LoginParam::load_candidate_params(self).await?;
113113
let old_addr = self.get_config(Config::ConfiguredAddr).await?;
114114
let success = configure(self, &mut param).await;
115-
self.set_config(Config::NotifyAboutWrongPw, None).await?;
115+
self.set_config_internal(Config::NotifyAboutWrongPw, None)
116+
.await?;
116117

117118
on_configure_completed(self, param, old_addr).await?;
118119

119120
success?;
120-
self.set_config(Config::NotifyAboutWrongPw, Some("1"))
121+
self.set_config_internal(Config::NotifyAboutWrongPw, Some("1"))
121122
.await?;
122123
Ok(())
123124
}
@@ -473,15 +474,15 @@ async fn configure(ctx: &Context, param: &mut LoginParam) -> Result<()> {
473474

474475
// the trailing underscore is correct
475476
param.save_as_configured_params(ctx).await?;
476-
ctx.set_config(Config::ConfiguredTimestamp, Some(&time().to_string()))
477+
ctx.set_config_internal(Config::ConfiguredTimestamp, Some(&time().to_string()))
477478
.await?;
478479

479480
progress!(ctx, 920);
480481

481482
e2ee::ensure_secret_key_exists(ctx).await?;
482483
info!(ctx, "key generation completed");
483484

484-
ctx.set_config_bool(Config::FetchedExistingMsgs, false)
485+
ctx.set_config_internal(Config::FetchedExistingMsgs, config::from_bool(false))
485486
.await?;
486487
ctx.scheduler.interrupt_inbox().await;
487488

src/contact.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1550,7 +1550,7 @@ pub(crate) async fn set_profile_image(
15501550
if contact_id == ContactId::SELF {
15511551
if was_encrypted {
15521552
context
1553-
.set_config(Config::Selfavatar, Some(profile_image))
1553+
.set_config_internal(Config::Selfavatar, Some(profile_image))
15541554
.await?;
15551555
} else {
15561556
info!(context, "Do not use unencrypted selfavatar.");
@@ -1563,7 +1563,9 @@ pub(crate) async fn set_profile_image(
15631563
AvatarAction::Delete => {
15641564
if contact_id == ContactId::SELF {
15651565
if was_encrypted {
1566-
context.set_config(Config::Selfavatar, None).await?;
1566+
context
1567+
.set_config_internal(Config::Selfavatar, None)
1568+
.await?;
15671569
} else {
15681570
info!(context, "Do not use unencrypted selfavatar deletion.");
15691571
}
@@ -1595,7 +1597,7 @@ pub(crate) async fn set_status(
15951597
if contact_id == ContactId::SELF {
15961598
if encrypted && has_chat_version {
15971599
context
1598-
.set_config(Config::Selfstatus, Some(&status))
1600+
.set_config_internal(Config::Selfstatus, Some(&status))
15991601
.await?;
16001602
}
16011603
} else {

src/context.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ impl Context {
408408
}
409409

410410
/// Starts the IO scheduler.
411-
pub async fn start_io(&mut self) {
411+
pub async fn start_io(&self) {
412412
if !self.is_configured().await.unwrap_or_default() {
413413
warn!(self, "can not start io on a context that is not configured");
414414
return;

0 commit comments

Comments
 (0)