[Spice-commits] 18 commits - gtk/channel-main.c gtk/gio-coroutine.c gtk/spice-channel.c gtk/spice-gstaudio.c gtk/spice-pulse.c gtk/spice-session.c gtk/spice-session.h gtk/spice-session-priv.h gtk/usb-acl-helper.c gtk/usb-device-manager.c gtk/usb-device-widget.c gtk/win-usb-driver-install.c TODO

Marc-André Lureau elmarco at kemper.freedesktop.org
Mon Nov 24 08:25:59 PST 2014


 TODO                         |    2 -
 gtk/channel-main.c           |   34 ++++++++++-------
 gtk/gio-coroutine.c          |    5 ++
 gtk/spice-channel.c          |   32 +++++++++++++---
 gtk/spice-gstaudio.c         |   83 ++++++++++++++++---------------------------
 gtk/spice-pulse.c            |   65 ++++++++++++---------------------
 gtk/spice-session-priv.h     |    5 +-
 gtk/spice-session.c          |   66 +++++++++++++++++++---------------
 gtk/spice-session.h          |    2 +
 gtk/usb-acl-helper.c         |    3 +
 gtk/usb-device-manager.c     |    1 
 gtk/usb-device-widget.c      |    3 +
 gtk/win-usb-driver-install.c |    3 +
 13 files changed, 160 insertions(+), 144 deletions(-)

New commits:
commit 8f4083906b47ad9bcc70ef0f871270df092e18f0
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sun Nov 9 17:16:42 2014 +0100

    migration: delay switch host reconnect
    
    The following critical happens on switch-host:
    
    (remote-viewer:4617): GSpice-CRITICAL **: channel_connect: assertion
    'c->sock == NULL' failed
    
    The critical happens since the main channel reset code calls
    set_agent_connected(), which will yield to main loop, so reconnection
    can't happen after calling spice_channel_disconnect(), and must wait
    until the channel reset completes.

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index e86fc15..6d06ee3 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2272,9 +2272,6 @@ static gboolean switch_host_delayed(gpointer data)
     spice_channel_disconnect(channel, SPICE_CHANNEL_SWITCHING);
     spice_session_switching_disconnect(session);
 
-    spice_channel_connect(channel);
-    spice_session_set_migration_state(session, SPICE_SESSION_MIGRATION_NONE);
-
     return FALSE;
 }
 
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 115efda..94f980f 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2639,6 +2639,12 @@ static void channel_disconnect(SpiceChannel *channel)
     spice_channel_reset(channel, FALSE);
 
     g_return_if_fail(SPICE_IS_CHANNEL(channel));
+
+    if (c->state == SPICE_CHANNEL_STATE_SWITCHING) {
+        spice_channel_connect(channel);
+        spice_session_set_migration_state(spice_channel_get_session(channel),
+                                          SPICE_SESSION_MIGRATION_NONE);
+    }
 }
 
 /**
commit 78f68e7b83a6379a18418f1912784db371be8c63
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sun Nov 9 16:35:37 2014 +0100

    migration: don't check socket error
    
    During migration, the original socket is closed before the coroutine
    finishes, so it's not guaranteed that c->sock will still be set when the
    channel is in an error state in spice_channel_iterate().

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 4fbcb18..115efda 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2140,6 +2140,10 @@ static gboolean spice_channel_iterate(SpiceChannel *channel)
 
     if (c->has_error) {
         GIOCondition ret;
+
+        if (!c->sock)
+            return FALSE;
+
         /* We don't want to report an error if the socket was closed gracefully
          * on the other end (VM shutdown) */
         ret = g_socket_condition_check(c->sock, G_IO_IN | G_IO_ERR | G_IO_HUP);
commit 578b9b02d904e3233fcb8d6fcf7ceff4b10a39ce
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 18:32:22 2014 +0100

    coroutine: reference object when signaling
    
    Before the signal is actually emitted, the channel may be
    released. Let's keep a reference to the object during
    the function time, to prevent the object from being destroyed before
    calling g_signal_emit() or g_object_notify() in main context.

diff --git a/gtk/gio-coroutine.c b/gtk/gio-coroutine.c
index c903bd2..c866e15 100644
--- a/gtk/gio-coroutine.c
+++ b/gtk/gio-coroutine.c
@@ -222,9 +222,11 @@ g_coroutine_signal_emit(gpointer instance, guint signal_id,
     if (coroutine_self_is_main()) {
         g_signal_emit_valist(instance, signal_id, detail, data.var_args);
     } else {
+        g_object_ref(instance);
         g_idle_add(emit_main_context, &data);
         coroutine_yield(NULL);
         g_warn_if_fail(data.notified);
+        g_object_unref(instance);
     }
 
     va_end (data.var_args);
@@ -253,7 +255,7 @@ void g_coroutine_object_notify(GObject *object,
         g_object_notify(object, property_name);
     } else {
 
-        data.instance = object;
+        data.instance = g_object_ref(object);
         data.caller = coroutine_self();
         data.propname = (gpointer)property_name;
         data.notified = FALSE;
@@ -268,5 +270,6 @@ void g_coroutine_object_notify(GObject *object,
          */
         coroutine_yield(NULL);
         g_warn_if_fail(data.notified);
+        g_object_unref(object);
     }
 }
commit 628844525cd7a4c1a4b931bfe65ac302b724e7e8
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 18:03:05 2014 +0100

    migration: use spice_session_abort_migration() on error
    
    Use a more complete method for the job

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 5c055ed..e86fc15 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2174,8 +2174,8 @@ static void main_migrate_connect(SpiceChannel *channel,
     coroutine_yield(NULL);
 
     if (mig.nchannels != 0) {
-        reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR;
-        spice_session_disconnect(mig.session);
+        CHANNEL_DEBUG(channel, "migrate failed: some channels failed to connect");
+        spice_session_abort_migration(session);
     } else {
         if (mig.do_seamless) {
             SPICE_DEBUG("migration (seamless): connections all ok");
commit 80f8bdc99f8e588977ff32b87e1a2a5af6ea5145
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 18:02:20 2014 +0100

    migration: remove unnecessary reference

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 86dca87..5c055ed 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2163,7 +2163,7 @@ static void main_migrate_connect(SpiceChannel *channel,
     mig.session = spice_session_new_from_session(session);
     if (mig.session == NULL)
         goto end;
-    session->priv->migration = g_object_ref(mig.session);
+    session->priv->migration = mig.session;
 
     main_priv->migrate_data = &mig;
 
@@ -2187,7 +2187,6 @@ static void main_migrate_connect(SpiceChannel *channel,
         spice_session_start_migrating(spice_channel_get_session(channel),
                                       mig.do_seamless);
     }
-    g_object_unref(mig.session);
 
 end:
     CHANNEL_DEBUG(channel, "migrate connect reply %d", reply_type);
commit b33bd0a64eac07c1e5f219f8a6844388b2439407
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 17:50:16 2014 +0100

    migration: create the migration session earlier
    
    The migration session creation may fail. Instead of delaying the session
    creation to the main_connect() callback, do it directly from the message
    handler context, to report failure early to server.

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index d4a7d24..86dca87 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2065,21 +2065,16 @@ static gboolean migrate_connect(gpointer data)
     SpiceChannelPrivate  *c;
     int port, sport;
     const char *host;
-    SpiceSession *session;
 
     g_return_val_if_fail(mig != NULL, FALSE);
     g_return_val_if_fail(mig->info != NULL, FALSE);
     g_return_val_if_fail(mig->nchannels == 0, FALSE);
     c = SPICE_CHANNEL(mig->src_channel)->priv;
     g_return_val_if_fail(c != NULL, FALSE);
+    g_return_val_if_fail(mig->session != NULL, FALSE);
 
-    session = spice_channel_get_session(mig->src_channel);
-    g_return_val_if_fail(session->priv->migration == NULL, FALSE);
-
-    mig->session = spice_session_new_from_session(session);
     mig->session->priv->migration_copy = true;
     spice_session_set_migration_state(mig->session, SPICE_SESSION_MIGRATION_CONNECTING);
-    session->priv->migration = g_object_ref(mig->session);
 
     if ((c->peer_hdr.major_version == 1) &&
         (c->peer_hdr.minor_version < 1)) {
@@ -2149,9 +2144,10 @@ static void main_migrate_connect(SpiceChannel *channel,
                                  uint32_t src_mig_version)
 {
     SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv;
+    int reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR;
     spice_migrate mig = { 0, };
     SpiceMsgOut *out;
-    int reply_type;
+    SpiceSession *session;
 
     mig.src_channel = channel;
     mig.info = dst_info;
@@ -2159,6 +2155,16 @@ static void main_migrate_connect(SpiceChannel *channel,
     mig.do_seamless = do_seamless;
     mig.src_mig_version = src_mig_version;
 
+    CHANNEL_DEBUG(channel, "migrate connect");
+    session = spice_channel_get_session(channel);
+    if (session->priv->migration != NULL)
+        goto end;
+
+    mig.session = spice_session_new_from_session(session);
+    if (mig.session == NULL)
+        goto end;
+    session->priv->migration = g_object_ref(mig.session);
+
     main_priv->migrate_data = &mig;
 
     /* no need to track idle, call is sync for this coroutine */
@@ -2166,7 +2172,6 @@ static void main_migrate_connect(SpiceChannel *channel,
 
     /* switch to main loop and wait for connections */
     coroutine_yield(NULL);
-    g_return_if_fail(mig.session != NULL);
 
     if (mig.nchannels != 0) {
         reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR;
@@ -2184,6 +2189,8 @@ static void main_migrate_connect(SpiceChannel *channel,
     }
     g_object_unref(mig.session);
 
+end:
+    CHANNEL_DEBUG(channel, "migrate connect reply %d", reply_type);
     out = spice_msg_out_new(SPICE_CHANNEL(channel), reply_type);
     spice_msg_out_send(out);
 }
commit 259d7c6c3a96cd18c8770684c74c1b88749e8e14
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Nov 12 17:59:02 2014 +0100

    migration: improve debug log

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 3966560..4fbcb18 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2545,6 +2545,7 @@ static void channel_reset(SpiceChannel *channel, gboolean migrating)
 {
     SpiceChannelPrivate *c = channel->priv;
 
+    CHANNEL_DEBUG(channel, "channel reset");
     if (c->connect_delayed_id) {
         g_source_remove(c->connect_delayed_id);
         c->connect_delayed_id = 0;
@@ -2611,6 +2612,7 @@ static void channel_reset(SpiceChannel *channel, gboolean migrating)
 G_GNUC_INTERNAL
 void spice_channel_reset(SpiceChannel *channel, gboolean migrating)
 {
+    CHANNEL_DEBUG(channel, "reset %s", migrating ? "migrating" : "");
     SPICE_CHANNEL_GET_CLASS(channel)->channel_reset(channel, migrating);
 }
 
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 4bb2b9f..c44a3e1 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1463,6 +1463,7 @@ void spice_session_abort_migration(SpiceSession *session)
         return;
     }
 
+    SPICE_DEBUG("migration: abort");
     if (s->migration_state != SPICE_SESSION_MIGRATION_MIGRATING)
         goto end;
 
@@ -1522,7 +1523,7 @@ void spice_session_channel_migrate(SpiceSession *session, SpiceChannel *channel)
     s->migration_left = g_list_remove(s->migration_left, channel);
 
     if (g_list_length(s->migration_left) == 0) {
-        CHANNEL_DEBUG(channel, "all channel migrated");
+        CHANNEL_DEBUG(channel, "migration: all channel migrated, success");
         spice_session_disconnect(s->migration);
         g_object_unref(s->migration);
         s->migration = NULL;
commit 5ef64315f816e14041eca65bd15266e2ea2e9394
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 17:44:41 2014 +0100

    migration: remove migration cleanup from dispose
    
    The spice_session_disconnect() method now calls
    spice_session_abort_migration(), so it is not necessary to do migration
    cleanups in dispose anymore

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index d8cb790..4bb2b9f 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -180,21 +180,9 @@ spice_session_dispose(GObject *gobject)
 
     spice_session_disconnect(session);
 
-    if (s->migration) {
-        spice_session_disconnect(s->migration);
-        g_object_unref(s->migration);
-        s->migration = NULL;
-    }
-
-    if (s->migration_left) {
-        g_list_free(s->migration_left);
-        s->migration_left = NULL;
-    }
-
-    if (s->after_main_init) {
-        g_source_remove(s->after_main_init);
-        s->after_main_init = 0;
-    }
+    g_warn_if_fail(s->migration == NULL);
+    g_warn_if_fail(s->migration_left == NULL);
+    g_warn_if_fail(s->after_main_init == 0);
 
     g_clear_object(&s->audio_manager);
     g_clear_object(&s->desktop_integration);
@@ -1500,6 +1488,12 @@ end:
     g_object_unref(s->migration);
     s->migration = NULL;
 
+    s->migrate_wait_init = FALSE;
+    if (s->after_main_init) {
+        g_source_remove(s->after_main_init);
+        s->after_main_init = 0;
+    }
+
     spice_session_set_migration_state(session, SPICE_SESSION_MIGRATION_NONE);
 }
 
commit 6d23963f5ce916d02b7b65be42b83a396959d41a
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 15:11:32 2014 +0100

    migration: set connecting state before fd request
    
    During migration, the main channel coroutine initiating the process is
    waiting for connection completion of all
    channels. migrate_channel_event_cb() yields back to the main channel
    coroutine once all channels have completed connection, or it will abort
    migration for unexpected channel events, such as SPICE_CHANNEL_CLOSED
    
    If the migration is cancelled before connection completes, but the
    channels state are still in the SPICE_CHANNEL_STATE_UNCONNECTED state,
    no events will be emitted in channel_disconnect(), and the source
    session main channel will remain frozen waiting for migration completion
    or failure.
    
    Currently, for client-fd channels, the channel state remains UNCONNECTED
    until the fd is provided. But if cancellation occurs, no channel events
    are emitted and the source session is stuck.
    
    Before requesting the fd, set the channel state to connecting, so it
    will emit an error if disconnect happens, and it will finish cancelling
    the migration in source session main channel.

diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index f59127e..3966560 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2461,10 +2461,8 @@ static gboolean channel_connect(SpiceChannel *channel)
         g_warning("%s: channel setup incomplete", __FUNCTION__);
         return false;
     }
-    if (c->state != SPICE_CHANNEL_STATE_UNCONNECTED) {
-        g_warning("Invalid channel_connect state: %d", c->state);
-        return true;
-    }
+
+    c->state = SPICE_CHANNEL_STATE_CONNECTING;
 
     if (spice_session_get_client_provided_socket(c->session)) {
         if (c->fd == -1) {
@@ -2476,7 +2474,7 @@ static gboolean channel_connect(SpiceChannel *channel)
             return true;
         }
     }
-    c->state = SPICE_CHANNEL_STATE_CONNECTING;
+
     c->xmit_queue_blocked = FALSE;
 
     g_return_val_if_fail(c->sock == NULL, FALSE);
@@ -2532,6 +2530,11 @@ gboolean spice_channel_open_fd(SpiceChannel *channel, int fd)
     g_return_val_if_fail(fd >= -1, FALSE);
 
     c = channel->priv;
+    if (c->state > SPICE_CHANNEL_STATE_CONNECTING) {
+        g_warning("Invalid channel_connect state: %d", c->state);
+        return true;
+    }
+
     c->fd = fd;
 
     return channel_connect(channel);
commit 2a98797caa202a0db271a677808903c02e34aca9
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 15:34:35 2014 +0100

    migration: fail with client provided fd
    
    Currently the fd request is done on the migration session, which is not
    connected with the client session, so the client has no way to provide
    fd for the migration. And the original and migration session ends up
    stuck. Failing early seems the best for now.

diff --git a/TODO b/TODO
index e29d3b5..68ea4d5 100644
--- a/TODO
+++ b/TODO
@@ -1,3 +1,4 @@
+* implement migration support with client fd
 * create a ChannelBaseAudio
 * revive the win32 GDI backend
 
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index fd52683..f59127e 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2468,6 +2468,10 @@ static gboolean channel_connect(SpiceChannel *channel)
 
     if (spice_session_get_client_provided_socket(c->session)) {
         if (c->fd == -1) {
+            CHANNEL_DEBUG(channel, "requesting fd");
+            /* FIXME: no way for client to provide fd atm. */
+            /* It could either chain on parent channel.. */
+            /* or register migration channel on parent session, or ? */
             g_signal_emit(channel, signals[SPICE_CHANNEL_OPEN_FD], 0, c->tls);
             return true;
         }
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 42ad280..d8cb790 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1237,12 +1237,20 @@ SpiceSession *spice_session_new(void)
 G_GNUC_INTERNAL
 SpiceSession *spice_session_new_from_session(SpiceSession *session)
 {
-    SpiceSession *copy = SPICE_SESSION(g_object_new(SPICE_TYPE_SESSION,
-                                                    "host", NULL,
-                                                    "ca-file", NULL,
-                                                    NULL));
-    SpiceSessionPrivate *c = copy->priv, *s = session->priv;
+    SpiceSessionPrivate *s = session->priv;
+    SpiceSession *copy;
+    SpiceSessionPrivate *c;
+
+    if (s->client_provided_sockets) {
+        g_warning("migration with client provided fd is not supported yet");
+        return NULL;
+    }
 
+    copy = SPICE_SESSION(g_object_new(SPICE_TYPE_SESSION,
+                                      "host", NULL,
+                                      "ca-file", NULL,
+                                      NULL));
+    c = copy->priv;
     g_clear_object(&c->proxy);
 
     g_warn_if_fail(c->host == NULL);
commit 83552a6eec43ce2fb9f55003f2deb40e919f97db
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 15:06:51 2014 +0100

    migration: set session migration during connect
    
    Track the migration session earlier, so that disconnecting before
    migration finished will abort and release it.

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 873487b..d4a7d24 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2079,6 +2079,7 @@ static gboolean migrate_connect(gpointer data)
     mig->session = spice_session_new_from_session(session);
     mig->session->priv->migration_copy = true;
     spice_session_set_migration_state(mig->session, SPICE_SESSION_MIGRATION_CONNECTING);
+    session->priv->migration = g_object_ref(mig->session);
 
     if ((c->peer_hdr.major_version == 1) &&
         (c->peer_hdr.minor_version < 1)) {
@@ -2178,9 +2179,8 @@ static void main_migrate_connect(SpiceChannel *channel,
             SPICE_DEBUG("migration (semi-seamless): connections all ok");
             reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED;
         }
-        spice_session_set_migration(spice_channel_get_session(channel),
-                                    mig.session,
-                                    mig.do_seamless);
+        spice_session_start_migrating(spice_channel_get_session(channel),
+                                      mig.do_seamless);
     }
     g_object_unref(mig.session);
 
diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h
index da43866..8e102e0 100644
--- a/gtk/spice-session-priv.h
+++ b/gtk/spice-session-priv.h
@@ -138,9 +138,8 @@ void spice_session_set_mm_time(SpiceSession *session, guint32 time);
 guint32 spice_session_get_mm_time(SpiceSession *session);
 
 void spice_session_switching_disconnect(SpiceSession *session);
-void spice_session_set_migration(SpiceSession *session,
-                                 SpiceSession *migration,
-                                 gboolean full_migration);
+void spice_session_start_migrating(SpiceSession *session,
+                                   gboolean full_migration);
 void spice_session_abort_migration(SpiceSession *session);
 void spice_session_set_migration_state(SpiceSession *session, SpiceSessionMigration state);
 
diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 3e3f16d..42ad280 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1391,22 +1391,22 @@ void spice_session_switching_disconnect(SpiceSession *self)
 }
 
 G_GNUC_INTERNAL
-void spice_session_set_migration(SpiceSession *session,
-                                 SpiceSession *migration,
-                                 gboolean full_migration)
+void spice_session_start_migrating(SpiceSession *session,
+                                   gboolean full_migration)
 {
     SpiceSessionPrivate *s = session->priv;
-    SpiceSessionPrivate *m = migration->priv;
+    SpiceSessionPrivate *m;
     gchar *tmp;
 
-    g_return_if_fail(s != NULL);
+    g_return_if_fail(s->migration != NULL);
+    m = s->migration->priv;
+    g_return_if_fail(m->migration_state == SPICE_SESSION_MIGRATION_CONNECTING);
+
 
     s->full_migration = full_migration;
     spice_session_set_migration_state(session, SPICE_SESSION_MIGRATION_MIGRATING);
 
-    g_warn_if_fail(s->migration == NULL);
-    s->migration = g_object_ref(migration);
-
+    /* swapping connection details happens after MIGRATION_CONNECTING state */
     tmp = s->host;
     s->host = m->host;
     m->host = tmp;
commit 427b46b1c2de1f407a63cfb56b03e5396eb1d291
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 14:55:58 2014 +0100

    migration: add "connecting" state
    
    Add a new migration state to track early migration step, when migration
    session is connecting to destination

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 62e8905..873487b 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2078,6 +2078,7 @@ static gboolean migrate_connect(gpointer data)
 
     mig->session = spice_session_new_from_session(session);
     mig->session->priv->migration_copy = true;
+    spice_session_set_migration_state(mig->session, SPICE_SESSION_MIGRATION_CONNECTING);
 
     if ((c->peer_hdr.major_version == 1) &&
         (c->peer_hdr.minor_version < 1)) {
diff --git a/gtk/spice-session.h b/gtk/spice-session.h
index 665c609..4043a64 100644
--- a/gtk/spice-session.h
+++ b/gtk/spice-session.h
@@ -52,6 +52,7 @@ typedef enum {
  * @SPICE_SESSION_MIGRATION_NONE: no migration going on
  * @SPICE_SESSION_MIGRATION_SWITCHING: the session is switching host (destroy and reconnect)
  * @SPICE_SESSION_MIGRATION_MIGRATING: the session is migrating seamlessly (reconnect)
+ * @SPICE_SESSION_MIGRATION_CONNECTING: the migration is connecting to destination
  *
  * Session migration state.
  **/
@@ -59,6 +60,7 @@ typedef enum {
     SPICE_SESSION_MIGRATION_NONE,
     SPICE_SESSION_MIGRATION_SWITCHING,
     SPICE_SESSION_MIGRATION_MIGRATING,
+    SPICE_SESSION_MIGRATION_CONNECTING,
 } SpiceSessionMigration;
 
 struct _SpiceSession
commit da17cfa49daafe4f6e6ec32cacc2c0211f52cf16
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 14:46:01 2014 +0100

    migration: abort migrate on disconnect
    
    If the session has an ongoing migration, but it is disconnected,
    abort it.

diff --git a/gtk/spice-session.c b/gtk/spice-session.c
index 88bcacd..3e3f16d 100644
--- a/gtk/spice-session.c
+++ b/gtk/spice-session.c
@@ -1467,6 +1467,9 @@ void spice_session_abort_migration(SpiceSession *session)
         return;
     }
 
+    if (s->migration_state != SPICE_SESSION_MIGRATION_MIGRATING)
+        goto end;
+
     for (ring = ring_get_head(&s->channels);
          ring != NULL; ring = next) {
         next = ring_next(&s->channels, ring);
@@ -1482,6 +1485,7 @@ void spice_session_abort_migration(SpiceSession *session)
                                          !s->full_migration);
     }
 
+end:
     g_list_free(s->migration_left);
     s->migration_left = NULL;
     spice_session_disconnect(s->migration);
@@ -1652,6 +1656,7 @@ void spice_session_disconnect(SpiceSession *session)
     s->name = NULL;
     memset(s->uuid, 0, sizeof(s->uuid));
 
+    spice_session_abort_migration(session);
     /* we leave disconnecting = TRUE, so that spice_channel_destroy()
        is not called multiple times on channels that are in pending
        destroy state. */
commit dc80df0c51079dd938c1a1465c2a0fa30feddced
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Sat Nov 8 14:30:01 2014 +0100

    migration: add a few more pre-conditions in migration code
    
    Those preconditions help to figure out several issues related to
    migration.

diff --git a/gtk/channel-main.c b/gtk/channel-main.c
index 1ad090f..62e8905 100644
--- a/gtk/channel-main.c
+++ b/gtk/channel-main.c
@@ -2074,6 +2074,8 @@ static gboolean migrate_connect(gpointer data)
     g_return_val_if_fail(c != NULL, FALSE);
 
     session = spice_channel_get_session(mig->src_channel);
+    g_return_val_if_fail(session->priv->migration == NULL, FALSE);
+
     mig->session = spice_session_new_from_session(session);
     mig->session->priv->migration_copy = true;
 
diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c
index 6c24bf5..fd52683 100644
--- a/gtk/spice-channel.c
+++ b/gtk/spice-channel.c
@@ -2500,6 +2500,8 @@ gboolean spice_channel_connect(SpiceChannel *channel)
     if (c->state >= SPICE_CHANNEL_STATE_CONNECTING)
         return TRUE;
 
+    g_return_val_if_fail(channel->priv->fd == -1, FALSE);
+
     return channel_connect(channel);
 }
 
@@ -2522,6 +2524,7 @@ gboolean spice_channel_open_fd(SpiceChannel *channel, int fd)
 
     g_return_val_if_fail(SPICE_IS_CHANNEL(channel), FALSE);
     g_return_val_if_fail(channel->priv != NULL, FALSE);
+    g_return_val_if_fail(channel->priv->fd == -1, FALSE);
     g_return_val_if_fail(fd >= -1, FALSE);
 
     c = channel->priv;
commit 673dabb26bd32f00ea90f358ce70161c3ae73cc6
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Fri Nov 7 20:27:16 2014 +0100

    usb: stop processing usb events on error
    
    If libusb returned an error in the event loop, stop further event
    handling. This avoid spinning in an error loop in error cases.

diff --git a/gtk/usb-device-manager.c b/gtk/usb-device-manager.c
index 9abcd8c..dbd3d1a 100644
--- a/gtk/usb-device-manager.c
+++ b/gtk/usb-device-manager.c
@@ -1174,6 +1174,7 @@ static gpointer spice_usb_device_manager_usb_ev_thread(gpointer user_data)
         if (rc && rc != LIBUSB_ERROR_INTERRUPTED) {
             const char *desc = spice_usbutil_libusb_strerror(rc);
             g_warning("Error handling USB events: %s [%i]", desc, rc);
+            break;
         }
     }
 
commit b1c04504b75d5e778a302f01328fc40598fe6741
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Thu Nov 6 19:45:46 2014 +0100

    audio: use weak references to channel
    
    The audio channels are currently referenced and released on channel
    close events. However, this event may not happen if the channel never
    was connected. Keeping channels alive also prevent session from
    finishing.
    
    By not holding the ref, the channel can go to dispose
    when it is no longer needed, and the session can be disposed too.

diff --git a/gtk/spice-gstaudio.c b/gtk/spice-gstaudio.c
index e6cf6a9..6173783 100644
--- a/gtk/spice-gstaudio.c
+++ b/gtk/spice-gstaudio.c
@@ -53,9 +53,8 @@ struct _SpiceGstaudioPrivate {
     guint                   mmtime_id;
 };
 
-static void channel_event(SpiceChannel *channel, SpiceChannelEvent event,
-                          gpointer data);
 static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel);
+static void channel_weak_notified(gpointer data, GObject *where_the_object_was);
 
 static void spice_gstaudio_finalize(GObject *obj)
 {
@@ -91,27 +90,21 @@ static void spice_gstaudio_dispose(GObject *obj)
     stream_dispose(&p->playback);
     stream_dispose(&p->record);
 
-    if (p->pchannel != NULL) {
-        g_signal_handlers_disconnect_by_func(p->pchannel,
-                                             channel_event, obj);
-        g_object_unref(p->pchannel);
-        p->pchannel = NULL;
-    }
+    if (p->pchannel)
+        g_object_weak_unref(G_OBJECT(p->pchannel), channel_weak_notified, gstaudio);
+    p->pchannel = NULL;
 
-    if (p->rchannel != NULL) {
-        g_signal_handlers_disconnect_by_func(p->rchannel,
-                                             channel_event, obj);
-        g_object_unref(p->rchannel);
-        p->rchannel = NULL;
-    }
+    if (p->rchannel)
+        g_object_weak_unref(G_OBJECT(p->rchannel), channel_weak_notified, gstaudio);
+    p->rchannel = NULL;
 
     if (G_OBJECT_CLASS(spice_gstaudio_parent_class)->dispose)
         G_OBJECT_CLASS(spice_gstaudio_parent_class)->dispose(obj);
 }
 
-static void spice_gstaudio_init(SpiceGstaudio *pulse)
+static void spice_gstaudio_init(SpiceGstaudio *gstaudio)
 {
-    pulse->priv = SPICE_GSTAUDIO_GET_PRIVATE(pulse);
+    gstaudio->priv = SPICE_GSTAUDIO_GET_PRIVATE(gstaudio);
 }
 
 static void spice_gstaudio_class_init(SpiceGstaudioClass *klass)
@@ -144,9 +137,8 @@ static void record_new_buffer(GstAppSink *appsink, gpointer data)
     gst_element_post_message(p->record.pipe, msg);
 }
 
-static void record_stop(SpiceRecordChannel *channel, gpointer data)
+static void record_stop(SpiceGstaudio *gstaudio)
 {
-    SpiceGstaudio *gstaudio = data;
     SpiceGstaudioPrivate *p = gstaudio->priv;
 
     SPICE_DEBUG("%s", __FUNCTION__);
@@ -230,7 +222,7 @@ static void record_start(SpiceRecordChannel *channel, gint format, gint channels
     if (p->record.pipe &&
         (p->record.rate != frequency ||
          p->record.channels != channels)) {
-        record_stop(channel, data);
+        record_stop(gstaudio);
         gst_object_unref(p->record.pipe);
         p->record.pipe = NULL;
     }
@@ -285,31 +277,6 @@ lerr:
         gst_element_set_state(p->record.pipe, GST_STATE_PLAYING);
 }
 
-static void channel_event(SpiceChannel *channel, SpiceChannelEvent event,
-                          gpointer data)
-{
-    SpiceGstaudio *gstaudio = data;
-    SpiceGstaudioPrivate *p = gstaudio->priv;
-
-    switch (event) {
-    case SPICE_CHANNEL_OPENED:
-        break;
-    case SPICE_CHANNEL_CLOSED:
-        if (channel == p->pchannel) {
-            p->pchannel = NULL;
-            g_object_unref(channel);
-        } else if (channel == p->rchannel) {
-            record_stop(SPICE_RECORD_CHANNEL(channel), gstaudio);
-            p->rchannel = NULL;
-            g_object_unref(channel);
-        } else /* if (p->pchannel || p->rchannel) */
-            g_warn_if_reached();
-        break;
-    default:
-        break;
-    }
-}
-
 static void playback_stop(SpicePlaybackChannel *channel, gpointer data)
 {
     SpiceGstaudio *gstaudio = data;
@@ -542,6 +509,22 @@ static void record_mute_changed(GObject *object, GParamSpec *pspec, gpointer dat
     g_object_unref(e);
 }
 
+static void
+channel_weak_notified(gpointer data,
+                      GObject *where_the_object_was)
+{
+    SpiceGstaudio *gstaudio = SPICE_GSTAUDIO(data);
+    SpiceGstaudioPrivate *p = gstaudio->priv;
+
+    if (where_the_object_was == (GObject *)p->pchannel) {
+        p->pchannel = NULL;
+    } else if (where_the_object_was == (GObject *)p->rchannel) {
+        SPICE_DEBUG("record closed");
+        record_stop(gstaudio);
+        p->rchannel = NULL;
+    }
+}
+
 static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
 {
     SpiceGstaudio *gstaudio = SPICE_GSTAUDIO(audio);
@@ -550,15 +533,14 @@ static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
     if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
         g_return_val_if_fail(p->pchannel == NULL, FALSE);
 
-        p->pchannel = g_object_ref(channel);
+        p->pchannel = channel;
+        g_object_weak_ref(G_OBJECT(p->pchannel), channel_weak_notified, audio);
         spice_g_signal_connect_object(channel, "playback-start",
                                       G_CALLBACK(playback_start), gstaudio, 0);
         spice_g_signal_connect_object(channel, "playback-data",
                                       G_CALLBACK(playback_data), gstaudio, 0);
         spice_g_signal_connect_object(channel, "playback-stop",
                                       G_CALLBACK(playback_stop), gstaudio, 0);
-        spice_g_signal_connect_object(channel, "channel-event",
-                                      G_CALLBACK(channel_event), gstaudio, 0);
         spice_g_signal_connect_object(channel, "notify::volume",
                                       G_CALLBACK(playback_volume_changed), gstaudio, 0);
         spice_g_signal_connect_object(channel, "notify::mute",
@@ -570,13 +552,12 @@ static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
     if (SPICE_IS_RECORD_CHANNEL(channel)) {
         g_return_val_if_fail(p->rchannel == NULL, FALSE);
 
-        p->rchannel = g_object_ref(channel);
+        p->rchannel = channel;
+        g_object_weak_ref(G_OBJECT(p->rchannel), channel_weak_notified, audio);
         spice_g_signal_connect_object(channel, "record-start",
                                       G_CALLBACK(record_start), gstaudio, 0);
         spice_g_signal_connect_object(channel, "record-stop",
-                                      G_CALLBACK(record_stop), gstaudio, 0);
-        spice_g_signal_connect_object(channel, "channel-event",
-                                      G_CALLBACK(channel_event), gstaudio, 0);
+                                      G_CALLBACK(record_stop), gstaudio, G_CONNECT_SWAPPED);
         spice_g_signal_connect_object(channel, "notify::volume",
                                       G_CALLBACK(record_volume_changed), gstaudio, 0);
         spice_g_signal_connect_object(channel, "notify::mute",
diff --git a/gtk/spice-pulse.c b/gtk/spice-pulse.c
index 744ce4d..fc1662e 100644
--- a/gtk/spice-pulse.c
+++ b/gtk/spice-pulse.c
@@ -74,10 +74,9 @@ static const char *context_state_names[] = {
 #define STATE_NAME(array, state) \
     ((state < G_N_ELEMENTS(array)) ? array[state] : NULL)
 
-static void channel_event(SpiceChannel *channel, SpiceChannelEvent event,
-                          gpointer data);
 static void stream_stop(SpicePulse *pulse, struct stream *s);
 static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel);
+static void channel_weak_notified(gpointer data, GObject *where_the_object_was);
 
 static void spice_pulse_finalize(GObject *obj)
 {
@@ -120,11 +119,11 @@ static void spice_pulse_dispose(GObject *obj)
     p->record.cork_op = NULL;
 
     if (p->pchannel)
-        g_object_unref(p->pchannel);
+        g_object_weak_unref(G_OBJECT(p->pchannel), channel_weak_notified, pulse);
     p->pchannel = NULL;
 
     if (p->rchannel)
-        g_object_unref(p->rchannel);
+        g_object_weak_unref(G_OBJECT(p->rchannel), channel_weak_notified, pulse);
     p->rchannel = NULL;
 
     G_OBJECT_CLASS(spice_pulse_parent_class)->dispose(obj);
@@ -567,9 +566,8 @@ static void record_start(SpiceRecordChannel *channel, gint format, gint channels
     p->state = state;
 }
 
-static void record_stop(SpiceRecordChannel *channel, gpointer data)
+static void record_stop(SpicePulse *pulse)
 {
-    SpicePulse *pulse = data;
     SpicePulsePrivate *p = pulse->priv;
 
     SPICE_DEBUG("%s", __FUNCTION__);
@@ -581,33 +579,6 @@ static void record_stop(SpiceRecordChannel *channel, gpointer data)
     stream_stop(pulse, &p->record);
 }
 
-static void channel_event(SpiceChannel *channel, SpiceChannelEvent event,
-                          gpointer data)
-{
-    SpicePulse *pulse = data;
-    SpicePulsePrivate *p = pulse->priv;
-
-    switch (event) {
-    case SPICE_CHANNEL_OPENED:
-        break;
-    case SPICE_CHANNEL_CLOSED:
-        if (channel == p->pchannel) {
-            SPICE_DEBUG("playback closed");
-            p->pchannel = NULL;
-            g_object_unref(channel);
-        } else if (channel == p->rchannel) {
-            SPICE_DEBUG("record closed");
-            record_stop(SPICE_RECORD_CHANNEL(channel), pulse);
-            p->rchannel = NULL;
-            g_object_unref(channel);
-        } else /* if (p->pchannel || p->rchannel) */
-            g_warn_if_reached();
-        break;
-    default:
-        break;
-    }
-}
-
 static void playback_volume_changed(GObject *object, GParamSpec *pspec, gpointer data)
 {
     SpicePulse *pulse = data;
@@ -748,6 +719,22 @@ static void record_volume_changed(GObject *object, GParamSpec *pspec, gpointer d
         pa_operation_unref(op);
 }
 
+static void
+channel_weak_notified(gpointer data,
+                      GObject *where_the_object_was)
+{
+    SpicePulse *pulse = SPICE_PULSE(data);
+    SpicePulsePrivate *p = pulse->priv;
+
+    if (where_the_object_was == (GObject *)p->pchannel) {
+        p->pchannel = NULL;
+    } else if (where_the_object_was == (GObject *)p->rchannel) {
+        SPICE_DEBUG("record closed");
+        record_stop(pulse);
+        p->rchannel = NULL;
+    }
+}
+
 static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
 {
     SpicePulse *pulse = SPICE_PULSE(audio);
@@ -756,15 +743,14 @@ static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
     if (SPICE_IS_PLAYBACK_CHANNEL(channel)) {
         g_return_val_if_fail(p->pchannel == NULL, FALSE);
 
-        p->pchannel = g_object_ref(channel);
+        p->pchannel = channel;
+        g_object_weak_ref(G_OBJECT(p->pchannel), channel_weak_notified, audio);
         spice_g_signal_connect_object(channel, "playback-start",
                                       G_CALLBACK(playback_start), pulse, 0);
         spice_g_signal_connect_object(channel, "playback-data",
                                       G_CALLBACK(playback_data), pulse, 0);
         spice_g_signal_connect_object(channel, "playback-stop",
                                       G_CALLBACK(playback_stop), pulse, 0);
-        spice_g_signal_connect_object(channel, "channel-event",
-                                      G_CALLBACK(channel_event), pulse, 0);
         spice_g_signal_connect_object(channel, "notify::volume",
                                       G_CALLBACK(playback_volume_changed), pulse, 0);
         spice_g_signal_connect_object(channel, "notify::mute",
@@ -778,13 +764,12 @@ static gboolean connect_channel(SpiceAudio *audio, SpiceChannel *channel)
     if (SPICE_IS_RECORD_CHANNEL(channel)) {
         g_return_val_if_fail(p->rchannel == NULL, FALSE);
 
-        p->rchannel = g_object_ref(channel);
+        p->rchannel = channel;
+        g_object_weak_ref(G_OBJECT(p->rchannel), channel_weak_notified, audio);
         spice_g_signal_connect_object(channel, "record-start",
                                       G_CALLBACK(record_start), pulse, 0);
         spice_g_signal_connect_object(channel, "record-stop",
-                                      G_CALLBACK(record_stop), pulse, 0);
-        spice_g_signal_connect_object(channel, "channel-event",
-                                      G_CALLBACK(channel_event), pulse, 0);
+                                      G_CALLBACK(record_stop), pulse, G_CONNECT_SWAPPED);
         spice_g_signal_connect_object(channel, "notify::volume",
                                       G_CALLBACK(record_volume_changed), pulse, 0);
         spice_g_signal_connect_object(channel, "notify::mute",
commit 92df1b6a7f1e007c11e48b8fceb082db045430b5
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Tue Nov 4 14:45:24 2014 +0100

    Add missing finalize chaining
    
    Finalize should chain up to the finalize method of the parent class.
    
    Trivial fix.

diff --git a/gtk/usb-acl-helper.c b/gtk/usb-acl-helper.c
index a3ba14d..6a49627 100644
--- a/gtk/usb-acl-helper.c
+++ b/gtk/usb-acl-helper.c
@@ -73,6 +73,9 @@ static void spice_usb_acl_helper_cleanup(SpiceUsbAclHelper *self)
 static void spice_usb_acl_helper_finalize(GObject *gobject)
 {
     spice_usb_acl_helper_cleanup(SPICE_USB_ACL_HELPER(gobject));
+
+    if (G_OBJECT_CLASS(spice_usb_acl_helper_parent_class)->finalize)
+        G_OBJECT_CLASS(spice_usb_acl_helper_parent_class)->finalize(gobject);
 }
 
 static void spice_usb_acl_helper_class_init(SpiceUsbAclHelperClass *klass)
diff --git a/gtk/usb-device-widget.c b/gtk/usb-device-widget.c
index 74862ee..1ec30e3 100644
--- a/gtk/usb-device-widget.c
+++ b/gtk/usb-device-widget.c
@@ -250,6 +250,9 @@ static void spice_usb_device_widget_finalize(GObject *object)
     }
     g_object_unref(priv->session);
     g_free(priv->device_format_string);
+
+    if (G_OBJECT_CLASS(spice_usb_device_widget_parent_class)->finalize)
+        G_OBJECT_CLASS(spice_usb_device_widget_parent_class)->finalize(object);
 }
 
 static void spice_usb_device_widget_class_init(
diff --git a/gtk/win-usb-driver-install.c b/gtk/win-usb-driver-install.c
index e17287a..674a7c6 100644
--- a/gtk/win-usb-driver-install.c
+++ b/gtk/win-usb-driver-install.c
@@ -74,6 +74,9 @@ static void spice_win_usb_driver_finalize(GObject *gobject)
 
     spice_win_usb_driver_close(self);
     g_clear_object(&priv->result);
+
+    if (G_OBJECT_CLASS(spice_win_usb_driver_parent_class)->finalize)
+        G_OBJECT_CLASS(spice_win_usb_driver_parent_class)->finalize(gobject);
 }
 
 static void spice_win_usb_driver_class_init(SpiceWinUsbDriverClass *klass)
commit 1a57f8bb78206dce07a0a3fa92e082cdf7cadad7
Author: Marc-André Lureau <marcandre.lureau at redhat.com>
Date:   Wed Nov 12 17:38:50 2014 +0100

    Remove obsolete TODO item
    
    Tunnel are long obsoleted (did they ever work)
    Remove it from TODO list.
    
    Pushed unreviewed as trivial

diff --git a/TODO b/TODO
index 34e2c39..e29d3b5 100644
--- a/TODO
+++ b/TODO
@@ -1,5 +1,4 @@
 * create a ChannelBaseAudio
-* missing TunnelChannel implementation
 * revive the win32 GDI backend
 
 See also list of bugs open:


More information about the Spice-commits mailing list