@@ -3,7 +3,6 @@ use std::time::Duration;
3
3
use anyhow:: { Context as _, Result } ;
4
4
use async_channel:: Receiver ;
5
5
use async_imap:: extensions:: idle:: IdleResponse ;
6
- use futures_lite:: FutureExt ;
7
6
use tokio:: time:: timeout;
8
7
9
8
use super :: session:: Session ;
@@ -27,8 +26,6 @@ impl Session {
27
26
idle_interrupt_receiver : Receiver < ( ) > ,
28
27
folder : & str ,
29
28
) -> Result < Self > {
30
- use futures:: future:: FutureExt ;
31
-
32
29
let create = true ;
33
30
self . select_with_uidvalidity ( context, folder, create)
34
31
. await ?;
@@ -63,42 +60,46 @@ impl Session {
63
60
handle. as_mut ( ) . set_read_timeout ( None ) ;
64
61
let ( idle_wait, interrupt) = handle. wait_with_timeout ( IDLE_TIMEOUT ) ;
65
62
66
- enum Event {
67
- IdleResponse ( IdleResponse ) ,
68
- Interrupt ,
69
- }
70
-
71
63
info ! (
72
64
context,
73
65
"IDLE entering wait-on-remote state in folder {folder:?}."
74
66
) ;
75
- let fut = idle_wait. map ( |ev| ev. map ( Event :: IdleResponse ) ) . race ( async {
76
- idle_interrupt_receiver. recv ( ) . await . ok ( ) ;
77
67
78
- // cancel imap idle connection properly
79
- drop ( interrupt) ;
68
+ // Spawn a task to relay interrupts from `idle_interrupt_receiver`
69
+ // into interruptions of IDLE.
70
+ let interrupt_relay = {
71
+ let context = context. clone ( ) ;
72
+ let folder = folder. to_string ( ) ;
73
+
74
+ tokio:: spawn ( async move {
75
+ idle_interrupt_receiver. recv ( ) . await . ok ( ) ;
80
76
81
- Ok ( Event :: Interrupt )
82
- } ) ;
77
+ info ! ( context, "{folder:?}: Received interrupt, stopping IDLE." ) ;
83
78
84
- match fut. await {
85
- Ok ( Event :: IdleResponse ( IdleResponse :: NewData ( x) ) ) => {
79
+ // Drop `interrupt` in order to stop the IMAP IDLE.
80
+ drop ( interrupt) ;
81
+ } )
82
+ } ;
83
+
84
+ match idle_wait. await {
85
+ Ok ( IdleResponse :: NewData ( x) ) => {
86
86
info ! ( context, "{folder:?}: Idle has NewData {x:?}" ) ;
87
87
}
88
- Ok ( Event :: IdleResponse ( IdleResponse :: Timeout ) ) => {
88
+ Ok ( IdleResponse :: Timeout ) => {
89
89
info ! ( context, "{folder:?}: Idle-wait timeout or interruption." ) ;
90
90
}
91
- Ok ( Event :: IdleResponse ( IdleResponse :: ManualInterrupt ) ) => {
91
+ Ok ( IdleResponse :: ManualInterrupt ) => {
92
92
info ! ( context, "{folder:?}: Idle wait was interrupted manually." ) ;
93
93
}
94
- Ok ( Event :: Interrupt ) => {
95
- info ! ( context, "{folder:?}: Idle wait was interrupted." ) ;
96
- }
97
94
Err ( err) => {
98
95
warn ! ( context, "{folder:?}: Idle wait errored: {err:?}." ) ;
99
96
}
100
97
}
101
98
99
+ // Abort the task, then await to ensure the future is dropped.
100
+ interrupt_relay. abort ( ) ;
101
+ interrupt_relay. await . ok ( ) ;
102
+
102
103
let mut session = tokio:: time:: timeout ( Duration :: from_secs ( 15 ) , handle. done ( ) )
103
104
. await
104
105
. with_context ( || format ! ( "{folder}: IMAP IDLE protocol timed out" ) ) ?
0 commit comments