Bug 1991950 - Hold onto buffer while IO is pending a=RyanVM

Original Revision: https://phabricator.services.mozilla.com/D267719

Differential Revision: https://phabricator.services.mozilla.com/D267842
This commit is contained in:
Rob Wu
2025-10-09 12:08:53 +00:00
committed by rvandermeulen@mozilla.com
parent 7986b34f92
commit 3bca077f3b
2 changed files with 41 additions and 2 deletions

View File

@@ -88,6 +88,7 @@ Object.assign(win32, {
ERROR_BROKEN_PIPE: 109, ERROR_BROKEN_PIPE: 109,
ERROR_INSUFFICIENT_BUFFER: 122, ERROR_INSUFFICIENT_BUFFER: 122,
ERROR_ABANDONED_WAIT_0: 735, ERROR_ABANDONED_WAIT_0: 735,
ERROR_IO_INCOMPLETE: 996,
ERROR_IO_PENDING: 997, ERROR_IO_PENDING: 997,
FILE_ATTRIBUTE_NORMAL: 0x00000080, FILE_ATTRIBUTE_NORMAL: 0x00000080,
@@ -229,6 +230,8 @@ var libc = new Library("libc", LIBC_CHOICES, {
win32.HANDLE /* hProcess */, win32.HANDLE /* hProcess */,
], ],
CancelIo: [win32.WINAPI, win32.BOOL, win32.HANDLE /* hFile */],
CloseHandle: [win32.WINAPI, win32.BOOL, win32.HANDLE /* hObject */], CloseHandle: [win32.WINAPI, win32.BOOL, win32.HANDLE /* hObject */],
CreateFileW: [ CreateFileW: [

View File

@@ -105,11 +105,20 @@ class Pipe extends BasePipe {
debug(`Failed to associate IOCP: ${ctypes.winLastError}`); debug(`Failed to associate IOCP: ${ctypes.winLastError}`);
} }
// this.buffer is set to an ArrayBuffer, which should not be reused nor
// released until the IO methods (ReadFile or WriteFile) have acknowledged
// completion, or reported an error other than ERROR_IO_PENDING.
this.buffer = null; this.buffer = null;
// Whether this.buffer is part of a pending IO operation.
this.bufferIsPendingIO = false;
// When close(force = true) is called while IO is pending, we notify
// read()/write() callers of completion but internally we await a IOCP
// message for this pipe before closing the pipe for real.
this.awaitingBufferClose = false;
} }
hasPendingIO() { hasPendingIO() {
return !!this.pending.length; return !!this.pending.length || this.bufferIsPendingIO;
} }
maybeClose() {} maybeClose() {}
@@ -138,6 +147,16 @@ class Pipe extends BasePipe {
} }
this.pending.length = 0; this.pending.length = 0;
if ((this.bufferIsPendingIO &&= this.#checkIfBufferIsStillPendingIO())) {
// We cannot release the pipe (specifically this.buffer) until the
// pending ReadFile/WriteFile operation on the buffer completed.
this.awaitingBufferClose = true;
let ok = libc.CancelIo(this.handle);
if (!ok) {
debug(`Pipe ${this.id}: Failed to cancel I/O: ${ctypes.winLastError}`);
}
return this.closedPromise;
}
this.buffer = null; this.buffer = null;
if (!this.closed) { if (!this.closed) {
@@ -161,6 +180,18 @@ class Pipe extends BasePipe {
onError() { onError() {
this.close(true); this.close(true);
} }
#checkIfBufferIsStillPendingIO() {
let numberOfBytesTransferred = win32.DWORD();
let ok = libc.GetOverlappedResult(
this.handle,
this.overlapped.address(),
numberOfBytesTransferred.address(),
false
);
// Ok or error other than ERROR_IO_INCOMPLETE means that I/O completed.
return !ok && ctypes.winLastError === win32.ERROR_IO_INCOMPLETE;
}
} }
class InputPipe extends Pipe { class InputPipe extends Pipe {
@@ -226,6 +257,7 @@ class InputPipe extends Pipe {
*/ */
readBuffer(count) { readBuffer(count) {
this.buffer = new ArrayBuffer(count); this.buffer = new ArrayBuffer(count);
this.bufferIsPendingIO = true;
let ok = libc.ReadFile( let ok = libc.ReadFile(
this.handle, this.handle,
@@ -239,6 +271,7 @@ class InputPipe extends Pipe {
!ok && !ok &&
(!this.process.handle || ctypes.winLastError !== win32.ERROR_IO_PENDING) (!this.process.handle || ctypes.winLastError !== win32.ERROR_IO_PENDING)
) { ) {
this.bufferIsPendingIO = ctypes.winLastError === win32.ERROR_IO_PENDING;
this.onError(); this.onError();
} else { } else {
io.updatePollEvents(); io.updatePollEvents();
@@ -326,6 +359,7 @@ class OutputPipe extends Pipe {
*/ */
writeBuffer(buffer) { writeBuffer(buffer) {
this.buffer = buffer; this.buffer = buffer;
this.bufferIsPendingIO = true;
let ok = libc.WriteFile( let ok = libc.WriteFile(
this.handle, this.handle,
@@ -336,6 +370,7 @@ class OutputPipe extends Pipe {
); );
if (!ok && ctypes.winLastError !== win32.ERROR_IO_PENDING) { if (!ok && ctypes.winLastError !== win32.ERROR_IO_PENDING) {
this.bufferIsPendingIO = false;
this.onError(); this.onError();
} else { } else {
io.updatePollEvents(); io.updatePollEvents();
@@ -849,7 +884,8 @@ io = {
debug(`IOCP notification for unknown pipe: ${pipeId}`); debug(`IOCP notification for unknown pipe: ${pipeId}`);
continue; continue;
} }
if (deqWinErr === win32.ERROR_BROKEN_PIPE) { pipe.bufferIsPendingIO = false;
if (deqWinErr === win32.ERROR_BROKEN_PIPE || pipe.awaitingBufferClose) {
pipe.onError(); pipe.onError();
continue; continue;
} }