From 173841cb216c244580f72d61892f67f91a4c7b8a 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/channel-main.c | 10 ++++---- gtk/decode-glz.c | 2 +- gtk/gio-coroutine.c | 48 ++++++++++++++++++++++++++++++++++++++------- gtk/gio-coroutine.h | 12 +++++++--- gtk/spice-channel-priv.h | 2 +- gtk/spice-channel.c | 17 +++++++++------ 7 files changed, 67 insertions(+), 27 deletions(-) diff --git a/gtk/channel-base.c b/gtk/channel-base.c index e41b1f5..fd36696 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; @@ -129,7 +130,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/channel-main.c b/gtk/channel-main.c index 42e4494..ebf660f 100644 --- a/gtk/channel-main.c +++ b/gtk/channel-main.c @@ -1709,7 +1709,7 @@ static gboolean timer_set_display(gpointer data) c->timer_id = 0; if (c->agent_connected) spice_main_send_monitor_config(SPICE_MAIN_CHANNEL(channel)); - spice_channel_wakeup(channel); + spice_channel_wakeup(channel, FALSE); return false; } @@ -1783,7 +1783,7 @@ void spice_main_clipboard_selection_grab(SpiceMainChannel *channel, guint select g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); agent_clipboard_grab(channel, selection, types, ntypes); - spice_channel_wakeup(SPICE_CHANNEL(channel)); + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE); } /** @@ -1822,7 +1822,7 @@ void spice_main_clipboard_selection_release(SpiceMainChannel *channel, guint sel return; agent_clipboard_release(channel, selection); - spice_channel_wakeup(SPICE_CHANNEL(channel)); + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE); } /** @@ -1863,7 +1863,7 @@ void spice_main_clipboard_selection_notify(SpiceMainChannel *channel, guint sele g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); agent_clipboard_notify(channel, selection, type, data, size); - spice_channel_wakeup(SPICE_CHANNEL(channel)); + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE); } /** @@ -1899,7 +1899,7 @@ void spice_main_clipboard_selection_request(SpiceMainChannel *channel, guint sel g_return_if_fail(SPICE_IS_MAIN_CHANNEL(channel)); agent_clipboard_request(channel, selection, type); - spice_channel_wakeup(SPICE_CHANNEL(channel)); + spice_channel_wakeup(SPICE_CHANNEL(channel), FALSE); } /** 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 806dad9..355c75c 100644 --- a/gtk/gio-coroutine.c +++ b/gtk/gio-coroutine.c @@ -23,8 +23,8 @@ typedef struct _GConditionWaitSource { + GCoroutine *self; GSource src; - struct coroutine *co; GConditionWaitFunc func; gpointer data; } GConditionWaitSource; @@ -70,6 +70,17 @@ GIOCondition g_coroutine_socket_wait(GCoroutine *self, return val; } +void g_coroutine_condition_cancel(GCoroutine *coroutine) +{ + g_return_if_fail(coroutine != NULL); + + if (coroutine->condition_id == 0) + return; + + g_source_remove(coroutine->condition_id); + coroutine->condition_id = 0; +} + void g_coroutine_wakeup(GCoroutine *coroutine) { g_return_if_fail(coroutine != NULL); @@ -79,7 +90,6 @@ void g_coroutine_wakeup(GCoroutine *coroutine) coroutine_yieldto(&coroutine->coroutine, NULL); } - /* * Call immediately before the main loop does an iteration. Returns * true if the condition we're checking is ready for dispatch @@ -120,15 +130,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->condition_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 @@ -139,13 +166,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->condition_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->condition_id == 0) + return func(data); + + self->condition_id = 0; return TRUE; } diff --git a/gtk/gio-coroutine.h b/gtk/gio-coroutine.h index cb96a52..1c4094e 100644 --- a/gtk/gio-coroutine.h +++ b/gtk/gio-coroutine.h @@ -32,6 +32,7 @@ struct _GCoroutine { struct coroutine coroutine; guint wait_id; + guint condition_id; }; /* @@ -47,11 +48,14 @@ typedef gboolean (*GConditionWaitFunc)(gpointer); typedef void (*GSignalEmitMainFunc)(GObject *object, int signum, gpointer params); -GCoroutine* g_coroutine_self (void); -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); +void g_coroutine_condition_cancel(GCoroutine *coroutine); -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-priv.h b/gtk/spice-channel-priv.h index 7fa005c..ebdc5ce 100644 --- a/gtk/spice-channel-priv.h +++ b/gtk/spice-channel-priv.h @@ -154,7 +154,7 @@ uint16_t spice_header_get_msg_type(uint8_t *header, gboolean is_mini_header); uint32_t spice_header_get_msg_size(uint8_t *header, gboolean is_mini_header); void spice_channel_up(SpiceChannel *channel); -void spice_channel_wakeup(SpiceChannel *channel); +void spice_channel_wakeup(SpiceChannel *channel, gboolean cancel); SpiceSession* spice_channel_get_session(SpiceChannel *channel); diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c index 806f37c..9316f90 100644 --- a/gtk/spice-channel.c +++ b/gtk/spice-channel.c @@ -650,7 +650,7 @@ static gboolean spice_channel_idle_wakeup(gpointer user_data) { SpiceChannel *channel = SPICE_CHANNEL(user_data); - spice_channel_wakeup(channel); + spice_channel_wakeup(channel, FALSE); g_object_unref(channel); return FALSE; @@ -674,7 +674,7 @@ void spice_msg_out_send(SpiceMsgOut *out) g_timeout_add_full(G_PRIORITY_HIGH, 0, spice_channel_idle_wakeup, g_object_ref(out->channel), NULL); else - spice_channel_wakeup(out->channel); + spice_channel_wakeup(out->channel, FALSE); } /* coroutine context */ @@ -1690,11 +1690,14 @@ error: /* system context */ /* TODO: we currently flush/wakeup immediately all buffered messages */ G_GNUC_INTERNAL -void spice_channel_wakeup(SpiceChannel *channel) +void spice_channel_wakeup(SpiceChannel *channel, gboolean cancel) { - SpiceChannelPrivate *c = channel->priv; + GCoroutine *c = &channel->priv->coroutine; + + if (cancel) + g_coroutine_condition_cancel(c); - g_coroutine_wakeup(&c->coroutine); + g_coroutine_wakeup(c); } G_GNUC_INTERNAL @@ -1996,7 +1999,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"); @@ -2407,7 +2410,7 @@ void spice_channel_disconnect(SpiceChannel *channel, SpiceChannelEvent reason) if (c->state == SPICE_CHANNEL_STATE_MIGRATING) { c->state = SPICE_CHANNEL_STATE_READY; } else - spice_channel_wakeup(channel); + spice_channel_wakeup(channel, TRUE); if (reason != SPICE_CHANNEL_NONE) g_signal_emit(G_OBJECT(channel), signals[SPICE_CHANNEL_EVENT], 0, reason); -- 1.7.7.5