On Thu, 17 Oct 2013 10:04:11 +1100 Lex Trotman <ele...@gmail.com> wrote:
> On 17 October 2013 06:08, Thomas Martitz < > thomas.mart...@student.htw-berlin.de> wrote: > > > Am 16.10.2013 21:04, schrieb Dimitar Zhekov: > > > >> What remains, without going too deep, is a modal dialog. Annoying, of > >> course, but with the benefit that you can easily stop a bad process. > > > > I may have missed it but why have it blocking if async spawn now works? > > The current build process (on Linux) doesn't block either. > > I think Dimitar is talking about for tools use where the results of the > command are pasted to the buffer, so the existence of the buffer and the > selection should not change while the command is running. Exactly. I'm a bit sick and overworked now, so here's an incomplete patch. When sending the current selection to a custom command, please don't close or modify the document - best of all, wait for the command to complete. Everything else should work, though it's not pretty. The utils_set_up_io_channel() macro will be removed, and utils_setup_io_channel() will remain, without cond (::= G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL) and nblock (::= TRUE). I don't care about the names of the new structure and functions. Once this thing is complete and working, rename them as you see fit. -- E-gards: Jimmy
diff --git a/src/build.c b/src/build.c index 8c2a3f9..8e25075 100644 --- a/src/build.c +++ b/src/build.c @@ -65,7 +65,7 @@ /* g_spawn_async_with_pipes doesn't work on Windows */ #ifdef G_OS_WIN32 -#define SYNC_SPAWN +/*#define SYNC_SPAWN*/ #endif /* Number of editor indicators to draw - limited as this can affect performance */ diff --git a/src/tools.c b/src/tools.c index aa18f6b..97f1e60 100644 --- a/src/tools.c +++ b/src/tools.c @@ -83,6 +83,10 @@ struct cc_data GString *buffer; /* buffer holding stdout content, or NULL */ gboolean error; /* whether and error occurred */ gboolean finished; /* whether the command has finished */ + gint stdin_fd; /* command stdin to write to */ + gchar *selection; /* selection to write to stdin */ + gint written; /* number of bytes written */ + gint remaining; /* remaining bytes to write */ }; @@ -290,13 +294,20 @@ static gboolean cc_replace_sel_cb(gpointer user_data) return TRUE; } - if (! data->error && data->buffer != NULL) + if (data->remaining) + { + ui_set_statusbar(TRUE, "%s: %s: %s", G_STRFUNC, "Failed sending data to command", + g_strerror(errno)); + } + else if (! data->error && data->buffer != NULL) { /* Command completed successfully */ sci_replace_sel(data->doc->editor->sci, data->buffer->str); } if (data->buffer) g_string_free(data->buffer, TRUE); + close(data->stdin_fd); + g_free(data->selection); g_slice_free1(sizeof *data, data); return FALSE; @@ -344,6 +355,33 @@ static void cc_exit_cb(GPid child_pid, gint status, gpointer user_data) } +static gboolean cc_outfunc(gpointer user_data) +{ + struct cc_data *data = user_data; + gint wrote; + + do + { + wrote = write(data->stdin_fd, data->selection + data->written, data->remaining); + #ifdef G_OS_UNIX + if (wrote < 0 && errno != EAGAIN) + #else + if (wrote < 0 && errno != EAGAIN && errno != ENOSPC) + #endif + { + ui_set_statusbar(TRUE, "%s: %s: %s", G_STRFUNC, "Failed sending data to command", + g_strerror(errno)); + data->error = TRUE; + return FALSE; + } + data->written += wrote; + data->remaining -= wrote; + } while (wrote > 0 && data->remaining > 0); + + return data->remaining > 0; +} + + /* Executes command (which should include all necessary command line args) and passes the current * selection through the standard input of command. The whole output of command replaces the * current selection. */ @@ -372,8 +410,6 @@ void tools_execute_custom_command(GeanyDocument *doc, const gchar *command) if (g_spawn_async_with_pipes(NULL, argv, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &stdin_fd, &stdout_fd, &stderr_fd, &error)) { - gchar *sel; - gint remaining, wrote; struct cc_data *data = g_slice_alloc(sizeof *data); data->error = FALSE; @@ -381,34 +417,25 @@ void tools_execute_custom_command(GeanyDocument *doc, const gchar *command) data->buffer = NULL; data->doc = doc; data->command = command; + data->stdin_fd = stdin_fd; + data->selection = sci_get_selection_contents(doc->editor->sci); + data->written = 0; + data->remaining = strlen(data->selection); g_child_watch_add(pid, cc_exit_cb, data); /* use GIOChannel to monitor stdout */ utils_set_up_io_channel(stdout_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - FALSE, cc_iofunc, data); + TRUE, cc_iofunc, data); /* copy program's stderr to Geany's stdout to help error tracking */ utils_set_up_io_channel(stderr_fd, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, - FALSE, cc_iofunc_err, data); - - /* get selection */ - sel = sci_get_selection_contents(doc->editor->sci); - + TRUE, cc_iofunc_err, data); /* write data to the command */ - remaining = strlen(sel); - do + if (data->remaining > 0 && cc_outfunc(data)) { - wrote = write(stdin_fd, sel, remaining); - if (G_UNLIKELY(wrote < 0)) - { - g_warning("%s: %s: %s\n", G_STRFUNC, "Failed sending data to command", - g_strerror(errno)); - break; - } - remaining -= wrote; - } while (remaining > 0); - close(stdin_fd); - g_free(sel); + utils_set_pipe_nonblock(stdin_fd); + g_timeout_add(50, cc_outfunc, data); + } } else { diff --git a/src/utils.c b/src/utils.c index a2c3526..35a188b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -40,6 +40,12 @@ # include <sys/types.h> #endif +#ifdef G_OS_UNIX +# include <fcntl.h> +#else +# include <windows.h> +#endif + #include <glib/gstdio.h> #include <gio/gio.h> @@ -1028,21 +1034,83 @@ gchar *utils_get_current_time_string(void) } -GIOChannel *utils_set_up_io_channel( - gint fd, GIOCondition cond, gboolean nblock, GIOFunc func, gpointer data) +void utils_set_pipe_nonblock(gint fd) +{ +#ifdef G_OS_UNIX + int state = fcntl(fd, F_GETFL); + + if (state != -1) + fcntl(fd, F_SETFL, state | O_NONBLOCK); +#else + HANDLE h = (HANDLE) _get_osfhandle(fd); + DWORD state; + + if (h != INVALID_HANDLE_VALUE && GetNamedPipeHandleState(h, &state, NULL, NULL, NULL, NULL, 0)) + { + state |= PIPE_NOWAIT; + SetNamedPipeHandleState(h, &state, NULL, NULL); + } +#endif +} + + +#ifdef G_OS_WIN32 +typedef struct _poll_source +{ + GSource source; + int fd; + GIOChannel *ioc; + GIOFunc func; + gpointer data; + gboolean valid; +} poll_source; + +static gboolean poll_source_prepare(G_GNUC_UNUSED GSource *source, gint *timeout) +{ + *timeout = -1; + return FALSE; +} + +static gboolean poll_source_check(poll_source *source) +{ + DWORD available; + + source->valid = PeekNamedPipe((HANDLE) _get_osfhandle(source->fd), NULL, 0, NULL, &available, + NULL); + return !source->valid || available; +} + +static gboolean poll_source_dispatch(poll_source *source, GSourceFunc callback, gpointer gdata) +{ + source->func(source->ioc, G_IO_IN | (source->valid ? 0 : G_IO_ERR), source->data); + return source->valid; +} + +static void poll_source_finalize(poll_source *source) +{ + g_io_channel_unref(source->ioc); +} + +static GSourceFuncs poll_source_funcs = { poll_source_prepare, + (gboolean (*)(GSource *)) poll_source_check, + (gboolean (*)(GSource *, GSourceFunc, gpointer)) poll_source_dispatch, + (void (*)(GSource *)) poll_source_finalize, NULL, NULL }; +#endif + + +GIOChannel *utils_setup_io_channel(gint fd, GIOFunc func, gpointer data) { GIOChannel *ioc; /*const gchar *encoding;*/ #ifdef G_OS_WIN32 ioc = g_io_channel_win32_new_fd(fd); + utils_set_pipe_nonblock(fd); #else ioc = g_io_channel_unix_new(fd); + g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); #endif - if (nblock) - g_io_channel_set_flags(ioc, G_IO_FLAG_NONBLOCK, NULL); - g_io_channel_set_encoding(ioc, NULL, NULL); /* if (! g_get_charset(&encoding)) @@ -1060,8 +1128,20 @@ GIOChannel *utils_set_up_io_channel( /* "auto-close" ;-) */ g_io_channel_set_close_on_unref(ioc, TRUE); - g_io_add_watch(ioc, cond, func, data); +#ifdef G_OS_WIN32 + { + poll_source *source = (poll_source *) g_source_new(&poll_source_funcs, sizeof(poll_source)); + + source->fd = fd; + source->ioc = ioc; + source->func = func; + source->data = data; + g_source_attach((GSource *) source, NULL); + } +#else + g_io_add_watch(ioc, G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL, func, data); g_io_channel_unref(ioc); +#endif return ioc; } diff --git a/src/utils.h b/src/utils.h index 4219e65..f28675b 100644 --- a/src/utils.h +++ b/src/utils.h @@ -224,8 +224,11 @@ gint utils_strtod(const gchar *source, gchar **end, gboolean with_route); gchar *utils_get_current_time_string(void); -GIOChannel *utils_set_up_io_channel(gint fd, GIOCondition cond, gboolean nblock, - GIOFunc func, gpointer data); +void utils_set_pipe_nonblock(gint fd); + +GIOChannel *utils_setup_io_channel(gint fd, GIOFunc func, gpointer data); +#define utils_set_up_io_channel(fd, cond, nblock, func, data) \ + utils_setup_io_channel(fd, func, data) gchar **utils_read_file_in_array(const gchar *filename);
_______________________________________________ Devel mailing list Devel@lists.geany.org https://lists.geany.org/cgi-bin/mailman/listinfo/devel