Move RedClient to a separate file

Also move the RedClient struct out of the header to avoid accessing the
internals from other files.

Signed-off-by: Jonathon Jongsma <jjongsma@redhat.com>
Acked-by: Frediano Ziglio <fziglio@redhat.com>
This commit is contained in:
Jonathon Jongsma 2016-10-26 14:47:35 -05:00 committed by Frediano Ziglio
parent 1f4705e130
commit bc8d967e67
17 changed files with 390 additions and 295 deletions

View File

@ -101,6 +101,8 @@ libserver_la_SOURCES = \
red-channel-client.c \
red-channel-client.h \
red-channel-client-private.h \
red-client.c \
red-client.h \
dummy-channel.c \
dummy-channel.h \
dummy-channel-client.c \

View File

@ -23,7 +23,7 @@
#include <config.h>
#include <inttypes.h>
#include "char-device.h"
#include "red-channel.h"
#include "red-client.h"
#include "reds.h"
#include "glib-compat.h"
@ -711,8 +711,10 @@ static RedCharDeviceClient *red_char_device_client_new(RedClient *client,
dev_client->max_send_queue_size = max_send_queue_size;
dev_client->do_flow_control = do_flow_control;
if (do_flow_control) {
RedsState *reds = red_client_get_server(client);
dev_client->wait_for_tokens_timer =
reds_core_timer_add(client->reds, device_client_wait_for_tokens_timeout,
reds_core_timer_add(reds, device_client_wait_for_tokens_timeout,
dev_client);
if (!dev_client->wait_for_tokens_timer) {
spice_error("failed to create wait for tokens timer");

View File

@ -25,7 +25,7 @@
#include "common-graphics-channel.h"
#include "dcc.h"
#include "main-channel-client.h"
#include "red-client.h"
#define CHANNEL_RECEIVE_BUF_SIZE 1024

View File

@ -23,6 +23,7 @@
#include "display-channel.h"
#include "display-channel-private.h"
#include "red-channel-client-private.h"
#include "red-client.h"
#include "main-channel-client.h"
#include "spice-server-enums.h"

View File

@ -20,6 +20,7 @@
#include "dummy-channel-client.h"
#include "red-channel.h"
#include "red-client.h"
static void dummy_channel_client_initable_interface_init(GInitableIface *iface);

View File

@ -40,6 +40,7 @@
#include "reds-stream.h"
#include "red-channel.h"
#include "red-channel-client.h"
#include "red-client.h"
#include "inputs-channel-client.h"
#include "main-channel-client.h"
#include "inputs-channel.h"

View File

@ -24,6 +24,7 @@
#include "main-channel-client.h"
#include "main-channel.h"
#include "red-channel-client.h"
#include "red-client.h"
#include "reds.h"
#define NET_TEST_WARMUP_BYTES 0

View File

@ -24,6 +24,7 @@
#include "red-common.h"
#include "reds.h"
#include "red-channel-client.h"
#include "red-client.h"
#include "main-channel.h"
#include "main-channel-client.h"

View File

@ -24,7 +24,7 @@
#include "red-common.h"
#include "dispatcher.h"
#include "main-dispatcher.h"
#include "red-channel.h"
#include "red-client.h"
#include "reds.h"
/*
@ -265,7 +265,7 @@ void main_dispatcher_client_disconnect(MainDispatcher *self, RedClient *client)
{
MainDispatcherClientDisconnectMessage msg;
if (!client->disconnecting) {
if (!red_client_is_disconnecting(client)) {
spice_debug("client %p", client);
msg.client = red_client_ref(client);
dispatcher_send_message(DISPATCHER(self), MAIN_DISPATCHER_CLIENT_DISCONNECT,

View File

@ -35,7 +35,7 @@
#include "red-channel-client.h"
#include "red-channel-client-private.h"
#include "red-channel.h"
#include "red-client.h"
#include "glib-compat.h"
static const SpiceDataHeaderOpaque full_header_wrapper;

View File

@ -31,9 +31,6 @@
#include "main-dispatcher.h"
#include "utils.h"
#define FOREACH_CHANNEL_CLIENT(_client, _iter, _data) \
GLIST_FOREACH((_client ? (_client)->channels : NULL), _iter, RedChannelClient, _data)
/*
* Lifetime of RedChannel, RedChannelClient and RedClient:
* RedChannel is created and destroyed by the calls to
@ -394,29 +391,6 @@ int red_channel_test_remote_cap(RedChannel *channel, uint32_t cap)
return TRUE;
}
/* returns TRUE If all channels are finished migrating, FALSE otherwise */
gboolean red_client_seamless_migration_done_for_channel(RedClient *client)
{
gboolean ret = FALSE;
pthread_mutex_lock(&client->lock);
client->num_migrated_channels--;
/* we assume we always have at least one channel who has migration data transfer,
* otherwise, this flag will never be set back to FALSE*/
if (!client->num_migrated_channels) {
client->during_target_migrate = FALSE;
client->seamless_migrate = FALSE;
/* migration completion might have been triggered from a different thread
* than the main thread */
main_dispatcher_seamless_migrate_dst_complete(reds_get_main_dispatcher(client->reds),
client);
ret = TRUE;
}
pthread_mutex_unlock(&client->lock);
return ret;
}
int red_channel_is_waiting_for_migrate_data(RedChannel *channel)
{
RedChannelClient *rcc;
@ -575,14 +549,6 @@ void red_channel_remove_client(RedChannel *channel, RedChannelClient *rcc)
// TODO: should we set rcc->channel to NULL???
}
void red_client_remove_channel(RedChannelClient *rcc)
{
RedClient *client = red_channel_client_get_client(rcc);
pthread_mutex_lock(&client->lock);
client->channels = g_list_remove(client->channels, rcc);
pthread_mutex_unlock(&client->lock);
}
void red_channel_disconnect(RedChannel *channel)
{
g_list_foreach(channel->priv->clients, (GFunc)red_channel_client_disconnect, NULL);
@ -672,201 +638,6 @@ int red_channel_no_item_being_sent(RedChannel *channel)
return TRUE;
}
/*
* RedClient implementation - kept in red-channel.c because they are
* pretty tied together.
*/
RedClient *red_client_new(RedsState *reds, int migrated)
{
RedClient *client;
client = spice_malloc0(sizeof(RedClient));
client->reds = reds;
pthread_mutex_init(&client->lock, NULL);
client->thread_id = pthread_self();
client->during_target_migrate = migrated;
client->refs = 1;
return client;
}
RedClient *red_client_ref(RedClient *client)
{
spice_assert(client);
g_atomic_int_inc(&client->refs);
return client;
}
RedClient *red_client_unref(RedClient *client)
{
if (g_atomic_int_dec_and_test(&client->refs)) {
spice_debug("release client=%p", client);
pthread_mutex_destroy(&client->lock);
free(client);
return NULL;
}
return client;
}
void red_client_set_migration_seamless(RedClient *client) // dest
{
GListIter iter;
RedChannelClient *rcc;
spice_assert(client->during_target_migrate);
pthread_mutex_lock(&client->lock);
client->seamless_migrate = TRUE;
/* update channel clients that got connected before the migration
* type was set. red_client_add_channel will handle newer channel clients */
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
if (red_channel_client_set_migration_seamless(rcc))
client->num_migrated_channels++;
}
pthread_mutex_unlock(&client->lock);
}
void red_client_migrate(RedClient *client)
{
GListIter iter;
RedChannelClient *rcc;
RedChannel *channel;
spice_printerr("migrate client with #channels %d", g_list_length(client->channels));
if (!pthread_equal(pthread_self(), client->thread_id)) {
spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
"If one of the threads is != io-thread && != vcpu-thread,"
" this might be a BUG",
client->thread_id, pthread_self());
}
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
channel = red_channel_client_get_channel(rcc);
if (red_channel_client_is_connected(rcc)) {
channel->priv->client_cbs.migrate(rcc);
}
}
}
void red_client_destroy(RedClient *client)
{
GListIter iter;
RedChannelClient *rcc;
spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->channels));
if (!pthread_equal(pthread_self(), client->thread_id)) {
spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
"If one of the threads is != io-thread && != vcpu-thread,"
" this might be a BUG",
client->thread_id,
pthread_self());
}
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
RedChannel *channel;
// some channels may be in other threads, so disconnection
// is not synchronous.
channel = red_channel_client_get_channel(rcc);
red_channel_client_set_destroying(rcc);
// some channels may be in other threads. However we currently
// assume disconnect is synchronous (we changed the dispatcher
// to wait for disconnection)
// TODO: should we go back to async. For this we need to use
// ref count for channel clients.
channel->priv->client_cbs.disconnect(rcc);
spice_assert(red_channel_client_pipe_is_empty(rcc));
spice_assert(red_channel_client_no_item_being_sent(rcc));
red_channel_client_destroy(rcc);
}
red_client_unref(client);
}
/* client->lock should be locked */
RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
{
GListIter iter;
RedChannelClient *rcc;
RedChannelClient *ret = NULL;
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
RedChannel *channel;
channel = red_channel_client_get_channel(rcc);
if (channel->priv->type == type && channel->priv->id == id) {
ret = rcc;
break;
}
}
return ret;
}
gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error)
{
uint32_t type, id;
RedChannel *channel;
gboolean result = TRUE;
spice_assert(rcc && client);
channel = red_channel_client_get_channel(rcc);
pthread_mutex_lock(&client->lock);
g_object_get(channel, "channel-type", &type, "id", &id, NULL);
if (red_client_get_channel(client, type, id)) {
g_set_error(error,
SPICE_SERVER_ERROR,
SPICE_SERVER_ERROR_FAILED,
"Client %p: duplicate channel type %d id %d",
client, type, id);
result = FALSE;
goto cleanup;
}
client->channels = g_list_prepend(client->channels, rcc);
if (client->during_target_migrate && client->seamless_migrate) {
if (red_channel_client_set_migration_seamless(rcc))
client->num_migrated_channels++;
}
cleanup:
pthread_mutex_unlock(&client->lock);
return result;
}
MainChannelClient *red_client_get_main(RedClient *client) {
return client->mcc;
}
void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
client->mcc = mcc;
}
void red_client_semi_seamless_migrate_complete(RedClient *client)
{
GListIter iter;
RedChannelClient *rcc;
pthread_mutex_lock(&client->lock);
if (!client->during_target_migrate || client->seamless_migrate) {
spice_error("unexpected");
pthread_mutex_unlock(&client->lock);
return;
}
client->during_target_migrate = FALSE;
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
red_channel_client_semi_seamless_migration_complete(rcc);
}
pthread_mutex_unlock(&client->lock);
reds_on_client_semi_seamless_migrate_complete(client->reds, client);
}
/* should be called only from the main thread */
int red_client_during_migrate_at_target(RedClient *client)
{
int ret;
pthread_mutex_lock(&client->lock);
ret = client->during_target_migrate;
pthread_mutex_unlock(&client->lock);
return ret;
}
/*
* Functions to push the same item to multiple pipes.
*/
@ -1060,3 +831,14 @@ const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *sel
{
return &self->priv->local_caps;
}
void red_channel_migrate_client(RedChannel *channel, RedChannelClient *rcc)
{
channel->priv->client_cbs.migrate(rcc);
}
void red_channel_disconnect_client(RedChannel *channel, RedChannelClient *rcc)
{
channel->priv->client_cbs.disconnect(rcc);
}

View File

@ -325,64 +325,6 @@ OutgoingHandlerInterface* red_channel_get_outgoing_handler(RedChannel *self);
const RedChannelCapabilities* red_channel_get_local_capabilities(RedChannel *self);
struct RedClient {
RedsState *reds;
GList *channels;
MainChannelClient *mcc;
pthread_mutex_t lock; // different channels can be in different threads
pthread_t thread_id;
int disconnecting;
/* Note that while semi-seamless migration is conducted by the main thread, seamless migration
* involves all channels, and thus the related variables can be accessed from different
* threads */
int during_target_migrate; /* if seamless=TRUE, migration_target is turned off when all
the clients received their migration data. Otherwise (semi-seamless),
it is turned off, when red_client_semi_seamless_migrate_complete
is called */
int seamless_migrate;
int num_migrated_channels; /* for seamless - number of channels that wait for migrate data*/
int refs;
};
RedClient *red_client_new(RedsState *reds, int migrated);
/*
* disconnects all the client's channels (should be called from the client's thread)
*/
void red_client_destroy(RedClient *client);
RedClient *red_client_ref(RedClient *client);
/*
* releases the client resources when refs == 0.
* We assume the red_client_destroy was called before
* we reached refs==0
*/
RedClient *red_client_unref(RedClient *client);
/* client->lock should be locked */
gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
void red_client_remove_channel(RedChannelClient *rcc);
RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
MainChannelClient *red_client_get_main(RedClient *client);
// main should be set once before all the other channels are created
void red_client_set_main(RedClient *client, MainChannelClient *mcc);
/* called when the migration handshake results in seamless migration (dst side).
* By default we assume semi-seamless */
void red_client_set_migration_seamless(RedClient *client);
void red_client_semi_seamless_migrate_complete(RedClient *client); /* dst side */
/* TRUE if the migration is seamless and there are still channels that wait from migration data.
* Or, during semi-seamless migration, and the main channel still waits for MIGRATE_END
* from the client.
* Note: Call it only from the main thread */
int red_client_during_migrate_at_target(RedClient *client);
void red_client_migrate(RedClient *client);
gboolean red_client_seamless_migration_done_for_channel(RedClient *client);
/*
* blocking functions.
*
@ -394,6 +336,10 @@ gboolean red_client_seamless_migration_done_for_channel(RedClient *client);
int red_channel_wait_all_sent(RedChannel *channel,
int64_t timeout);
/* wrappers for client callbacks */
void red_channel_migrate_client(RedChannel *channel, RedChannelClient *rcc);
void red_channel_disconnect_client(RedChannel *channel, RedChannelClient *rcc);
#define CHANNEL_BLOCKED_SLEEP_DURATION 10000 //micro
G_END_DECLS

290
server/red-client.c Normal file
View File

@ -0,0 +1,290 @@
/*
Copyright (C) 2009-2016 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */
#include "red-channel.h"
#include "red-client.h"
#include "reds.h"
#define FOREACH_CHANNEL_CLIENT(_client, _iter, _data) \
GLIST_FOREACH((_client ? (_client)->channels : NULL), _iter, RedChannelClient, _data)
struct RedClient {
RedsState *reds;
GList *channels;
MainChannelClient *mcc;
pthread_mutex_t lock; // different channels can be in different threads
pthread_t thread_id;
int disconnecting;
/* Note that while semi-seamless migration is conducted by the main thread, seamless migration
* involves all channels, and thus the related variables can be accessed from different
* threads */
/* if seamless=TRUE, migration_target is turned off when all
* the clients received their migration data. Otherwise (semi-seamless),
* it is turned off, when red_client_semi_seamless_migrate_complete
* is called */
int during_target_migrate;
int seamless_migrate;
int num_migrated_channels; /* for seamless - number of channels that wait for migrate data*/
int refs;
};
RedClient *red_client_ref(RedClient *client)
{
spice_assert(client);
g_atomic_int_inc(&client->refs);
return client;
}
RedClient *red_client_unref(RedClient *client)
{
if (g_atomic_int_dec_and_test(&client->refs)) {
spice_debug("release client=%p", client);
pthread_mutex_destroy(&client->lock);
free(client);
return NULL;
}
return client;
}
RedClient *red_client_new(RedsState *reds, int migrated)
{
RedClient *client;
client = spice_malloc0(sizeof(RedClient));
client->reds = reds;
pthread_mutex_init(&client->lock, NULL);
client->thread_id = pthread_self();
client->during_target_migrate = migrated;
client->refs = 1;
return client;
}
void red_client_set_migration_seamless(RedClient *client) // dest
{
GListIter iter;
RedChannelClient *rcc;
spice_assert(client->during_target_migrate);
pthread_mutex_lock(&client->lock);
client->seamless_migrate = TRUE;
/* update channel clients that got connected before the migration
* type was set. red_client_add_channel will handle newer channel clients */
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
if (red_channel_client_set_migration_seamless(rcc)) {
client->num_migrated_channels++;
}
}
pthread_mutex_unlock(&client->lock);
}
void red_client_migrate(RedClient *client)
{
GListIter iter;
RedChannelClient *rcc;
RedChannel *channel;
spice_printerr("migrate client with #channels %d", g_list_length(client->channels));
if (!pthread_equal(pthread_self(), client->thread_id)) {
spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
"If one of the threads is != io-thread && != vcpu-thread,"
" this might be a BUG",
client->thread_id, pthread_self());
}
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
channel = red_channel_client_get_channel(rcc);
if (red_channel_client_is_connected(rcc)) {
red_channel_migrate_client(channel, rcc);
}
}
}
void red_client_destroy(RedClient *client)
{
GListIter iter;
RedChannelClient *rcc;
spice_printerr("destroy client %p with #channels=%d", client, g_list_length(client->channels));
if (!pthread_equal(pthread_self(), client->thread_id)) {
spice_warning("client->thread_id (0x%lx) != pthread_self (0x%lx)."
"If one of the threads is != io-thread && != vcpu-thread,"
" this might be a BUG",
client->thread_id,
pthread_self());
}
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
RedChannel *channel;
// some channels may be in other threads, so disconnection
// is not synchronous.
channel = red_channel_client_get_channel(rcc);
red_channel_client_set_destroying(rcc);
// some channels may be in other threads. However we currently
// assume disconnect is synchronous (we changed the dispatcher
// to wait for disconnection)
// TODO: should we go back to async. For this we need to use
// ref count for channel clients.
red_channel_disconnect_client(channel, rcc);
spice_assert(red_channel_client_pipe_is_empty(rcc));
spice_assert(red_channel_client_no_item_being_sent(rcc));
red_channel_client_destroy(rcc);
}
red_client_unref(client);
}
/* client->lock should be locked */
RedChannelClient *red_client_get_channel(RedClient *client, int type, int id)
{
GListIter iter;
RedChannelClient *rcc;
RedChannelClient *ret = NULL;
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
int channel_type, channel_id;
RedChannel *channel;
channel = red_channel_client_get_channel(rcc);
g_object_get(channel, "channel-type", &channel_type, "id", &channel_id, NULL);
if (channel_type == type && channel_id == id) {
ret = rcc;
break;
}
}
return ret;
}
gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error)
{
uint32_t type, id;
RedChannel *channel;
gboolean result = TRUE;
spice_assert(rcc && client);
channel = red_channel_client_get_channel(rcc);
pthread_mutex_lock(&client->lock);
g_object_get(channel, "channel-type", &type, "id", &id, NULL);
if (red_client_get_channel(client, type, id)) {
g_set_error(error,
SPICE_SERVER_ERROR,
SPICE_SERVER_ERROR_FAILED,
"Client %p: duplicate channel type %d id %d",
client, type, id);
result = FALSE;
goto cleanup;
}
client->channels = g_list_prepend(client->channels, rcc);
if (client->during_target_migrate && client->seamless_migrate) {
if (red_channel_client_set_migration_seamless(rcc)) {
client->num_migrated_channels++;
}
}
cleanup:
pthread_mutex_unlock(&client->lock);
return result;
}
MainChannelClient *red_client_get_main(RedClient *client) {
return client->mcc;
}
void red_client_set_main(RedClient *client, MainChannelClient *mcc) {
client->mcc = mcc;
}
void red_client_semi_seamless_migrate_complete(RedClient *client)
{
GListIter iter;
RedChannelClient *rcc;
pthread_mutex_lock(&client->lock);
if (!client->during_target_migrate || client->seamless_migrate) {
spice_error("unexpected");
pthread_mutex_unlock(&client->lock);
return;
}
client->during_target_migrate = FALSE;
FOREACH_CHANNEL_CLIENT(client, iter, rcc) {
red_channel_client_semi_seamless_migration_complete(rcc);
}
pthread_mutex_unlock(&client->lock);
reds_on_client_semi_seamless_migrate_complete(client->reds, client);
}
/* should be called only from the main thread */
int red_client_during_migrate_at_target(RedClient *client)
{
int ret;
pthread_mutex_lock(&client->lock);
ret = client->during_target_migrate;
pthread_mutex_unlock(&client->lock);
return ret;
}
void red_client_remove_channel(RedChannelClient *rcc)
{
RedClient *client = red_channel_client_get_client(rcc);
pthread_mutex_lock(&client->lock);
client->channels = g_list_remove(client->channels, rcc);
pthread_mutex_unlock(&client->lock);
}
/* returns TRUE If all channels are finished migrating, FALSE otherwise */
gboolean red_client_seamless_migration_done_for_channel(RedClient *client)
{
gboolean ret = FALSE;
pthread_mutex_lock(&client->lock);
client->num_migrated_channels--;
/* we assume we always have at least one channel who has migration data transfer,
* otherwise, this flag will never be set back to FALSE*/
if (!client->num_migrated_channels) {
client->during_target_migrate = FALSE;
client->seamless_migrate = FALSE;
/* migration completion might have been triggered from a different thread
* than the main thread */
main_dispatcher_seamless_migrate_dst_complete(reds_get_main_dispatcher(client->reds),
client);
ret = TRUE;
}
pthread_mutex_unlock(&client->lock);
return ret;
}
gboolean red_client_is_disconnecting(RedClient *client)
{
return client->disconnecting;
}
void red_client_set_disconnecting(RedClient *client)
{
client->disconnecting = TRUE;
}
RedsState *red_client_get_server(RedClient *client)
{
return client->reds;
}

65
server/red-client.h Normal file
View File

@ -0,0 +1,65 @@
/*
Copyright (C) 2009-2015 Red Hat, Inc.
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _H_RED_CLIENT
#define _H_RED_CLIENT
#include "main-channel-client.h"
RedClient *red_client_new(RedsState *reds, int migrated);
/*
* disconnects all the client's channels (should be called from the client's thread)
*/
void red_client_destroy(RedClient *client);
RedClient *red_client_ref(RedClient *client);
/*
* releases the client resources when refs == 0.
* We assume the red_client_destroy was called before
* we reached refs==0
*/
RedClient *red_client_unref(RedClient *client);
gboolean red_client_add_channel(RedClient *client, RedChannelClient *rcc, GError **error);
void red_client_remove_channel(RedChannelClient *rcc);
RedChannelClient *red_client_get_channel(RedClient *client, int type, int id);
MainChannelClient *red_client_get_main(RedClient *client);
// main should be set once before all the other channels are created
void red_client_set_main(RedClient *client, MainChannelClient *mcc);
/* called when the migration handshake results in seamless migration (dst side).
* By default we assume semi-seamless */
void red_client_set_migration_seamless(RedClient *client);
void red_client_semi_seamless_migrate_complete(RedClient *client); /* dst side */
gboolean red_client_seamless_migration_done_for_channel(RedClient *client);
/* TRUE if the migration is seamless and there are still channels that wait from migration data.
* Or, during semi-seamless migration, and the main channel still waits for MIGRATE_END
* from the client.
* Note: Call it only from the main thread */
int red_client_during_migrate_at_target(RedClient *client);
void red_client_migrate(RedClient *client);
gboolean red_client_is_disconnecting(RedClient *client);
void red_client_set_disconnecting(RedClient *client);
RedsState* red_client_get_server(RedClient *client);
#endif /* _H_RED_CLIENT */

View File

@ -74,6 +74,7 @@
#include "video-encoder.h"
#include "red-channel-client.h"
#include "main-channel-client.h"
#include "red-client.h"
static void reds_client_monitors_config(RedsState *reds, VDAgentMonitorsConfig *monitors_config);
static gboolean reds_use_client_monitors_config(RedsState *reds);
@ -565,7 +566,7 @@ void reds_client_disconnect(RedsState *reds, RedClient *client)
exit(0);
}
if (!client || client->disconnecting) {
if (!client || red_client_is_disconnecting(client)) {
spice_debug("client %p already during disconnection", client);
return;
}
@ -575,7 +576,7 @@ void reds_client_disconnect(RedsState *reds, RedClient *client)
* main_channel_client_on_disconnect->
* reds_client_disconnect->red_client_destroy->main_channel...
*/
client->disconnecting = TRUE;
red_client_set_disconnecting(client);
// TODO: we need to handle agent properly for all clients!!!! (e.g., cut and paste, how?)
// We shouldn't initialize the agent when there are still clients connected

View File

@ -39,6 +39,7 @@
#include "red-channel-client.h"
/* FIXME: for now, allow sound channel access to private RedChannelClient data */
#include "red-channel-client-private.h"
#include "red-client.h"
#include "sound.h"
#include <common/snd_codec.h>
#include "demarshallers.h"

View File

@ -21,6 +21,7 @@
#include "stream.h"
#include "display-channel-private.h"
#include "main-channel-client.h"
#include "red-client.h"
#define FPS_TEST_INTERVAL 1
#define FOREACH_STREAMS(display, item) \