Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions news/changelog-1.6.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ All changes included in 1.6:

- ([#11135](https://github.com/quarto-dev/quarto-cli/issues/11135)): Use `--headless=old` mode for Chromium to avoid recent issues with the new `--headless` mode. Setting `--headless=new` can be configured with `QUARTO_CHROMIUM_HEADLESS_MODE=new` environment variable, however it is not recommended new headless mode seems to be unstable. Only use to be unblocked of a situation (like `QUARTO_CHROMIUM_HEADLESS_MODE="none"` if you use an old chrome version somehow that don't support `--headless=old`).
- ([#10170](https://github.com/quarto-dev/quarto-cli/issues/10170)): Quarto should find chrome executable automatically on most OS. If this is does not find it, or a specific version is needed, set `QUARTO_CHROMIUM` environment variable to the executable path.
- Quarto now makes sure that all started chromium instances are closed when the process ends, no matter how it ends (success, error, or interruption).

## Other Fixes and Improvements

Expand Down
15 changes: 14 additions & 1 deletion src/core/cri/cri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { InternalError } from "../lib/error.ts";
import { getenv } from "../env.ts";
import { kRenderFileLifetime } from "../../config/constants.ts";
import { debug } from "../../deno_ral/log.ts";
import {
registerForExitCleanup,
unregisterForExitCleanup,
} from "../process.ts";

async function waitForServer(port: number, timeout = 3000) {
const interval = 50;
Expand Down Expand Up @@ -101,6 +105,9 @@ export async function criClient(appPath?: string, port?: number) {
];
const browser = Deno.run({ cmd, stdout: "piped", stderr: "piped" });

// Register for cleanup inside exitWithCleanup() in case something goes wrong
const thisProcessId = registerForExitCleanup(browser);

if (!(await waitForServer(port as number))) {
let msg = "Couldn't find open server.";
// Printing more error information if chrome process errored
Expand All @@ -121,7 +128,13 @@ export async function criClient(appPath?: string, port?: number) {
const result = {
close: async () => {
await client.close();
browser.close();
// FIXME: 2024-10
// We have a bug where `client.close()` doesn't return properly and we don't go below
// meaning the `browser` process is not killed here, and it will be handled in exitWithCleanup().

browser.kill(); // Chromium headless won't terminate on its own, so we need to send kill signal
browser.close(); // Closing the browser Deno process
unregisterForExitCleanup(thisProcessId); // All went well so not need to cleanup on quarto exit
},

rawClient: () => client,
Expand Down
17 changes: 13 additions & 4 deletions src/core/process.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,16 @@ const processList = new Map<number, Deno.Process>();
let processCount = 0;
let cleanupRegistered = false;

export function registerForExitCleanup(process: Deno.Process) {
const thisProcessId = ++processCount; // don't risk repeated PIDs
processList.set(thisProcessId, process);
return thisProcessId;
}

export function unregisterForExitCleanup(processId: number) {
processList.delete(processId);
}

function ensureCleanup() {
if (!cleanupRegistered) {
cleanupRegistered = true;
Expand Down Expand Up @@ -61,12 +71,11 @@ export async function execProcess(
stdout: typeof (options.stdout) === "number" ? options.stdout : "piped",
stderr: typeof (options.stderr) === "number" ? options.stderr : "piped",
});
const thisProcessId = ++processCount; // don't risk repeated PIDs
processList.set(thisProcessId, process);
const thisProcessId = registerForExitCleanup(process);

if (stdin !== undefined) {
if (!process.stdin) {
processList.delete(processCount);
unregisterForExitCleanup(thisProcessId);
throw new Error("Process stdin not available");
}
// write in 4k chunks (deno observed to overflow at > 64k)
Expand Down Expand Up @@ -170,7 +179,7 @@ export async function execProcess(
// close the process
process.close();

processList.delete(thisProcessId);
unregisterForExitCleanup(thisProcessId);

debug(`[execProcess] Success: ${status.success}, code: ${status.code}`);

Expand Down
Loading