[glib: 1/2] Ensure g_subprocess_communicate_async() never blocks
- From: Sebastian Dröge <sdroege src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib: 1/2] Ensure g_subprocess_communicate_async() never blocks
- Date: Mon, 17 Aug 2020 11:20:17 +0000 (UTC)
commit 4cf95e390449bfc2ecbbefdf6a9317b634114c0a
Author: Alexander Larsson <alexl redhat com>
Date: Mon Aug 17 12:25:34 2020 +0200
Ensure g_subprocess_communicate_async() never blocks
It turns out that our async write operation implementation is broken
on non-O_NONBLOCK pipes, because the default async write
implementation calls write() after poll() said there were some
space. However, the semantics of pipes is that unless O_NONBLOCK is set
then the write *will* block if the passed in write count is larger than
the available space.
This caused a deadlock in https://gitlab.gnome.org/GNOME/glib/-/issues/2182
due to the loop-back of the app stdout to the parent, but even without such
a deadlock it is a problem that we may block the mainloop at all.
In the particular case of g_subprocess_communicate() we have full
control of the pipes after starting the app, so it is safe to enable
O_NONBLOCK (i.e. we can ensure all the code using the fd after this can handle
non-blocking mode).
This fixes https://gitlab.gnome.org/GNOME/glib/-/issues/2182
gio/gsubprocess.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
---
diff --git a/gio/gsubprocess.c b/gio/gsubprocess.c
index b5515c705..8cc935423 100644
--- a/gio/gsubprocess.c
+++ b/gio/gsubprocess.c
@@ -1595,6 +1595,23 @@ g_subprocess_communicate_internal (GSubprocess *subprocess,
if (subprocess->stdin_pipe)
{
g_assert (stdin_buf != NULL);
+
+#ifdef G_OS_UNIX
+ /* We're doing async writes to the pipe, and the async write mechanism assumes
+ * that streams polling as writable do SOME progress (possibly partial) and then
+ * stop, but never block.
+ *
+ * However, for blocking pipes, unix will return writable if there is *any* space left
+ * but still block until the full buffer size is available before returning from write.
+ * So, to avoid async blocking on the main loop we make this non-blocking here.
+ *
+ * It should be safe to change the fd because we're the only user at this point as
+ * per the g_subprocess_communicate() docs, and all the code called by this function
+ * properly handles non-blocking fds.
+ */
+ g_unix_set_fd_nonblocking (g_unix_output_stream_get_fd (G_UNIX_OUTPUT_STREAM
(subprocess->stdin_pipe)), TRUE, NULL);
+#endif
+
state->stdin_buf = g_memory_input_stream_new_from_bytes (stdin_buf);
g_output_stream_splice_async (subprocess->stdin_pipe, (GInputStream*)state->stdin_buf,
G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE |
G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]