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.
This commit is contained in:
commit
29d14b1c99
77
src/spawn.c
77
src/spawn.c
@ -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)
|
||||
|
||||
static void spawn_destroy_common(SpawnChannelData *sc)
|
||||
{
|
||||
SpawnChannelData *sc = (SpawnChannelData *) data;
|
||||
|
||||
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,6 +894,16 @@ 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;
|
||||
@ -878,17 +911,19 @@ static gboolean spawn_read_cb(GIOChannel *channel, GIOCondition condition, gpoin
|
||||
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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user