[geany/geany] 29d14b: Merge pull request #1461 from zhekov/master

Colomban Wendling git-noreply at xxxxx
Sat Mar 10 00:53:00 UTC 2018


Branch:      refs/heads/master
Author:      Colomban Wendling <ban at herbesfolles.org>
Committer:   Colomban Wendling <ban at herbesfolles.org>
Date:        Sat, 10 Mar 2018 00:53:00 UTC
Commit:      29d14b1c9934189018374a2ce24ab269c10e0239
             https://github.com/geany/geany/commit/29d14b1c9934189018374a2ce24ab269c10e0239

Log Message:
-----------
Merge pull request #1461 from zhekov/master

Handle continuous G_IO_IN-s without any data.

This fixes spawn clients using recursive sources, like the Scope
plugin.


Modified Paths:
--------------
    src/spawn.c

Modified: src/spawn.c
77 lines changed, 68 insertions(+), 9 deletions(-)
===================================================================
@@ -842,15 +842,16 @@ typedef struct _SpawnChannelData
 	GString *buffer;       /* NULL if recursive */
 	GString *line_buffer;  /* NULL if char buffered */
 	gsize max_length;
+	/* stdout/stderr: fix continuous empty G_IO_IN-s for recursive channels */
+	guint empty_gio_ins;
 } SpawnChannelData;
 
+#define SPAWN_CHANNEL_GIO_WATCH(sc) ((sc)->empty_gio_ins < 200)
 
-static void spawn_destroy_cb(gpointer data)
-{
-	SpawnChannelData *sc = (SpawnChannelData *) data;
 
+static void spawn_destroy_common(SpawnChannelData *sc)
+{
 	g_io_channel_shutdown(sc->channel, FALSE, NULL);
-	sc->channel = NULL;
 
 	if (sc->buffer)
 		g_string_free(sc->buffer, TRUE);
@@ -860,6 +861,28 @@ static void spawn_destroy_cb(gpointer data)
 }
 
 
+static void spawn_timeout_destroy_cb(gpointer data)
+{
+	SpawnChannelData *sc = (SpawnChannelData *) data;
+
+	spawn_destroy_common(sc);
+	g_io_channel_unref(sc->channel);
+	sc->channel = NULL;
+}
+
+
+static void spawn_destroy_cb(gpointer data)
+{
+	SpawnChannelData *sc = (SpawnChannelData *) data;
+
+	if (SPAWN_CHANNEL_GIO_WATCH(sc))
+	{
+		spawn_destroy_common(sc);
+		sc->channel = NULL;
+	}
+}
+
+
 static gboolean spawn_write_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
 {
 	SpawnChannelData *sc = (SpawnChannelData *) data;
@@ -871,24 +894,36 @@ static gboolean spawn_write_cb(GIOChannel *channel, GIOCondition condition, gpoi
 }
 
 
+static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpointer data);
+
+static gboolean spawn_timeout_read_cb(gpointer data)
+{
+	SpawnChannelData *sc = (SpawnChannelData *) data;
+
+	/* The solution for continuous empty G_IO_IN-s is to generate them outselves. :) */
+	return spawn_read_cb(sc->channel, G_IO_IN, data);
+}
+
 static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpointer data)
 {
 	SpawnChannelData *sc = (SpawnChannelData *) data;
 	GString *line_buffer = sc->line_buffer;
 	GString *buffer = sc->buffer ? sc->buffer : g_string_sized_new(sc->max_length);
 	GIOCondition input_cond = condition & (G_IO_IN | G_IO_PRI);
 	GIOCondition failure_cond = condition & G_IO_FAILURE;
+	GIOStatus status = G_IO_STATUS_NORMAL;
 	/*
 	 * - Normally, read only once. With IO watches, our data processing must be immediate,
 	 *   which may give the child time to emit more data, and a read loop may combine it into
 	 *   large stdout and stderr portions. Under Windows, looping blocks.
 	 * - On failure, read in a loop. It won't block now, there will be no more data, and the
 	 *   IO watch is not guaranteed to be called again (under Windows this is the last call).
+	 * - When using timeout callbacks, read in a loop. Otherwise, the input processing will
+	 *   be limited to (1/0.050 * DEFAULT_IO_LENGTH) KB/sec, which is pretty low.
 	 */
 	if (input_cond)
 	{
 		gsize chars_read;
-		GIOStatus status;
 
 		if (line_buffer)
 		{
@@ -923,7 +958,7 @@ static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpoin
 					}
 				}
 
-				if (!failure_cond)
+				if (SPAWN_CHANNEL_GIO_WATCH(sc) && !failure_cond)
 					break;
 			}
 		}
@@ -936,7 +971,7 @@ static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpoin
 				/* input only, failures are reported separately below */
 				sc->cb.read(buffer, input_cond, sc->cb_data);
 
-				if (!failure_cond)
+				if (SPAWN_CHANNEL_GIO_WATCH(sc) && !failure_cond)
 					break;
 			}
 		}
@@ -967,6 +1002,25 @@ static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpoin
 
 		sc->cb.read(buffer, input_cond | failure_cond, sc->cb_data);
 	}
+	/* Check for continuous activations with G_IO_IN | G_IO_PRI, without any
+	   data to read and without errors. If detected, switch to timeout source. */
+	else if (SPAWN_CHANNEL_GIO_WATCH(sc) && status == G_IO_STATUS_AGAIN)
+	{
+		sc->empty_gio_ins++;
+
+		if (!SPAWN_CHANNEL_GIO_WATCH(sc))
+		{
+			GSource *old_source = g_main_current_source();
+			GSource *new_source = g_timeout_source_new(50);
+
+			g_io_channel_ref(sc->channel);
+			g_source_set_can_recurse(new_source, g_source_get_can_recurse(old_source));
+			g_source_set_callback(new_source, spawn_timeout_read_cb, data, spawn_timeout_destroy_cb);
+			g_source_attach(new_source, g_source_get_context(old_source));
+			g_source_unref(new_source);
+			failure_cond |= G_IO_ERR;
+		}
+	}
 
 	if (buffer != sc->buffer)
 		g_string_free(buffer, TRUE);
@@ -1003,7 +1057,7 @@ static void spawn_finalize(SpawnWatcherData *sw)
 }
 
 
-static gboolean spawn_timeout_cb(gpointer data)
+static gboolean spawn_timeout_watch_cb(gpointer data)
 {
 	SpawnWatcherData *sw = (SpawnWatcherData *) data;
 	int i;
@@ -1031,7 +1085,7 @@ static void spawn_watch_cb(GPid pid, gint status, gpointer data)
 		{
 			GSource *source = g_timeout_source_new(50);
 
-			g_source_set_callback(source, spawn_timeout_cb, data, NULL);
+			g_source_set_callback(source, spawn_timeout_watch_cb, data, NULL);
 			g_source_attach(source, sw->main_context);
 			g_source_unref(source);
 			return;
@@ -1066,6 +1120,9 @@ static void spawn_watch_cb(GPid pid, gint status, gpointer data)
  *  The default max lengths are 24K for line buffered stdout, 8K for line buffered stderr,
  *  4K for unbuffered input under Unix, and 2K for unbuffered input under Windows.
  *
+ *  Due to a bug in some glib versions, the sources for recursive stdout/stderr callbacks may
+ *  be converted from child watch to timeout(50ms). No callback changes are required.
+ *
  *  @c exit_cb is always invoked last, after all I/O callbacks.
  *
  *  The @a child_pid will be closed automatically, after @a exit_cb is invoked.
@@ -1171,6 +1228,8 @@ gboolean spawn_with_callbacks(const gchar *working_directory, const gchar *comma
 					sc->line_buffer = g_string_sized_new(sc->max_length +
 						DEFAULT_IO_LENGTH);
 				}
+
+				sc->empty_gio_ins = 0;
 			}
 
 			source = g_io_create_watch(sc->channel, condition);



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list