Commit 1fe480c16a3c
tarball10 files changed+194-99
@@ -21,7 +21,7 @@ serde = { version = "1.0.219", features = ["derive", "rc"], optional = true }
2121
thiserror = "2.0.16"2222
mime = "0.3.17"2323
sha2 = "0.10.9"24
-tokio = { version = "1.47.1", features = ["rt", "sync", "time"] }24
+tokio = { version = "1.47.1", features = ["macros", "sync"] }2525
tracing = "0.1.41"2626
url = { version = "2.5.7", optional = true }2727
@@ -1,6 +1,7 @@
11
use std::sync::Arc;22
33
use futures_util::StreamExt;4
+use matrix_sdk::executor::{JoinHandle, spawn};45
use matrix_sdk::{56
Client,67
encryption::{@@ -14,7 +15,7 @@ use matrix_sdk::{
1415
ruma::events::key::verification::VerificationMethod,1516
};1617
use ruma::{OwnedDeviceId, UserId};17
-use tokio::{sync::Mutex, task::JoinHandle};18
+use tokio::sync::Mutex;1819
1920
use crate::{2021
EncryptionState, Error, ErrorKind, Event, KeyBackupState, RecoveryState, VerificationAction,@@ -266,7 +267,7 @@ impl VerificationController {
266267
Error::with_source(ErrorKind::Interrupted, "event receiver has closed", source)267268
})?;268269
let active = self.active.clone();269
- self.task = Some(tokio::spawn(async move {270
+ self.task = Some(spawn(async move {270271
follow_request(request, changes, active, sender).await;271272
}));272273
Ok(())@@ -311,7 +312,7 @@ impl VerificationController {
311312
}312313
}313314
let active = self.active.clone();314
- self.task = Some(tokio::spawn(async move {315
+ self.task = Some(spawn(async move {315316
follow_request(request, changes, active, sender).await;316317
}));317318
}@@ -0,0 +1,82 @@
1
+use std::future::Future;2
+3
+use futures_util::{StreamExt, stream::FuturesUnordered};4
+use matrix_sdk::SendOutsideWasm;5
+pub(super) use matrix_sdk::executor::{AbortHandle, JoinError, JoinHandle, spawn};6
+7
+#[cfg(not(target_family = "wasm"))]8
+pub(super) type EngineBoxFuture<'a, T> = futures_util::future::BoxFuture<'a, T>;9
+#[cfg(target_family = "wasm")]10
+pub(super) type EngineBoxFuture<'a, T> = futures_util::future::LocalBoxFuture<'a, T>;11
+12
+#[cfg(not(target_family = "wasm"))]13
+pub(super) fn is_panic(error: &JoinError) -> bool {14
+ error.is_panic()15
+}16
+17
+/// the sdk's wasm join error has no payload, though a panic there aborts the instance anyway18
+#[cfg(target_family = "wasm")]19
+pub(super) fn is_panic(error: &JoinError) -> bool {20
+ !error.is_cancelled()21
+}22
+23
+#[cfg(not(target_family = "wasm"))]24
+pub(super) fn resume_unwind(error: JoinError) -> ! {25
+ std::panic::resume_unwind(error.into_panic())26
+}27
+28
+#[cfg(target_family = "wasm")]29
+pub(super) fn resume_unwind(error: JoinError) -> ! {30
+ panic!("{error}")31
+}32
+33
+/// a `JoinSet` that runs on wasm too, and like it aborts whatever's left when dropped34
+pub(super) struct TaskSet<T: 'static> {35
+ handles: FuturesUnordered<JoinHandle<T>>,36
+}37
+38
+impl<T: 'static> TaskSet<T> {39
+ pub(super) fn new() -> Self {40
+ Self {41
+ handles: FuturesUnordered::new(),42
+ }43
+ }44
+45
+ pub(super) fn spawn(46
+ &mut self,47
+ task: impl Future<Output = T> + SendOutsideWasm + 'static,48
+ ) -> AbortHandle49
+ where50
+ T: SendOutsideWasm,51
+ {52
+ let handle = spawn(task);53
+ let abort = handle.abort_handle();54
+ self.handles.push(handle);55
+ abort56
+ }57
+58
+ pub(super) fn is_empty(&self) -> bool {59
+ self.handles.is_empty()60
+ }61
+62
+ pub(super) async fn join_next(&mut self) -> Option<Result<T, JoinError>> {63
+ self.handles.next().await64
+ }65
+66
+ fn abort_all(&self) {67
+ for handle in &self.handles {68
+ handle.abort();69
+ }70
+ }71
+72
+ pub(super) async fn shutdown(&mut self) {73
+ self.abort_all();74
+ while self.handles.next().await.is_some() {}75
+ }76
+}77
+78
+impl<T: 'static> Drop for TaskSet<T> {79
+ fn drop(&mut self) {80
+ self.abort_all();81
+ }82
+}@@ -9,10 +9,10 @@ use std::{
99
1010
use futures_util::{1111
FutureExt, StreamExt,12
- future::{BoxFuture, Shared, WeakShared},12
+ future::{Shared, WeakShared},1313
};1414
use matrix_sdk::{15
- Client, Room,15
+ Client, Room, SendOutsideWasm, SyncOutsideWasm,1616
media::{MediaFormat, MediaRequestParameters, MediaThumbnailSettings},1717
room::reply::{EnforceThread, Reply as SdkReply},1818
ruma::{@@ -31,6 +31,7 @@ use matrix_sdk_crypto::{AttachmentDecryptor, MediaEncryptionInfo};
3131
use ruma::MxcUri;3232
use tokio::sync::Mutex;3333
34
+use super::executor::EngineBoxFuture;3435
use crate::{3536
AttachmentUpload, Error, ErrorKind, Media, MediaId, MediaKind, MediaSize, TimelineId,3637
normalise::{MediaRegistration, MediaScope},@@ -42,7 +43,7 @@ const DOWNLOAD_IDLE: Duration = Duration::from_secs(60);
4243
/// the sdk client gives up on any request after thirty seconds, far too soon for a video4344
const DOWNLOAD_TOTAL: Duration = Duration::from_secs(60 * 60);4445
45
-type FlightFuture = BoxFuture<'static, Result<Media, Error>>;46
+type FlightFuture = EngineBoxFuture<'static, Result<Media, Error>>;4647
type Flight = Shared<FlightFuture>;4748
4849
/// owns everything needed to turn a media id or an mxc uri into bytes, so the@@ -188,7 +189,7 @@ impl MediaService {
188189
match flights.get(&key).and_then(WeakShared::upgrade) {189190
Some(flight) => flight,190191
None => {191
- let flight: Flight = AssertUnwindSafe(fetch(192
+ let prepared = AssertUnwindSafe(fetch(192193
self.client.clone(),193194
request,194195
mimetype,@@ -199,9 +200,9 @@ impl MediaService {
199200
result.unwrap_or_else(|_| {200201
Err(Error::new(ErrorKind::Other, "media fetch panicked"))201202
})202
- })203
- .boxed()204
- .shared();203
+ });204
+ let boxed: FlightFuture = Box::pin(prepared);205
+ let flight: Flight = boxed.shared();205206
if let Some(weak) = flight.downgrade() {206207
flights.insert(key, weak);207208
}@@ -335,7 +336,9 @@ async fn cached_media(
335336
client: &Client,336337
request: &MediaRequestParameters,337338
) -> Result<Option<Vec<u8>>, Error> {338
- fn cache_failed(source: impl std::error::Error + Send + Sync + 'static) -> Error {339
+ fn cache_failed(340
+ source: impl std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static,341
+ ) -> Error {339342
Error::with_source(ErrorKind::Store, "reading media cache failed", source)340343
}341344
client@@ -356,7 +359,9 @@ fn maybe_decrypt(
356359
let MediaSource::Encrypted(file) = &request.source else {357360
return Ok(bytes);358361
};359
- fn decrypt_failed(source: impl std::error::Error + Send + Sync + 'static) -> Error {362
+ fn decrypt_failed(363
+ source: impl std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static,364
+ ) -> Error {360365
Error::with_source(361366
ErrorKind::MediaUnavailable,362367
"media decryption failed",@@ -447,7 +452,7 @@ async fn download_bounded(
447452
let mut stream = response.bytes_stream();448453
let mut bytes = Vec::new();449454
loop {450
- let chunk = tokio::time::timeout(DOWNLOAD_IDLE, stream.next())455
+ let chunk = matrix_sdk::timeout::timeout(stream.next(), DOWNLOAD_IDLE)451456
.await452457
.map_err(|_| Error::new(ErrorKind::Network, "media download stalled"))?;453458
let Some(chunk) = chunk else { break };@@ -1,4 +1,5 @@
11
mod commands;2
+mod executor;23
mod media;34
mod mutations;45
mod room;@@ -8,23 +9,22 @@ mod watchers;
89
use std::{910
collections::{HashMap, VecDeque},1011
future::Future,12
+ panic::AssertUnwindSafe,1113
sync::Arc,1214
};1315
14
-use futures_util::future::BoxFuture;15
-use matrix_sdk::{Client, SessionChange};16
+use futures_util::FutureExt;17
+use matrix_sdk::{Client, SendOutsideWasm, SessionChange};1618
use matrix_sdk_ui::sync_service::SyncService;1719
use ruma::OwnedRoomId;1820
use thiserror::Error as ThisError;19
-use tokio::{20
- sync::{Notify, broadcast, mpsc, watch},21
- task::{AbortHandle, Id as TaskId, JoinError, JoinSet},22
-};21
+use tokio::sync::{Notify, broadcast, mpsc, watch};2322
2423
pub(crate) use media::MediaService;2524
2625
use self::{2726
commands::Job,27
+ executor::{EngineBoxFuture, TaskSet},2828
mutations::{Mutation, MutationJob},2929
room::{ActorNote, ActorRequest, RoomActor},3030
timeline::{SessionContext, Supervisor},@@ -143,36 +143,49 @@ impl TaskContext {
143143
}144144
}145145
146
+/// tasks catch their own panics, since the sdk's wasm join error drops the payload147
+fn panic_message(payload: Box<dyn std::any::Any + Send>) -> String {148
+ payload149
+ .downcast_ref::<&str>()150
+ .map(ToString::to_string)151
+ .or_else(|| payload.downcast_ref::<String>().cloned())152
+ .unwrap_or_else(|| "task panicked with a non-string payload".to_owned())153
+}154
+146155
struct TaskRegistry {147
- tasks: JoinSet<()>,148
- contexts: HashMap<TaskId, TaskContext>,156
+ tasks: TaskSet<(TaskContext, Option<String>)>,149157
}150158
151159
impl TaskRegistry {152160
fn spawn(153161
&mut self,154162
context: TaskContext,155
- task: impl Future<Output = ()> + Send + 'static,156
- ) -> AbortHandle {157
- let handle = self.tasks.spawn(task);158
- self.contexts.insert(handle.id(), context);159
- handle160
- }161
-162
- async fn join_next(&mut self) -> Option<(TaskContext, Result<(), JoinError>)> {163
- let result = self.tasks.join_next_with_id().await?;164
- let id = match &result {165
- Ok((id, ())) => *id,166
- Err(error) => error.id(),167
- };168
- let context = self.contexts.remove(&id)?;169
- Some((context, result.map(drop)))163
+ task: impl Future<Output = ()> + SendOutsideWasm + 'static,164
+ ) {165
+ self.tasks.spawn(async move {166
+ let panic = AssertUnwindSafe(task)167
+ .catch_unwind()168
+ .await169
+ .err()170
+ .map(panic_message);171
+ (context, panic)172
+ });173
+ }174
+175
+ fn is_empty(&self) -> bool {176
+ self.tasks.is_empty()177
+ }178
+179
+ async fn join_next(&mut self) -> Option<(TaskContext, Option<String>)> {180
+ loop {181
+ if let Ok(done) = self.tasks.join_next().await? {182
+ return Some(done);183
+ }184
+ }170185
}171186
172187
async fn shutdown(&mut self) {173
- self.tasks.abort_all();174
- while self.tasks.join_next().await.is_some() {}175
- self.contexts.clear();188
+ self.tasks.shutdown().await;176189
}177190
}178191
@@ -230,7 +243,7 @@ pub struct Engine {
230243
encryption: mpsc::Sender<Job<EncryptionCommand>>,231244
queues: Option<Queues>,232245
/// one progress forwarder per room that has uploaded, and whether it's subscribed yet233
- uploads: HashMap<OwnedRoomId, (AbortHandle, watch::Receiver<bool>)>,246
+ uploads: HashMap<OwnedRoomId, watch::Receiver<bool>>,234247
/// the client can outlive us, so the handler has to come off it when we stop235248
verification_handler: Option<matrix_sdk::event_handler::EventHandlerHandle>,236249
stop: Arc<Notify>,@@ -283,8 +296,7 @@ impl Engine {
283296
config,284297
media,285298
tasks: TaskRegistry {286
- tasks: JoinSet::new(),287
- contexts: HashMap::new(),299
+ tasks: TaskSet::new(),288300
},289301
rooms: HashMap::new(),290302
next_generation: 0,@@ -309,7 +321,7 @@ impl Engine {
309321
310322
/// boxed because the engine's future is deep enough to blow the compiler's query depth311323
/// in every crate that spawns it, which isn't something a client should have to fix312
- pub fn run(self) -> BoxFuture<'static, Result<(), EngineError>> {324
+ pub fn run(self) -> EngineBoxFuture<'static, Result<(), EngineError>> {313325
Box::pin(self.run_inner())314326
}315327
@@ -346,13 +358,10 @@ impl Engine {
346358
break;347359
}348360
() = self.stop.notified() => break,349
- Some((context, result)) = self.tasks.join_next(), if !self.tasks.tasks.is_empty() => {350
- let Err(error) = result else { continue };351
- if !error.is_panic() {352
- continue;353
- }361
+ Some((context, panic)) = self.tasks.join_next(), if !self.tasks.is_empty() => {362
+ let Some(message) = panic else { continue };354363
if let TaskContext::Command(label) = context {355
- tracing::error!(label, %error, "command task panicked");364
+ tracing::error!(label, panic = %message, "command task panicked");356365
continue;357366
}358367
failure = Some(EngineError::TaskPanicked { task: context.label() });@@ -392,7 +401,7 @@ impl Engine {
392401
self.verification_handler = Some(self.verification_handler(incoming));393402
let client = &self.client;394403
let events = || self.port.event_sender();395
- let watchers: [(&'static str, BoxFuture<'static, ()>); 11] = [404
+ let watchers: [(&'static str, EngineBoxFuture<'static, ()>); 11] = [396405
(397406
"upload failure watcher",398407
Box::pin(watchers::upload_failures(@@ -606,7 +615,7 @@ impl Engine {
606615
&mut self,607616
label: &'static str,608617
reply: Responder,609
- work: impl Future<Output = Result<(), Error>> + Send + 'static,618
+ work: impl Future<Output = Result<(), Error>> + SendOutsideWasm + 'static,610619
) {611620
self.tasks.spawn(TaskContext::Command(label), async move {612621
reply.done(work.await);@@ -630,18 +639,17 @@ impl Engine {
630639
}631640
632641
fn upload_forwarder(&mut self, room: &matrix_sdk::Room) -> watch::Receiver<bool> {633
- self.uploads634
- .retain(|_, (forwarder, _)| !forwarder.is_finished());635
- if let Some((_, ready)) = self.uploads.get(room.room_id()) {642
+ self.uploads.retain(|_, ready| ready.has_changed().is_ok());643
+ if let Some(ready) = self.uploads.get(room.room_id()) {636644
return ready.clone();637645
}638646
let (subscribed, ready) = watch::channel(false);639
- let forwarder = self.tasks.spawn(647
+ self.tasks.spawn(640648
TaskContext::Command("upload watcher"),641649
watchers::room_uploads(room.clone(), self.port.event_sender(), subscribed),642650
);643651
self.uploads644
- .insert(room.room_id().to_owned(), (forwarder, ready.clone()));652
+ .insert(room.room_id().to_owned(), ready.clone());645653
ready646654
}647655
@@ -19,9 +19,10 @@ use matrix_sdk::{
1919
},2020
};2121
use ruma::{OwnedRoomId, OwnedUserId};22
-use tokio::{sync::mpsc, task::JoinSet};22
+use tokio::sync::mpsc;2323
2424
use super::commands::{Job, guarded};25
+use super::executor::TaskSet;2526
use crate::{2627
CommandOutput, CreateRoomOptions, Error, ErrorKind, PowerLevelChanges, RoomCommand, RoomPreset,2728
RoomVisibility,@@ -56,7 +57,7 @@ pub(super) async fn run_scheduler(
5657
let fresh_dms = FreshDms::default();5758
let mut waiting = HashMap::<Key, VecDeque<MutationJob>>::new();5859
let mut running = HashSet::<Key>::new();59
- let mut tasks = JoinSet::<Key>::new();60
+ let mut tasks = TaskSet::<Key>::new();6061
let mut queued = 0_usize;6162
let mut independent = 0_u64;6263
let mut receiving = true;@@ -150,7 +151,7 @@ fn key(mutation: &Mutation, server: Option<&ruma::ServerName>) -> Option<Key> {
150151
type FreshDms = Arc<std::sync::Mutex<HashMap<OwnedUserId, OwnedRoomId>>>;151152
152153
fn spawn(153
- tasks: &mut JoinSet<Key>,154
+ tasks: &mut TaskSet<Key>,154155
client: &Client,155156
fresh_dms: &FreshDms,156157
key: Key,@@ -3,11 +3,9 @@ use std::collections::{HashMap, VecDeque};
33
use matrix_sdk::{Room, RoomState};44
use matrix_sdk_ui::timeline::{RoomExt, Timeline, TimelineFocus, TimelineReadReceiptTracking};55
use ruma::{OwnedEventId, OwnedRoomId};6
-use tokio::{7
- sync::mpsc,8
- task::{AbortHandle, JoinSet},9
-};6
+use tokio::sync::mpsc;107
8
+use super::executor::{AbortHandle, JoinError, TaskSet, is_panic, resume_unwind};119
use super::timeline::{SessionContext, TaskGuard, TimelineSession};1210
use crate::{1311
Error, ErrorKind, Event, TimelineCommand, TimelineId, TypingState, transport::Responder,@@ -100,7 +98,7 @@ impl RoomActor {
10098
let mut timelines = Timelines {10199
room: room_timeline,102100
threads: Threads::new(self.requests.max_capacity()),103
- work: JoinSet::new(),101
+ work: TaskSet::new(),104102
};105103
let close = self.serve_open(&mut timelines).await;106104
if close.is_none() {@@ -150,7 +148,7 @@ impl RoomActor {
150148
}151149
Some(label) = self.panics.recv() => panic!("{label} panicked in room {}", self.room_id),152150
Some(done) = timelines.work.join_next(), if !timelines.work.is_empty() => {153
- if let Err(error) = done && error.is_panic() {151
+ if let Err(error) = done && is_panic(&error) {154152
tracing::warn!(room_id = %self.room_id, %error, "timeline operation panicked");155153
}156154
}@@ -209,7 +207,7 @@ async fn watch_typing(
209207
struct Timelines {210208
room: TimelineSession,211209
threads: Threads,212
- work: JoinSet<()>,210
+ work: TaskSet<()>,213211
}214212
215213
impl Timelines {@@ -262,7 +260,7 @@ impl Timelines {
262260
263261
async fn thread_built(264262
&mut self,265
- built: Result<Built, tokio::task::JoinError>,263
+ built: Result<Built, JoinError>,266264
room_id: &OwnedRoomId,267265
context: &SessionContext,268266
) {@@ -324,7 +322,7 @@ struct OpeningThread {
324322
struct Threads {325323
open: HashMap<OwnedEventId, TimelineSession>,326324
opening: HashMap<OwnedEventId, OpeningThread>,327
- builds: JoinSet<Built>,325
+ builds: TaskSet<Built>,328326
next_generation: u64,329327
queued: usize,330328
capacity: usize,@@ -335,7 +333,7 @@ impl Threads {
335333
Self {336334
open: HashMap::new(),337335
opening: HashMap::new(),338
- builds: JoinSet::new(),336
+ builds: TaskSet::new(),339337
next_generation: 0,340338
queued: 0,341339
capacity,@@ -403,7 +401,7 @@ impl Threads {
403401
/// `None` when the build belongs to a thread that was closed while it ran404402
fn built(405403
&mut self,406
- built: Result<Built, tokio::task::JoinError>,404
+ built: Result<Built, JoinError>,407405
) -> Option<(408406
OwnedEventId,409407
Queued,@@ -412,7 +410,7 @@ impl Threads {
412410
let (root, generation, timeline) = match built {413411
Ok(built) => built,414412
Err(error) if error.is_cancelled() => return None,415
- Err(error) => std::panic::resume_unwind(error.into_panic()),413
+ Err(error) => resume_unwind(error),416414
};417415
if self418416
.opening@@ -443,8 +441,7 @@ impl Threads {
443441
}444442
445443
async fn close(&mut self) {446
- self.builds.abort_all();447
- while self.builds.join_next().await.is_some() {}444
+ self.builds.shutdown().await;448445
self.opening.clear();449446
for (_, session) in self.open.drain() {450447
session.close().await;@@ -1,6 +1,7 @@
1
-use std::{collections::HashSet, future::Future, panic::AssertUnwindSafe, pin::Pin, sync::Arc};1
+use std::{collections::HashSet, future::Future, panic::AssertUnwindSafe, sync::Arc};22
33
use futures_util::{FutureExt, StreamExt};4
+use matrix_sdk::SendOutsideWasm;45
use matrix_sdk::{56
room::edit::EditedContent,67
room::reply::{EnforceThread, Reply as SdkReply},@@ -16,11 +17,9 @@ use matrix_sdk::{
1617
};1718
use matrix_sdk_ui::timeline::{Timeline, TimelineEventItemId};1819
use ruma::{OwnedEventId, OwnedUserId};19
-use tokio::{20
- sync::mpsc,21
- task::{JoinHandle, JoinSet},22
-};20
+use tokio::sync::mpsc;2321
22
+use super::executor::{EngineBoxFuture, JoinHandle, TaskSet, spawn};2423
use crate::{2524
Error, ErrorKind, Event, EventRef, HistoryState, MessageContent, MessageEditContent,2625
MessageMentions, RichText, TimelineCommand, TimelineId,@@ -44,10 +43,10 @@ impl Supervisor {
4443
pub(super) fn spawn(4544
&self,4645
label: &'static str,47
- task: impl Future<Output = ()> + Send + 'static,46
+ task: impl Future<Output = ()> + SendOutsideWasm + 'static,4847
) -> JoinHandle<()> {4948
let panics = self.0.clone();50
- tokio::spawn(async move {49
+ spawn(async move {5150
if AssertUnwindSafe(task).catch_unwind().await.is_err() {5251
let _ = panics.send(label);5352
}@@ -166,11 +165,11 @@ impl TimelineSession {
166165
&self,167166
command: TimelineCommand,168167
reply: Responder,169
- work: &mut JoinSet<()>,168
+ work: &mut TaskSet<()>,170169
) {171170
let timeline = self.timeline.clone();172171
let thread_root = self.id.thread_root().cloned();173
- let background: Pin<Box<dyn Future<Output = Result<(), Error>> + Send>> = match command {172
+ let background: EngineBoxFuture<'static, Result<(), Error>> = match command {174173
TimelineCommand::Paginate { limit, .. } => return self.paginate(limit, Some(reply)),175174
// network-bound, so it runs beside the actor instead of queueing the room behind it176175
TimelineCommand::Redact { target, reason, .. } => Box::pin(async move {@@ -331,7 +330,7 @@ fn item_id(target: EventRef) -> TimelineEventItemId {
331330
struct ReplyFetcher {332331
timeline: Arc<Timeline>,333332
asked: HashSet<OwnedEventId>,334
- fetches: JoinSet<()>,333
+ fetches: TaskSet<()>,335334
}336335
337336
impl ReplyFetcher {@@ -369,7 +368,7 @@ async fn start_watcher(
369368
let mut replies = ReplyFetcher {370369
timeline: timeline.clone(),371370
asked: HashSet::new(),372
- fetches: JoinSet::new(),371
+ fetches: TaskSet::new(),373372
};374373
replies.fetch(normalised.missing_replies);375374
let id = id.clone();@@ -1,7 +1,4 @@
1
-use std::{2
- sync::Arc,3
- time::{Duration, Instant},4
-};1
+use std::{sync::Arc, time::Duration};52
63
use futures_util::StreamExt;74
use matrix_sdk::{@@ -10,11 +7,10 @@ use matrix_sdk::{
107
send_queue::{LocalEchoContent, RoomSendQueueUpdate},118
};129
use matrix_sdk_ui::sync_service::{State as SyncState, SyncService};13
-use tokio::{14
- sync::{Notify, broadcast::error::RecvError, watch},15
- task::JoinSet,16
-};10
+use ruma::time::Instant;11
+use tokio::sync::{Notify, broadcast::error::RecvError, watch};1712
13
+use super::executor::TaskSet;1814
use crate::{1915
ConnectionState, Event, encryption::encryption_state, error::classify_sdk_error,2016
transport::EventSender,@@ -106,7 +102,7 @@ pub(super) async fn connection(
106102
}107103
if restart {108104
sync.stop().await;109
- tokio::time::sleep(backoff).await;105
+ matrix_sdk::sleep::sleep(backoff).await;110106
backoff = (backoff * 2).min(SYNC_RESTART_MAX);111107
sync.start().await;112108
}@@ -119,7 +115,7 @@ pub(super) async fn send_queue_errors(
119115
mut errors: tokio::sync::broadcast::Receiver<matrix_sdk::send_queue::SendQueueRoomError>,120116
online: watch::Receiver<ConnectionState>,121117
) {122
- let mut retries = JoinSet::new();118
+ let mut retries = TaskSet::new();123119
loop {124120
let error = tokio::select! {125121
error = errors.recv() => error,@@ -130,7 +126,7 @@ pub(super) async fn send_queue_errors(
130126
let client = client.clone();131127
let online = online.clone();132128
retries.spawn(async move {133
- tokio::time::sleep(SEND_QUEUE_RETRY).await;129
+ matrix_sdk::sleep::sleep(SEND_QUEUE_RETRY).await;134130
if *online.borrow() == ConnectionState::Online135131
&& let Some(room) = client.get_room(&error.room_id)136132
{@@ -1,7 +1,7 @@
11
use std::sync::Arc;22
33
use matrix_sdk::{4
- HttpError, RefreshTokenError,4
+ HttpError, RefreshTokenError, SendOutsideWasm, SyncOutsideWasm,55
authentication::oauth::error::{66
OAuthAuthorizationCodeError, OAuthDiscoveryError, OAuthError, OAuthTokenRevocationError,77
RequestTokenError,@@ -17,6 +17,12 @@ use serde::{Deserialize, Serialize};
1717
1818
use crate::InteractiveAuthChallenge;1919
20
+/// the sdk's `SendOutsideWasm` isn't an auto trait, so it can't go in a trait object21
+#[cfg(not(target_family = "wasm"))]22
+type SourceError = dyn std::error::Error + Send + Sync;23
+#[cfg(target_family = "wasm")]24
+type SourceError = dyn std::error::Error;25
+2026
/// the one thing that goes wrong, for commands and queries alike. match on `kind`, show `message`2127
#[derive(Clone, Debug, thiserror::Error)]2228
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]@@ -29,7 +35,7 @@ pub struct Error {
2935
pub challenge: Option<Box<InteractiveAuthChallenge>>,3036
#[source]3137
#[cfg_attr(feature = "serde", serde(skip))]32
- source: Option<Arc<dyn std::error::Error + Send + Sync>>,38
+ source: Option<Arc<SourceError>>,3339
}3440
3541
#[derive(Clone, Copy, Debug, Eq, PartialEq)]@@ -75,7 +81,7 @@ impl Error {
7581
pub(crate) fn with_source(7682
kind: ErrorKind,7783
message: impl Into<String>,78
- source: impl std::error::Error + Send + Sync + 'static,84
+ source: impl std::error::Error + SendOutsideWasm + SyncOutsideWasm + 'static,7985
) -> Self {8086
Self {8187
source: Some(Arc::new(source)),