Skip to content

Commit e62ca4e

Browse files
authored
feat(ipc): preserve channel message order (#9070)
1 parent 6c06832 commit e62ca4e

File tree

4 files changed

+54
-7
lines changed

4 files changed

+54
-7
lines changed

.changes/preserve-channel-order.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"tauri": patch:enhance
3+
"@tauri-apps/api": patch:enhance
4+
---
5+
6+
Added a mechanism to preserve channel message order.

core/tauri/scripts/bundle.global.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/tauri/src/ipc/channel.rs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::{
66
collections::HashMap,
77
str::FromStr,
88
sync::{
9-
atomic::{AtomicU32, Ordering},
9+
atomic::{AtomicU32, AtomicUsize, Ordering},
1010
Arc, Mutex,
1111
},
1212
};
@@ -132,18 +132,26 @@ impl Channel {
132132
}
133133

134134
pub(crate) fn from_callback_fn<R: Runtime>(webview: Webview<R>, callback: CallbackFn) -> Self {
135+
let counter = AtomicUsize::new(0);
136+
135137
Channel::new_with_id(callback.0, move |body| {
136138
let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed);
139+
137140
webview
138141
.state::<ChannelDataIpcQueue>()
139142
.0
140143
.lock()
141144
.unwrap()
142145
.insert(data_id, body);
146+
147+
let i = counter.fetch_add(1, Ordering::Relaxed);
148+
143149
webview.eval(&format!(
144-
"window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then(window['_' + {}]).catch(console.error)",
150+
"window.__TAURI_INTERNALS__.invoke('{FETCH_CHANNEL_DATA_COMMAND}', null, {{ headers: {{ '{CHANNEL_ID_HEADER_NAME}': '{data_id}' }} }}).then((response) => window['_' + {}]({{ message: response, id: {i} }})).catch(console.error)",
145151
callback.0
146-
))
152+
))?;
153+
154+
Ok(())
147155
})
148156
}
149157

tooling/api/src/core.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,44 @@ class Channel<T = unknown> {
3131
#onmessage: (response: T) => void = () => {
3232
// no-op
3333
}
34+
#nextMessageId = 0
35+
#pendingMessages: Record<string, T> = {}
3436

3537
constructor() {
36-
this.id = transformCallback((response: T) => {
37-
this.#onmessage(response)
38-
})
38+
this.id = transformCallback(
39+
({ message, id }: { message: T; id: number }) => {
40+
// the id is used as a mechanism to preserve message order
41+
if (id === this.#nextMessageId) {
42+
this.#nextMessageId = id + 1
43+
this.#onmessage(message)
44+
45+
// process pending messages
46+
const pendingMessageIds = Object.keys(this.#pendingMessages)
47+
if (pendingMessageIds.length > 0) {
48+
let nextId = id + 1
49+
for (const pendingId of pendingMessageIds.sort()) {
50+
// if we have the next message, process it
51+
if (parseInt(pendingId) === nextId) {
52+
// eslint-disable-next-line security/detect-object-injection
53+
const message = this.#pendingMessages[pendingId]
54+
// eslint-disable-next-line security/detect-object-injection
55+
delete this.#pendingMessages[pendingId]
56+
57+
this.#onmessage(message)
58+
59+
// move the id counter to the next message to check
60+
nextId += 1
61+
} else {
62+
// we do not have the next message, let's wait
63+
break
64+
}
65+
}
66+
}
67+
} else {
68+
this.#pendingMessages[id.toString()] = message
69+
}
70+
}
71+
)
3972
}
4073

4174
set onmessage(handler: (response: T) => void) {

0 commit comments

Comments
 (0)