Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 4dfe172

Browse files
authoredMay 21, 2024··
websocket: use linkedlist insteadof Set
1 parent af3379f commit 4dfe172

File tree

1 file changed

+85
-44
lines changed

1 file changed

+85
-44
lines changed
 

‎lib/web/websocket/sender.js

+85-44
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,34 @@
33
const { WebsocketFrameSend } = require('./frame')
44
const { opcodes, sendHints } = require('./constants')
55

6-
/** @type {Uint8Array} */
6+
/** @type {typeof Uint8Array} */
77
const FastBuffer = Buffer[Symbol.species]
88

9-
class SendQueue {
10-
#queued = new Set()
11-
#size = 0
9+
/**
10+
* @typedef {object} SendQueueNode
11+
* @property {SendQueueNode | null} next
12+
* @property {Promise<void> | null} promise
13+
* @property {((...args: any[]) => any)} callback
14+
* @property {Buffer | null} frame
15+
* @property {boolean} resolved
16+
*/
1217

13-
/** @type {import('net').Socket} */
18+
class SendQueue {
19+
/**
20+
* @type {SendQueueNode | null}
21+
*/
22+
#head = null
23+
/**
24+
* @type {SendQueueNode | null}
25+
*/
26+
#tail = null
27+
28+
/**
29+
* @type {boolean}
30+
*/
31+
#running = false
32+
33+
/** @type {import('node:net').Socket} */
1434
#socket
1535

1636
constructor (socket) {
@@ -19,66 +39,87 @@ class SendQueue {
1939

2040
add (item, cb, hint) {
2141
if (hint !== sendHints.blob) {
22-
const data = clone(item, hint)
23-
24-
if (this.#size === 0) {
25-
this.#dispatch(data, cb, hint)
42+
const frame = createFrame(item, hint)
43+
if (!this.#running) {
44+
// fast-path
45+
this.#socket.write(frame, cb)
2646
} else {
27-
this.#queued.add([data, cb, true, hint])
28-
this.#size++
29-
30-
this.#run()
47+
/** @type {SendQueueNode} */
48+
const node = {
49+
next: null,
50+
promise: null,
51+
callback: cb,
52+
frame,
53+
resolved: true
54+
}
55+
if (this.#tail !== null) {
56+
this.#tail.next = node
57+
}
58+
this.#tail = node
3159
}
32-
3360
return
3461
}
3562

36-
const promise = item.arrayBuffer()
37-
const queue = [null, cb, false, hint]
38-
promise.then((ab) => {
39-
queue[0] = clone(ab, hint)
40-
queue[2] = true
41-
42-
this.#run()
43-
})
44-
45-
this.#queued.add(queue)
46-
this.#size++
47-
}
48-
49-
#run () {
50-
for (const queued of this.#queued) {
51-
const [data, cb, done, hint] = queued
63+
/** @type {SendQueueNode} */
64+
const node = {
65+
next: null,
66+
promise: item.arrayBuffer().then((ab) => {
67+
node.resolved = true
68+
node.frame = createFrame(ab, hint)
69+
}),
70+
callback: cb,
71+
frame: null,
72+
resolved: false
73+
}
5274

53-
if (!done) return
75+
if (this.#tail === null) {
76+
this.#tail = node
77+
}
5478

55-
this.#queued.delete(queued)
56-
this.#size--
79+
if (this.#head === null) {
80+
this.#head = node
81+
}
5782

58-
this.#dispatch(data, cb, hint)
83+
if (!this.#running) {
84+
this.#run()
5985
}
6086
}
6187

62-
#dispatch (data, cb, hint) {
63-
const frame = new WebsocketFrameSend()
64-
const opcode = hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY
65-
66-
frame.frameData = data
67-
const buffer = frame.createFrame(opcode)
68-
69-
this.#socket.write(buffer, cb)
88+
async #run () {
89+
this.#running = true
90+
/** @type {SendQueueNode | null} */
91+
let node = this.#head
92+
while (node !== null) {
93+
// wait pending promise
94+
if (!node.resolved) {
95+
await node.promise
96+
}
97+
// write
98+
this.#socket.write(node.frame, node.callback)
99+
// cleanup
100+
node.callback = node.frame = node.promise = null
101+
// set next
102+
node = node.next
103+
}
104+
this.#head = null
105+
this.#tail = null
106+
this.#running = false
70107
}
71108
}
72109

73-
function clone (data, hint) {
110+
function createFrame (data, hint) {
111+
return new WebsocketFrameSend(toBuffer(data, hint)).createFrame(hint === sendHints.string ? opcodes.TEXT : opcodes.BINARY)
112+
}
113+
114+
function toBuffer (data, hint) {
74115
switch (hint) {
75116
case sendHints.string:
76117
return Buffer.from(data)
77118
case sendHints.arrayBuffer:
78119
case sendHints.blob:
79120
return new FastBuffer(data)
80121
case sendHints.typedArray:
81-
return Buffer.copyBytesFrom(data)
122+
return new FastBuffer(data.buffer, data.byteOffset, data.byteLength)
82123
}
83124
}
84125

0 commit comments

Comments
 (0)
Please sign in to comment.