On Sun, Dec 18, 2011 at 06:27:25PM +0100, Marc-André Lureau wrote: > Yonit Halperin described the flow, that I followed: > > (1) when client_migrate_info is called SPICE_MSG_MAIN_MIGRATE_BEGIN is > sent to the client. > > Then, the client should link to the target server (SpiceLinkMess), > i.e., connect all the channels, but it shouldn't poll from the target, > only from the source. You can refer to RedClient::Migrate class. The > connection id in the link message should be the id of the connection > to the source server. > > (2) The client sends to the source server > SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECT_ERROR) > > (3) When migration completes SPICE_MSG_MAIN_MIGRATE_(END|CANCEL) is > sent to the client. > > (3.1) In case of SPICE_MSG_MAIN_MIGRATE_CANCEL, the client closes the > connections to the target. > > (3.2) In case of SPICE_MSG_MAIN_MIGRATE_END, the client should reset > all the data that is related to the connection to the source and > complete the transition to the target server (without sending > ATTACH_CHANNELS, and without guest display initialization via agent). > > Then, the client sends SPICE_MSG_MAIN_MIGRATE_END to the target.
MSGC ? > --- > gtk/channel-main.c | 56 ++++++++++++++++++++++++++---- > gtk/spice-channel.c | 30 ++++++++++++---- > gtk/spice-session-priv.h | 4 ++ > gtk/spice-session.c | 84 > ++++++++++++++++++++++++++++++++++++++++++++++ > 4 files changed, 158 insertions(+), 16 deletions(-) > > diff --git a/gtk/channel-main.c b/gtk/channel-main.c > index 38a2c5d..42e4494 100644 > --- a/gtk/channel-main.c > +++ b/gtk/channel-main.c > @@ -75,6 +75,7 @@ struct _SpiceMainChannelPrivate { > GQueue *agent_msg_queue; > > guint switch_host_delayed_id; > + guint migrate_delayed_id; > }; > > typedef struct spice_migrate spice_migrate; > @@ -158,6 +159,8 @@ static void spice_main_channel_init(SpiceMainChannel > *channel) > > c = channel->priv = SPICE_MAIN_CHANNEL_GET_PRIVATE(channel); > c->agent_msg_queue = g_queue_new(); > + > + spice_channel_set_capability(SPICE_CHANNEL(channel), > SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); > } > > static void spice_main_get_property(GObject *object, > @@ -242,6 +245,11 @@ static void spice_main_channel_dispose(GObject *obj) > c->switch_host_delayed_id = 0; > } > > + if (c->migrate_delayed_id) { > + g_source_remove(c->migrate_delayed_id); > + c->migrate_delayed_id = 0; > + } > + > if (G_OBJECT_CLASS(spice_main_channel_parent_class)->dispose) > G_OBJECT_CLASS(spice_main_channel_parent_class)->dispose(obj); > } > @@ -1116,18 +1124,20 @@ static void main_handle_init(SpiceChannel *channel, > SpiceMsgIn *in) > session = spice_channel_get_session(channel); > spice_session_set_connection_id(session, init->session_id); > > - out = spice_msg_out_new(SPICE_CHANNEL(channel), > SPICE_MSGC_MAIN_ATTACH_CHANNELS); > - spice_msg_out_send_internal(out); > - > set_mouse_mode(SPICE_MAIN_CHANNEL(channel), init->supported_mouse_modes, > init->current_mouse_mode); > > + spice_session_set_mm_time(session, init->multi_media_time); > + > c->agent_tokens = init->agent_tokens; > - if (init->agent_connected) { > + if (init->agent_connected) > agent_start(SPICE_MAIN_CHANNEL(channel)); > - } > > - spice_session_set_mm_time(session, init->multi_media_time); > + if (spice_session_migrate_after_main_init(session)) > + return; > + > + out = spice_msg_out_new(SPICE_CHANNEL(channel), > SPICE_MSGC_MAIN_ATTACH_CHANNELS); > + spice_msg_out_send_internal(out); > } > > /* coroutine context */ > @@ -1389,13 +1399,15 @@ static void migrate_channel_new_cb(SpiceSession *s, > SpiceChannel *channel, gpoin > G_CALLBACK(migrate_channel_event_cb), data); > } > > -static void migrate_channel_connect(spice_migrate *mig, int type, int id) > +static SpiceChannel* migrate_channel_connect(spice_migrate *mig, int type, > int id) > { > SPICE_DEBUG("migrate_channel_connect %d:%d", type, id); > > SpiceChannel *newc = spice_channel_new(mig->session, type, id); > spice_channel_connect(newc); > mig->nchannels++; > + > + return newc; > } > > /* main context */ > @@ -1522,7 +1534,7 @@ static gboolean migrate_connect(gpointer data) > > /* the migration process is in 2 steps, first the main channel and > then the rest of the channels */ > - migrate_channel_connect(mig, SPICE_CHANNEL_MAIN, 0); > + mig->session->priv->cmain = migrate_channel_connect(mig, > SPICE_CHANNEL_MAIN, 0); > > return FALSE; > } > @@ -1561,6 +1573,33 @@ static void main_handle_migrate_begin(SpiceChannel > *channel, SpiceMsgIn *in) > } > > /* main context */ > +static gboolean migrate_delayed(gpointer data) > +{ > + SpiceChannel *channel = data; > + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv; > + > + g_warn_if_fail(c->migrate_delayed_id != 0); > + c->migrate_delayed_id = 0; > + > + spice_session_migrate_end(channel->priv->session); > + > + return FALSE; > +} > + > +/* coroutine context */ > +static void main_handle_migrate_end(SpiceChannel *channel, SpiceMsgIn *in) > +{ > + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv; > + > + SPICE_DEBUG("migrate end"); > + > + g_return_if_fail(c->migrate_delayed_id == 0); > + g_return_if_fail(spice_channel_test_capability(channel, > SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE)); > + > + c->migrate_delayed_id = g_idle_add(migrate_delayed, channel); > +} > + > +/* main context */ > static gboolean switch_host_delayed(gpointer data) > { > SpiceChannel *channel = data; > @@ -1638,6 +1677,7 @@ static const spice_msg_handler main_handlers[] = { > [ SPICE_MSG_MAIN_AGENT_TOKEN ] = main_handle_agent_token, > > [ SPICE_MSG_MAIN_MIGRATE_BEGIN ] = main_handle_migrate_begin, > + [ SPICE_MSG_MAIN_MIGRATE_END ] = main_handle_migrate_end, > [ SPICE_MSG_MAIN_MIGRATE_CANCEL ] = main_handle_migrate_cancel, > [ SPICE_MSG_MAIN_MIGRATE_SWITCH_HOST ] = main_handle_migrate_switch_host, > }; > diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c > index 6420c53..cbd89e1 100644 > --- a/gtk/spice-channel.c > +++ b/gtk/spice-channel.c > @@ -1008,6 +1008,7 @@ static void spice_channel_recv_link_hdr(SpiceChannel > *channel) > goto error; > } > > + SPICE_DEBUG("Peer version: %d:%d", c->peer_hdr.major_version, > c->peer_hdr.minor_version); > if (c->peer_hdr.major_version != c->link_hdr.major_version) { > if (c->peer_hdr.major_version == 1) { > /* enter spice 0.4 mode */ > @@ -1810,11 +1811,26 @@ static void spice_channel_iterate_read(SpiceChannel > *channel) > spice_channel_recv_msg(channel, > (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, > NULL); > break; > + case SPICE_CHANNEL_STATE_MIGRATING: > + return; > default: > g_critical("unknown state %d", c->state); > } > } > > +static gboolean wait_migration(gpointer data) > +{ > + SpiceChannel *channel = SPICE_CHANNEL(data); > + SpiceChannelPrivate *c = channel->priv; > + > + if (c->state != SPICE_CHANNEL_STATE_MIGRATING) { > + SPICE_DEBUG("unfreeze channel %s", c->name); > + return TRUE; > + } > + > + return FALSE; > +} > + > /* coroutine context */ > static gboolean spice_channel_iterate(SpiceChannel *channel) > { > @@ -1822,11 +1838,9 @@ static gboolean spice_channel_iterate(SpiceChannel > *channel) > GIOCondition ret; > > do { > - while (c->state == SPICE_CHANNEL_STATE_MIGRATING) { > - /* freeze coroutine */ > - coroutine_yield(NULL); > - g_return_val_if_fail(c->state != SPICE_CHANNEL_STATE_MIGRATING, > FALSE); > - } > + /* freeze coroutine */ > + if (c->state == SPICE_CHANNEL_STATE_MIGRATING) > + g_condition_wait(wait_migration, channel); > > if (c->has_error) { > SPICE_DEBUG("channel has error, breaking loop"); > @@ -1835,6 +1849,7 @@ static gboolean spice_channel_iterate(SpiceChannel > *channel) > > SPICE_CHANNEL_GET_CLASS(channel)->iterate_write(channel); > ret = g_io_wait_interruptible(&c->wait, c->sock, G_IO_IN); > + > #ifdef WIN32 > /* FIXME: windows gsocket is buggy, it doesn't return correct > condition... */ > ret = g_socket_condition_check(c->sock, G_IO_IN); > @@ -2211,6 +2226,7 @@ void spice_channel_disconnect(SpiceChannel *channel, > SpiceChannelEvent reason) > { > SpiceChannelPrivate *c = SPICE_CHANNEL_GET_PRIVATE(channel); > > + SPICE_DEBUG("channel disconnect %d", reason); > g_return_if_fail(c != NULL); > > if (c->state == SPICE_CHANNEL_STATE_UNCONNECTED) > @@ -2223,13 +2239,11 @@ void spice_channel_disconnect(SpiceChannel *channel, > SpiceChannelEvent reason) > > if (c->state == SPICE_CHANNEL_STATE_MIGRATING) { > c->state = SPICE_CHANNEL_STATE_READY; > - coroutine_yieldto(&c->coroutine, NULL); This looks strange - is this removal related to this patch? > } else > spice_channel_wakeup(channel); > > - if (reason != SPICE_CHANNEL_NONE) { > + if (reason != SPICE_CHANNEL_NONE) > g_signal_emit(G_OBJECT(channel), signals[SPICE_CHANNEL_EVENT], 0, > reason); > - } > } > > static gboolean test_capability(GArray *caps, guint32 cap) > diff --git a/gtk/spice-session-priv.h b/gtk/spice-session-priv.h > index 0851c39..4d52254 100644 > --- a/gtk/spice-session-priv.h > +++ b/gtk/spice-session-priv.h > @@ -78,6 +78,8 @@ struct _SpiceSessionPrivate { > GList *migration_left; > SpiceSessionMigration migration_state; > gboolean disconnecting; > + gboolean migrate_wait_init; > + guint after_main_init; > > display_cache images; > display_cache palettes; > @@ -123,6 +125,8 @@ void spice_session_get_caches(SpiceSession *session, > SpiceGlzDecoderWindow **glz_window); > void spice_session_palettes_clear(SpiceSession *session); > void spice_session_images_clear(SpiceSession *session); > +void spice_session_migrate_end(SpiceSession *session); > +gboolean spice_session_migrate_after_main_init(SpiceSession *session); > > G_END_DECLS > > diff --git a/gtk/spice-session.c b/gtk/spice-session.c > index 2b10635..9432073 100644 > --- a/gtk/spice-session.c > +++ b/gtk/spice-session.c > @@ -146,6 +146,11 @@ spice_session_dispose(GObject *gobject) > s->migration_left = NULL; > } > > + if (s->after_main_init) { > + g_source_remove(s->after_main_init); > + s->after_main_init = 0; > + } > + > g_clear_object(&s->audio_manager); > g_clear_object(&s->gtk_session); > g_clear_object(&s->usb_manager); > @@ -1146,6 +1151,85 @@ void spice_session_channel_migrate(SpiceSession > *session, SpiceChannel *channel) > } > } > > +/* main context */ > +static gboolean after_main_init(gpointer data) > +{ > + SpiceSession *self = data; > + SpiceSessionPrivate *s = self->priv; > + GList *l; > + > + for (l = s->migration_left; l != NULL; ) { > + SpiceChannel *channel = l->data; > + l = l->next; > + > + spice_session_channel_migrate(self, channel); > + channel->priv->state = SPICE_CHANNEL_STATE_READY; > + spice_channel_up(channel); > + } > + > + s->after_main_init = 0; > + return FALSE; > +} > + > +/* coroutine context */ > +G_GNUC_INTERNAL > +gboolean spice_session_migrate_after_main_init(SpiceSession *self) > +{ > + SpiceSessionPrivate *s = self->priv; > + > + if (!s->migrate_wait_init) > + return FALSE; > + > + g_return_val_if_fail(g_list_length(s->migration_left) != 0, FALSE); > + g_return_val_if_fail(s->after_main_init == 0, FALSE); > + > + s->migrate_wait_init = FALSE; > + s->after_main_init = g_idle_add(after_main_init, self); > + > + return TRUE; > +} > + > +/* main context */ > +G_GNUC_INTERNAL > +void spice_session_migrate_end(SpiceSession *self) > +{ > + SpiceSessionPrivate *s = self->priv; > + SpiceMsgOut *out; > + GList *l; > + > + g_return_if_fail(s->migration); > + g_return_if_fail(s->migration->priv->cmain); > + g_return_if_fail(g_list_length(s->migration_left) != 0); > + > + /* disconnect and reset all channels */ > + for (l = s->migration_left; l != NULL; ) { > + SpiceChannel *channel = l->data; > + l = l->next; > + > + /* reset for migration, disconnect */ > + spice_channel_reset(channel, TRUE); > + > + if (SPICE_IS_MAIN_CHANNEL(channel)) { > + /* migrate main to target, so we can start talking */ > + spice_session_channel_migrate(self, channel); > + } else { > + /* freeze other channels */ > + channel->priv->state = SPICE_CHANNEL_STATE_MIGRATING; > + } > + } > + > + spice_session_palettes_clear(self); > + spice_session_images_clear(self); > + glz_decoder_window_clear(s->glz_window); > + > + /* send MIGRATE_END to target */ > + out = spice_msg_out_new(s->cmain, SPICE_MSGC_MAIN_MIGRATE_END); > + spice_msg_out_send(out); > + > + /* now wait after main init for the rest of channels migration */ > + s->migrate_wait_init = TRUE; > +} > + > /** > * spice_session_get_read_only: > * @session: a #SpiceSession > -- > 1.7.7.4 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@lists.freedesktop.org > http://lists.freedesktop.org/mailman/listinfo/spice-devel _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel