fix: drain stdout pipe to prevent project listing deadlock (#106)
The list_gcloud_projects() function piped stdout from `gcloud projects list` but only read it after the child process exited. When a user has enough GCP projects that the output exceeds the OS pipe buffer (~64KB), gcloud blocks on write, the parent blocks waiting for exit, and neither side makes progress — hitting the 10s timeout. Spawn a thread to drain stdout concurrently so the pipe buffer never fills up while the main thread polls for process completion. Fixes googleworkspace/cli#96
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
---
|
||||
"@googleworkspace/cli": patch
|
||||
---
|
||||
|
||||
fix: drain stdout pipe to prevent project listing timeout during auth setup
|
||||
|
||||
Fixed `gws auth setup` timing out at step 3 (GCP project selection) for users
|
||||
with many projects. The `gcloud projects list` stdout pipe was only read after
|
||||
the child process exited, causing a deadlock when output exceeded the OS pipe
|
||||
buffer (~64 KB). Stdout is now drained in a background thread to prevent the
|
||||
pipe from filling up.
|
||||
+11
-9
@@ -545,6 +545,16 @@ fn list_gcloud_projects() -> (Vec<(String, String)>, Option<String>) {
|
||||
Err(e) => return (Vec::new(), Some(format!("Failed to run gcloud: {e}"))),
|
||||
};
|
||||
|
||||
// Drain stdout in a background thread to prevent pipe buffer deadlock.
|
||||
// Without this, gcloud blocks once the OS pipe buffer (~64 KB) fills up,
|
||||
// and the parent blocks waiting for gcloud to exit — a classic deadlock.
|
||||
let stdout = child.stdout.take().expect("stdout was piped");
|
||||
let reader_handle = std::thread::spawn(move || {
|
||||
let mut buf = String::new();
|
||||
std::io::Read::read_to_string(&mut { stdout }, &mut buf).ok();
|
||||
buf
|
||||
});
|
||||
|
||||
// Wait with timeout
|
||||
let timeout = std::time::Duration::from_secs(10);
|
||||
let start = std::time::Instant::now();
|
||||
@@ -552,15 +562,7 @@ fn list_gcloud_projects() -> (Vec<(String, String)>, Option<String>) {
|
||||
match child.try_wait() {
|
||||
Ok(Some(status)) => {
|
||||
if status.success() {
|
||||
let stdout = child
|
||||
.stdout
|
||||
.take()
|
||||
.map(|mut s| {
|
||||
let mut buf = String::new();
|
||||
std::io::Read::read_to_string(&mut s, &mut buf).ok();
|
||||
buf
|
||||
})
|
||||
.unwrap_or_default();
|
||||
let stdout = reader_handle.join().unwrap_or_default();
|
||||
let projects = stdout
|
||||
.lines()
|
||||
.filter_map(|line| {
|
||||
|
||||
Reference in New Issue
Block a user