mirror of
https://gitlab.uni-freiburg.de/opensourcevdi/spice
synced 2026-01-08 21:14:11 +00:00
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:
parent
1f4705e130
commit
bc8d967e67
@ -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 \
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
|
||||
@ -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
290
server/red-client.c
Normal 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
65
server/red-client.h
Normal 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 */
|
||||
@ -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
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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) \
|
||||
|
||||
Loading…
Reference in New Issue
Block a user