1
1
use std:: collections:: BTreeMap ;
2
2
use std:: sync:: Arc ;
3
+ use std:: time:: Duration ;
3
4
use std:: { collections:: HashMap , str:: FromStr } ;
4
5
5
6
use anyhow:: { anyhow, bail, ensure, Context , Result } ;
@@ -62,14 +63,14 @@ use crate::api::types::qr::QrObject;
62
63
struct AccountState {
63
64
/// The Qr code for current [`CommandApi::provide_backup`] call.
64
65
///
65
- /// If there currently is a call to [`CommandApi::provide_backup`] this will be
66
- /// `Pending` or `Ready` , otherwise `NoProvider `.
67
- backup_provider_qr : watch:: Sender < ProviderQr > ,
66
+ /// If there is currently is a call to [`CommandApi::provide_backup`] this will be
67
+ /// `Some` , otherwise `None `.
68
+ backup_provider_qr : watch:: Sender < Option < Qr > > ,
68
69
}
69
70
70
71
impl Default for AccountState {
71
72
fn default ( ) -> Self {
72
- let ( tx , _rx ) = watch:: channel ( ProviderQr :: NoProvider ) ;
73
+ let tx = watch:: Sender :: new ( None ) ;
73
74
Self {
74
75
backup_provider_qr : tx,
75
76
}
@@ -123,21 +124,13 @@ impl CommandApi {
123
124
. with_state ( account_id, |state| state. backup_provider_qr . subscribe ( ) )
124
125
. await ;
125
126
126
- let val: ProviderQr = receiver. borrow_and_update ( ) . clone ( ) ;
127
- match val {
128
- ProviderQr :: NoProvider => bail ! ( "No backup being provided" ) ,
129
- ProviderQr :: Pending => loop {
130
- if receiver. changed ( ) . await . is_err ( ) {
131
- bail ! ( "No backup being provided (account state dropped)" ) ;
132
- }
133
- let val: ProviderQr = receiver. borrow ( ) . clone ( ) ;
134
- match val {
135
- ProviderQr :: NoProvider => bail ! ( "No backup being provided" ) ,
136
- ProviderQr :: Pending => continue ,
137
- ProviderQr :: Ready ( qr) => break Ok ( qr) ,
138
- } ;
139
- } ,
140
- ProviderQr :: Ready ( qr) => Ok ( qr) ,
127
+ loop {
128
+ if let Some ( qr) = receiver. borrow_and_update ( ) . clone ( ) {
129
+ return Ok ( qr) ;
130
+ }
131
+ if receiver. changed ( ) . await . is_err ( ) {
132
+ bail ! ( "No backup being provided (account state dropped)" ) ;
133
+ }
141
134
}
142
135
}
143
136
}
@@ -1569,32 +1562,39 @@ impl CommandApi {
1569
1562
/// Returns once a remote device has retrieved the backup, or is cancelled.
1570
1563
async fn provide_backup ( & self , account_id : u32 ) -> Result < ( ) > {
1571
1564
let ctx = self . get_context ( account_id) . await ?;
1565
+
1566
+ let provider = imex:: BackupProvider :: prepare ( & ctx) . await ?;
1572
1567
self . with_state ( account_id, |state| {
1573
- state. backup_provider_qr . send_replace ( ProviderQr :: Pending ) ;
1568
+ state. backup_provider_qr . send_replace ( Some ( provider . qr ( ) ) ) ;
1574
1569
} )
1575
1570
. await ;
1576
1571
1577
- let provider = imex:: BackupProvider :: prepare ( & ctx) . await ?;
1572
+ let res = provider. await ;
1573
+
1578
1574
self . with_state ( account_id, |state| {
1579
- state
1580
- . backup_provider_qr
1581
- . send_replace ( ProviderQr :: Ready ( provider. qr ( ) ) ) ;
1575
+ state. backup_provider_qr . send_replace ( None ) ;
1582
1576
} )
1583
1577
. await ;
1584
1578
1585
- provider . await
1579
+ res
1586
1580
}
1587
1581
1588
1582
/// Returns the text of the QR code for the running [`CommandApi::provide_backup`].
1589
1583
///
1590
1584
/// This QR code text can be used in [`CommandApi::get_backup`] on a second device to
1591
1585
/// retrieve the backup and setup this second device.
1592
1586
///
1593
- /// This call will fail if there is currently no concurrent call to
1594
- /// [`CommandApi::provide_backup`]. This call may block if the QR code is not yet
1595
- /// ready .
1587
+ /// This call will block until the QR code is ready,
1588
+ /// even if there is no concurrent call to [`CommandApi::provide_backup`],
1589
+ /// but will fail after 10 seconds to avoid deadlocks .
1596
1590
async fn get_backup_qr ( & self , account_id : u32 ) -> Result < String > {
1597
- let qr = self . inner_get_backup_qr ( account_id) . await ?;
1591
+ let qr = tokio:: time:: timeout (
1592
+ Duration :: from_secs ( 10 ) ,
1593
+ self . inner_get_backup_qr ( account_id) ,
1594
+ )
1595
+ . await
1596
+ . context ( "Backup provider did not start in time" ) ?
1597
+ . context ( "Failed to get backup QR code" ) ?;
1598
1598
qr:: format_backup ( & qr)
1599
1599
}
1600
1600
@@ -1603,14 +1603,20 @@ impl CommandApi {
1603
1603
/// This QR code can be used in [`CommandApi::get_backup`] on a second device to
1604
1604
/// retrieve the backup and setup this second device.
1605
1605
///
1606
- /// This call will fail if there is currently no concurrent call to
1607
- /// [`CommandApi::provide_backup`]. This call may block if the QR code is not yet
1608
- /// ready .
1606
+ /// This call will block until the QR code is ready,
1607
+ /// even if there is no concurrent call to [`CommandApi::provide_backup`],
1608
+ /// but will fail after 10 seconds to avoid deadlocks .
1609
1609
///
1610
1610
/// Returns the QR code rendered as an SVG image.
1611
1611
async fn get_backup_qr_svg ( & self , account_id : u32 ) -> Result < String > {
1612
1612
let ctx = self . get_context ( account_id) . await ?;
1613
- let qr = self . inner_get_backup_qr ( account_id) . await ?;
1613
+ let qr = tokio:: time:: timeout (
1614
+ Duration :: from_secs ( 10 ) ,
1615
+ self . inner_get_backup_qr ( account_id) ,
1616
+ )
1617
+ . await
1618
+ . context ( "Backup provider did not start in time" ) ?
1619
+ . context ( "Failed to get backup QR code" ) ?;
1614
1620
generate_backup_qr ( & ctx, & qr) . await
1615
1621
}
1616
1622
@@ -2141,15 +2147,3 @@ async fn get_config(
2141
2147
. await
2142
2148
}
2143
2149
}
2144
-
2145
- /// Whether a QR code for a BackupProvider is currently available.
2146
- #[ allow( clippy:: large_enum_variant) ]
2147
- #[ derive( Clone , Debug ) ]
2148
- enum ProviderQr {
2149
- /// There is no provider, asking for a QR is an error.
2150
- NoProvider ,
2151
- /// There is a provider, the QR code is pending.
2152
- Pending ,
2153
- /// There is a provider and QR code.
2154
- Ready ( Qr ) ,
2155
- }
0 commit comments