On Wed, Aug 15, 2012 at 9:56 AM, Yonit Halperin <yhalperi@xxxxxxxxxx> wrote: > Flow: > (1) *src* main channel coroutine (main_handle_migrate_begin_seamless): > handles SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS; yields to the main loop, > supplying it the destination information needed for connection. > (2) main context (migrate_connect): > Establishes a new session for connecting to the destination. > After all the channels are opened (async), their state, except for > the one of the main channel, is modified to > SPICE_CHANNEL_STATE_MIGRATING (see migrate_channel_event_cb); > no reading is done from the channel during this state. > The dest main channel's state is changed to SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE > > (3) *dest* main channel coroutine: sends to the dest server SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS > (see spice_channel_recv_auth) > (4) *dest* main channel coroutine: recevices SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK. > Then it emits the signal "migration-handshake-done". > (5) main context: when all the dest session channels are connected, and the main channel handshake > is done, we yield to the src main channel coroutine (see > migrate_channel_event_cb and main_migrate_handshake_done_cb) > (6) *src* main channel coroutine: sends to the src server > SPICE_MSGC_MAIN_MIGRATE_(CONNECTED|CONNECTED_SEAMLESS|CONNECT_ERROR) > > For more details see spice-protocol. commit > 1ad5d259cb4b695ec3106de7ccd082e031e7ae11 > --- > gtk/channel-main.c | 163 +++++++++++++++++++++++++++++++++++++++++++-- > gtk/spice-channel-priv.h | 1 + > gtk/spice-channel.c | 17 +++++ > gtk/spice-channel.h | 6 ++- > 4 files changed, 178 insertions(+), 9 deletions(-) > > diff --git a/gtk/channel-main.c b/gtk/channel-main.c > index bb0e361..bdcdbcb 100644 > --- a/gtk/channel-main.c > +++ b/gtk/channel-main.c > @@ -80,6 +80,7 @@ struct _SpiceMainChannelPrivate { > > guint switch_host_delayed_id; > guint migrate_delayed_id; > + guint32 migrate_src_version; > }; > > typedef struct spice_migrate spice_migrate; > @@ -90,6 +91,12 @@ struct spice_migrate { > SpiceSession *session; > guint nchannels; > SpiceChannel *channel; > + bool do_seamless; /* used as input and output for the seamless migration handshake. > + input: whether to send to the dest SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS > + output: whether the dest approved seamless migration > + (SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK/NACK) > + */ > + uint32_t src_mig_version; > }; > > G_DEFINE_TYPE(SpiceMainChannel, spice_main_channel, SPICE_TYPE_CHANNEL) > @@ -121,6 +128,7 @@ enum { > SPICE_MAIN_CLIPBOARD_SELECTION_REQUEST, > SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE, > SPICE_MIGRATION_STARTED, > + SPICE_MIGRATION_HANDSHAKE_DONE, > SPICE_MAIN_LAST_SIGNAL, > }; > > @@ -131,6 +139,9 @@ static void agent_send_msg_queue(SpiceMainChannel *channel); > static void agent_free_msg_queue(SpiceMainChannel *channel); > static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event, > gpointer data); > +static void main_migrate_handshake_done_cb(SpiceChannel *channel, gboolean seamless, > + gpointer data); > +static void spice_main_channel_send_migration_handshake(SpiceChannel *channel); > > /* ------------------------------------------------------------------ */ > > @@ -164,6 +175,7 @@ static void spice_main_channel_reset_capabilties(SpiceChannel *channel) > spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEMI_SEAMLESS_MIGRATE); > spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_NAME_AND_UUID); > spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_AGENT_CONNECTED_TOKENS); > + spice_channel_set_capability(SPICE_CHANNEL(channel), SPICE_MAIN_CAP_SEAMLESS_MIGRATE); > } > > static void spice_main_channel_init(SpiceMainChannel *channel) > @@ -327,6 +339,7 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) > channel_class->iterate_write = spice_channel_iterate_write; > channel_class->channel_reset = spice_main_channel_reset; > channel_class->channel_reset_capabilities = spice_main_channel_reset_capabilties; > + channel_class->channel_send_migration_handshake = spice_main_channel_send_migration_handshake; > > /** > * SpiceMainChannel:mouse-mode: > @@ -659,6 +672,27 @@ static void spice_main_channel_class_init(SpiceMainChannelClass *klass) > G_TYPE_OBJECT); > > > + /** > + * SpiceMainChannel::migration-handshake-done: > + * @main: the #SpiceMainChannel that emitted the signal > + * @session: a migration #SpiceSession > + * > + * Inform when seamless migration handshake has completed, and > + * its result: seamless or semi-seamless migration. > + * > + * Since: 0.13 > + **/ > + signals[SPICE_MIGRATION_HANDSHAKE_DONE] = > + g_signal_new("migration-handshake-done", > + G_OBJECT_CLASS_TYPE(gobject_class), > + G_SIGNAL_RUN_LAST, > + 0, > + NULL, NULL, > + g_cclosure_marshal_VOID__BOOLEAN, > + G_TYPE_NONE, > + 1, > + G_TYPE_BOOLEAN); > + > g_type_class_add_private(klass, sizeof(SpiceMainChannelPrivate)); > } > > @@ -714,6 +748,10 @@ struct SPICE_MAIN_CLIPBOARD_SELECTION_RELEASE { > guint8 selection; > }; > > +struct SPICE_MIGRATION_HANDSHAKE_DONE { > + gboolean seamless; > +}; > + > /* main context */ > static void do_emit_main_context(GObject *object, int signum, gpointer params) > { > @@ -766,6 +804,11 @@ static void do_emit_main_context(GObject *object, int signum, gpointer params) > p->selection); > break; > } > + case SPICE_MIGRATION_HANDSHAKE_DONE: { > + struct SPICE_MIGRATION_HANDSHAKE_DONE *p = params; > + g_signal_emit(object, signals[signum], 0, > + p->seamless); > + } > default: > g_warn_if_reached(); > } > @@ -1573,6 +1616,25 @@ static SpiceChannel* migrate_channel_connect(spice_migrate *mig, int type, int i > return newc; > } > > +/* coroutine context */ > +static void spice_main_channel_send_migration_handshake(SpiceChannel *channel) > +{ > + SpiceMainChannelPrivate *c = SPICE_MAIN_CHANNEL(channel)->priv; > + > + if (!spice_channel_test_capability(channel, SPICE_MAIN_CAP_SEAMLESS_MIGRATE)) { > + emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, false); > + } else { > + SpiceMsgcMainMigrateDstDoSeamless msg_data; > + SpiceMsgOut *msg_out; > + > + msg_data.src_version = c->migrate_src_version; > + > + msg_out = spice_msg_out_new(channel, SPICE_MSGC_MAIN_MIGRATE_DST_DO_SEAMLESS); > + msg_out->marshallers->msgc_main_migrate_dst_do_seamless(msg_out->marshaller, &msg_data); > + spice_msg_out_send_internal(msg_out); > + } > +} > + > /* main context */ > static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent event, > gpointer data) > @@ -1588,9 +1650,20 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev > > switch (event) { > case SPICE_CHANNEL_OPENED: > - c->state = SPICE_CHANNEL_STATE_MIGRATING; > > if (c->channel_type == SPICE_CHANNEL_MAIN) { > + if (mig->do_seamless) { > + SpiceMainChannelPrivate *main_priv = SPICE_MAIN_CHANNEL(channel)->priv; > + > + c->state = SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE; > + main_priv->migrate_src_version = mig->src_mig_version; > + g_signal_connect(channel, "migration-handshake-done", > + G_CALLBACK(main_migrate_handshake_done_cb), > + mig); We shouldn't add a public signal API for an internal condition wait. You can either call directly an internal method or use g_coroutine_condition_wait() depending on the needs. > + } else { > + c->state = SPICE_CHANNEL_STATE_MIGRATING; > + mig->nchannels--; > + } > /* now connect the rest of the channels */ > GList *channels, *l; > l = channels = spice_session_get_channels(session); > @@ -1602,9 +1675,11 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev > migrate_channel_connect(mig, curc->channel_type, curc->channel_id); > } > g_list_free(channels); > + } else { > + c->state = SPICE_CHANNEL_STATE_MIGRATING; > + mig->nchannels--; > } > > - mig->nchannels--; > SPICE_DEBUG("migration: channel opened chan:%p, left %d", channel, mig->nchannels); > if (mig->nchannels == 0) > coroutine_yieldto(mig->from, NULL); > @@ -1616,6 +1691,25 @@ static void migrate_channel_event_cb(SpiceChannel *channel, SpiceChannelEvent ev > } > } > > +/* main context */ > +static void main_migrate_handshake_done_cb(SpiceChannel *channel, gboolean seamless, > + gpointer data) > +{ > + spice_migrate *mig = data; > + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; > + > + g_return_if_fail(c->channel_type == SPICE_CHANNEL_MAIN); > + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE); > + g_return_if_fail(mig->do_seamless == true); > + > + g_signal_handlers_disconnect_by_func(channel, main_migrate_handshake_done_cb, data); > + mig->do_seamless = seamless; > + c->state = SPICE_CHANNEL_STATE_MIGRATING; > + mig->nchannels--; > + if (mig->nchannels == 0) > + coroutine_yieldto(mig->from, NULL); > +} > + > #ifdef __GNUC__ > typedef struct __attribute__ ((__packed__)) OldRedMigrationBegin { > #else > @@ -1703,16 +1797,19 @@ static gboolean migrate_connect(gpointer data) > } > > /* coroutine context */ > -static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) > +static void main_migrate_connect(SpiceChannel *channel, > + SpiceMigrationDstInfo *dst_info, bool do_seamless, > + uint32_t src_mig_version) > { > - SpiceMsgMainMigrationBegin *msg = spice_msg_in_parsed(in); > spice_migrate mig = { 0, }; > SpiceMsgOut *out; > int reply_type; > > mig.channel = channel; > - mig.info = &msg->dst_info; > + mig.info = dst_info; > mig.from = coroutine_self(); > + mig.do_seamless = do_seamless; > + mig.src_mig_version = src_mig_version; > > /* no need to track idle, call is sync for this coroutine */ > g_idle_add(migrate_connect, &mig); > @@ -1725,8 +1822,13 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) > reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECT_ERROR; > spice_session_disconnect(mig.session); > } else { > - SPICE_DEBUG("migration: connections all ok"); > - reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED; > + if (mig.do_seamless) { > + SPICE_DEBUG("migration (seamless): connections all ok"); > + reply_type = SPICE_MSGC_MAIN_MIGRATE_CONNECTED_SEAMLESS; > + } else { > + 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); > } > g_object_unref(mig.session); > @@ -1735,6 +1837,38 @@ static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) > spice_msg_out_send(out); > } > > +/* coroutine context */ > +static void main_handle_migrate_begin(SpiceChannel *channel, SpiceMsgIn *in) > +{ > + SpiceMsgMainMigrationBegin *msg = spice_msg_in_parsed(in); > + > + main_migrate_connect(channel, &msg->dst_info, false, 0); > +} > + > +/* coroutine context */ > +static void main_handle_migrate_begin_seamless(SpiceChannel *channel, SpiceMsgIn *in) > +{ > + SpiceMsgMainMigrateBeginSeamless *msg = spice_msg_in_parsed(in); > + > + main_migrate_connect(channel, &msg->dst_info, true, msg->src_mig_version); > +} > + > +static void main_handle_migrate_dst_seamless_ack(SpiceChannel *channel, SpiceMsgIn *in) > +{ > + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; > + > + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE); > + emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, true); > +} > + > +static void main_handle_migrate_dst_seamless_nack(SpiceChannel *channel, SpiceMsgIn *in) > +{ > + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; > + > + g_return_if_fail(c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE); > + emit_main_context(channel, SPICE_MIGRATION_HANDSHAKE_DONE, false); > +} > + > /* main context */ > static gboolean migrate_delayed(gpointer data) > { > @@ -1848,7 +1982,10 @@ static const spice_msg_handler main_handlers[] = { > [ 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, > - [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ] = main_handle_agent_connected_tokens, > + [ SPICE_MSG_MAIN_AGENT_CONNECTED_TOKENS ] = main_handle_agent_connected_tokens, > + [ SPICE_MSG_MAIN_MIGRATE_BEGIN_SEAMLESS ] = main_handle_migrate_begin_seamless, > + [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK] = main_handle_migrate_dst_seamless_ack, > + [ SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK] = main_handle_migrate_dst_seamless_nack, > }; > > /* coroutine context */ > @@ -1856,11 +1993,21 @@ static void spice_main_handle_msg(SpiceChannel *channel, SpiceMsgIn *msg) > { > int type = spice_msg_in_type(msg); > SpiceChannelClass *parent_class; > + SpiceChannelPrivate *c = SPICE_CHANNEL(channel)->priv; > > g_return_if_fail(type < SPICE_N_ELEMENTS(main_handlers)); > > parent_class = SPICE_CHANNEL_CLASS(spice_main_channel_parent_class); > > + if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) { > + if (type != SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_ACK && > + type != SPICE_MSG_MAIN_MIGRATE_DST_SEAMLESS_NACK) { > + g_critical("unexpected msg (%d)." > + "Only MIGRATE_DST_SEAMLESS_ACK/NACK are allowed", type); > + return; > + } > + } > + > if (main_handlers[type] != NULL) > main_handlers[type](channel, msg); > else if (parent_class->handle_msg) > diff --git a/gtk/spice-channel-priv.h b/gtk/spice-channel-priv.h > index 4f094d8..8ed79fa 100644 > --- a/gtk/spice-channel-priv.h > +++ b/gtk/spice-channel-priv.h > @@ -71,6 +71,7 @@ enum spice_channel_state { > SPICE_CHANNEL_STATE_READY, > SPICE_CHANNEL_STATE_SWITCHING, > SPICE_CHANNEL_STATE_MIGRATING, > + SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE, > }; > > struct _SpiceChannelPrivate { > diff --git a/gtk/spice-channel.c b/gtk/spice-channel.c > index 54d406d..5f37cbc 100644 > --- a/gtk/spice-channel.c > +++ b/gtk/spice-channel.c > @@ -49,6 +49,7 @@ static void spice_channel_send_link(SpiceChannel *channel); > static void channel_disconnect(SpiceChannel *channel); > static void channel_reset(SpiceChannel *channel, gboolean migrating); > static void spice_channel_reset_capabilities(SpiceChannel *channel); > +static void spice_channel_send_migration_handshake(SpiceChannel *channel); > > /** > * SECTION:spice-channel > @@ -1080,6 +1081,10 @@ static void spice_channel_recv_auth(SpiceChannel *channel) > > emit_main_context(channel, SPICE_CHANNEL_EVENT, SPICE_CHANNEL_OPENED); > > + if (c->state == SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE) { > + spice_channel_send_migration_handshake(channel); > + } > + > if (c->state != SPICE_CHANNEL_STATE_MIGRATING) > spice_channel_up(channel); > } > @@ -2002,6 +2007,7 @@ static void spice_channel_iterate_read(SpiceChannel *channel) > spice_channel_recv_auth(channel); > break; > case SPICE_CHANNEL_STATE_READY: > + case SPICE_CHANNEL_STATE_MIGRATION_HANDSHAKE: > spice_channel_recv_msg(channel, > (handler_msg_in)SPICE_CHANNEL_GET_CLASS(channel)->handle_msg, NULL); > break; > @@ -2654,3 +2660,14 @@ static void spice_channel_reset_capabilities(SpiceChannel *channel) > SPICE_CHANNEL_GET_CLASS(channel)->channel_reset_capabilities(channel); > } > } > + > +static void spice_channel_send_migration_handshake(SpiceChannel *channel) > +{ > + SpiceChannelPrivate *c = SPICE_CHANNEL_GET_PRIVATE(channel); > + > + if (SPICE_CHANNEL_GET_CLASS(channel)->channel_send_migration_handshake) { > + SPICE_CHANNEL_GET_CLASS(channel)->channel_send_migration_handshake(channel); > + } else { > + c->state = SPICE_CHANNEL_STATE_MIGRATING; > + } > +} > diff --git a/gtk/spice-channel.h b/gtk/spice-channel.h > index 30ff1ba..d40b844 100644 > --- a/gtk/spice-channel.h > +++ b/gtk/spice-channel.h > @@ -90,11 +90,15 @@ struct _SpiceChannelClass > void (*channel_reset)(SpiceChannel *channel, gboolean migrating); > void (*channel_reset_capabilities)(SpiceChannel *channel); > > + /*< private >*/ > + /* virtual methods, coroutine context */ > + void (*channel_send_migration_handshake)(SpiceChannel *channel); > + > /* > * If adding fields to this struct, remove corresponding > * amount of padding to avoid changing overall struct size > */ > - gchar _spice_reserved[SPICE_RESERVED_PADDING]; > + gchar _spice_reserved[SPICE_RESERVED_PADDING - sizeof(void *)]; > }; > > GType spice_channel_get_type(void); > -- > 1.7.7.6 > > _______________________________________________ > Spice-devel mailing list > Spice-devel@xxxxxxxxxxxxxxxxxxxxx > http://lists.freedesktop.org/mailman/listinfo/spice-devel -- Marc-André Lureau _______________________________________________ Spice-devel mailing list Spice-devel@xxxxxxxxxxxxxxxxxxxxx http://lists.freedesktop.org/mailman/listinfo/spice-devel