From e4740d998aff541a9e803732bf0722623a334fa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Mon, 9 Jan 2012 16:34:16 +0100 Subject: [PATCH spice-gtk] Make g_coroutine_condition_wait() cancellable https://bugs.freedesktop.org/show_bug.cgi?id=44570 --- gtk/channel-base.c | 3 ++- gtk/decode-glz.c | 2 +- gtk/gio-coroutine.c | 50 +++++++++++++++++++++++++++++++++++++++----------- gtk/gio-coroutine.h | 9 ++++++--- gtk/spice-channel.c | 2 +- 5 files changed, 49 insertions(+), 17 deletions(-) diff --git a/gtk/channel-base.c b/gtk/channel-base.c index 54232ef..39b4a08 100644 --- a/gtk/channel-base.c +++ b/gtk/channel-base.c @@ -116,6 +116,7 @@ static gboolean wait_for_channel(gpointer data) G_GNUC_INTERNAL void spice_channel_handle_wait_for_channels(SpiceChannel *channel, SpiceMsgIn *in) { + SpiceChannelPrivate *c = channel->priv; SpiceMsgWaitForChannels *wfc = spice_msg_in_parsed(in); int i; @@ -128,7 +129,7 @@ void spice_channel_handle_wait_for_channels(SpiceChannel *channel, SpiceMsgIn *i }; SPICE_DEBUG("waiting for serial %lu (%d/%d)", data.wait->message_serial, i + 1, wfc->wait_count); - g_condition_wait(wait_for_channel, &data); + g_coroutine_condition_wait(&c->coroutine, wait_for_channel, &data); SPICE_DEBUG("waiting for serial %lu, done", data.wait->message_serial); } } diff --git a/gtk/decode-glz.c b/gtk/decode-glz.c index 317fa9e..377f7af 100644 --- a/gtk/decode-glz.c +++ b/gtk/decode-glz.c @@ -153,7 +153,7 @@ static void *glz_decoder_window_bits(SpiceGlzDecoderWindow *w, uint64_t id, .id = id - dist, }; - g_condition_wait(wait_for_image, &data); + g_coroutine_condition_wait(g_coroutine_self(), wait_for_image, &data); slot = (id - dist) % w->nimages; } diff --git a/gtk/gio-coroutine.c b/gtk/gio-coroutine.c index 4816cdd..db944cb 100644 --- a/gtk/gio-coroutine.c +++ b/gtk/gio-coroutine.c @@ -23,12 +23,17 @@ typedef struct _GConditionWaitSource { + GCoroutine *self; GSource src; - struct coroutine *co; GConditionWaitFunc func; gpointer data; } GConditionWaitSource; +GCoroutine* g_coroutine_self(void) +{ + return (GCoroutine*)coroutine_self(); +} + /* Main loop helper functions */ static gboolean g_io_wait_helper(GSocket *sock G_GNUC_UNUSED, GIOCondition cond, @@ -54,14 +59,12 @@ GIOCondition g_coroutine_socket_wait(GCoroutine *self, g_source_set_callback(src, (GSourceFunc)g_io_wait_helper, self, NULL); self->source_id = g_source_attach(src, NULL); ret = coroutine_yield(NULL); + self->source_id = 0; g_source_unref(src); if (ret != NULL) val = *ret; - else - g_source_remove(self->source_id); - self->source_id = 0; return val; } @@ -69,8 +72,11 @@ void g_coroutine_wakeup(GCoroutine *self) { g_return_if_fail(self != NULL); - if (self->source_id != 0) + if (self->source_id != 0) { + g_source_remove(self->source_id); + self->source_id = 0; coroutine_yieldto(&self->coroutine, NULL); + } } @@ -114,15 +120,32 @@ static gboolean g_condition_wait_helper(gpointer data) return FALSE; } -gboolean g_condition_wait(GConditionWaitFunc func, gpointer data) +/* + * g_coroutine_condition_wait: + * @coroutine: the coroutine to wait on + * @func: the condition callback + * @data: the user data passed to @func callback + * + * This function will wait on caller coroutine until @func returns %TRUE. + * + * @func is called when entering the main loop from the main context (coroutine). + * + * The condition can be cancelled by calling g_coroutine_wakeup() + * + * Returns: %TRUE if condition reached, %FALSE if not and cancelled + */ +gboolean g_coroutine_condition_wait(GCoroutine *self, GConditionWaitFunc func, gpointer data) { GSource *src; GConditionWaitSource *vsrc; + g_return_val_if_fail(self != NULL, FALSE); + g_return_val_if_fail(self->source_id == 0, FALSE); + g_return_val_if_fail(func != NULL, FALSE); + /* Short-circuit check in case we've got it ahead of time */ - if (func(data)) { + if (func(data)) return TRUE; - } /* * Don't have it, so yield to the main loop, checking the condition @@ -133,13 +156,18 @@ gboolean g_condition_wait(GConditionWaitFunc func, gpointer data) vsrc->func = func; vsrc->data = data; - vsrc->co = coroutine_self(); + vsrc->self = self; - g_source_attach(src, NULL); - g_source_set_callback(src, g_condition_wait_helper, coroutine_self(), NULL); + self->source_id = g_source_attach(src, NULL); + g_source_set_callback(src, g_condition_wait_helper, self, NULL); coroutine_yield(NULL); g_source_unref(src); + /* it got woked up / cancelled? */ + if (self->source_id == 0) + return func(data); + + self->source_id = 0; return TRUE; } diff --git a/gtk/gio-coroutine.h b/gtk/gio-coroutine.h index b42e327..5038448 100644 --- a/gtk/gio-coroutine.h +++ b/gtk/gio-coroutine.h @@ -47,10 +47,13 @@ typedef gboolean (*GConditionWaitFunc)(gpointer); typedef void (*GSignalEmitMainFunc)(GObject *object, int signum, gpointer params); -void g_coroutine_wakeup (GCoroutine *coroutine); -GIOCondition g_coroutine_socket_wait(GCoroutine *coroutine, GSocket *sock, GIOCondition cond); +GCoroutine* g_coroutine_self (void); +void g_coroutine_wakeup (GCoroutine *coroutine); +GIOCondition g_coroutine_socket_wait (GCoroutine *coroutine, + GSocket *sock, GIOCondition cond); +gboolean g_coroutine_condition_wait (GCoroutine *coroutine, + GConditionWaitFunc func, gpointer data); -gboolean g_condition_wait (GConditionWaitFunc func, gpointer data); void g_signal_emit_main_context(GObject *object, GSignalEmitMainFunc func, int signum, gpointer params, const char *debug_info); void g_object_notify_main_context(GObject *object, const gchar *property_name); diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index e4b4d03..1dadcf2 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -1869,7 +1869,7 @@ static gboolean spice_channel_iterate(SpiceChannel *channel) do { /* freeze coroutine */ if (c->state == SPICE_CHANNEL_STATE_MIGRATING) - g_condition_wait(wait_migration, channel); + g_coroutine_condition_wait(&c->coroutine, wait_migration, channel); if (c->has_error) { SPICE_DEBUG("channel has error, breaking loop"); -- 1.7.7.5