Repository navigation
Expand file tree
/
Copy pathDaemonLogOutputWorker.ts
More file actions
92 lines (82 loc) · 2.63 KB
/
Copy pathDaemonLogOutputWorker.ts
File metadata and controls
92 lines (82 loc) · 2.63 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license.
// See LICENSE in the project root for license information.
import { write } from 'node:fs';
import { LOG_OUTPUT_FD, MAX_LOG_OUTPUT_BYTES, type LogOutputResponse } from './DaemonLogOutputProtocol';
let writing: boolean = false;
let finishing: boolean = false;
function terminate(): void {
// This process owns only a duplicate output descriptor. A blocked filesystem write cannot be
// cancelled by JavaScript; terminating this isolated writer also handles an abruptly lost parent.
process.kill(process.pid, 'SIGKILL');
}
function send(message: LogOutputResponse, completed?: () => void): void {
if (!process.send || !process.connected) {
terminate();
return;
}
process.send(message, (error: Error | null) => {
if (error) terminate();
else completed?.();
});
}
function fail(error: Error & { code?: string }): void {
if (finishing) return;
finishing = true;
process.exitCode = 1;
send({ kind: 'error', message: error.message, code: error.code }, () => {
if (writing) terminate();
else process.disconnect!();
});
}
async function writeAllAsync(bytes: Uint8Array): Promise<void> {
let offset: number = 0;
while (offset < bytes.byteLength) {
const count: number = await new Promise((resolve, reject) => {
write(LOG_OUTPUT_FD, bytes, offset, bytes.byteLength - offset, null, (error, written) => {
if (error) reject(error);
else resolve(written);
});
});
if (count <= 0) throw new Error('Daemon log output made no write progress.');
offset += count;
}
}
process.once('disconnect', () => {
if (!finishing || writing) terminate();
});
process.on('message', (message: unknown) => {
if (finishing) return;
if (typeof message !== 'object' || message === null || !('kind' in message)) {
fail(new Error('Invalid daemon log output request.'));
return;
}
if (message.kind === 'end' && !writing) {
finishing = true;
process.exitCode = 0;
process.disconnect!();
return;
}
if (
message.kind !== 'write' ||
writing ||
!('bytes' in message) ||
!(message.bytes instanceof Uint8Array) ||
message.bytes.byteLength > MAX_LOG_OUTPUT_BYTES
) {
fail(new Error('Daemon log output requires one bounded write at a time.'));
return;
}
const bytes: Uint8Array = message.bytes;
writing = true;
void writeAllAsync(bytes).then(
() => {
writing = false;
if (!finishing) send({ kind: 'written', byteLength: bytes.byteLength });
},
(error: Error) => {
writing = false;
fail(error);
}
);
});
send({ kind: 'ready' });