1 /* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3 Copyright (C) 2009-2015 Red Hat, Inc.
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, see <http://www.gnu.org/licenses/>.
17 */
18 #include <config.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <errno.h>
22 #include <pthread.h>
23
24 #include "red-common.h"
25 #include "dispatcher.h"
26 #include "main-dispatcher.h"
27 #include "red-client.h"
28 #include "reds.h"
29
30 /*
31 * Main Dispatcher
32 * ===============
33 *
34 * Communication channel between any non main thread and the main thread.
35 *
36 * The main thread is that from which spice_server_init is called.
37 *
38 * Messages are single sized, sent from the non-main thread to the main-thread.
39 * No acknowledge is sent back. This prevents a possible deadlock with the main
40 * thread already waiting on a response for the existing red_dispatcher used
41 * by the worker thread.
42 *
43 * All events have three functions:
44 * main_dispatcher_<event_name> - non static, public function
45 * main_dispatcher_self_<event_name> - handler for main thread
46 * main_dispatcher_handle_<event_name> - handler for callback from main thread
47 * seperate from self because it may send an ack or do other work in the future.
48 */
49
50 enum {
51 MAIN_DISPATCHER_CHANNEL_EVENT = 0,
52 MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
53 MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
54 MAIN_DISPATCHER_CLIENT_DISCONNECT,
55
56 MAIN_DISPATCHER_NUM_MESSAGES
57 };
58
59 struct MainDispatcherChannelEventMessage {
60 int event;
61 SpiceChannelEventInfo *info;
62 };
63
64 struct MainDispatcherMigrateSeamlessDstCompleteMessage {
65 RedClient *client;
66 };
67
68 struct MainDispatcherMmTimeLatencyMessage {
69 RedClient *client;
70 uint32_t latency;
71 };
72
73 struct MainDispatcherClientDisconnectMessage {
74 RedClient *client;
75 };
76
77 /* channel_event - calls core->channel_event, must be done in main thread */
main_dispatcher_handle_channel_event(void * opaque,void * payload)78 static void main_dispatcher_handle_channel_event(void *opaque,
79 void *payload)
80 {
81 auto reds = (RedsState*) opaque;
82 auto channel_event = (MainDispatcherChannelEventMessage*) payload;
83
84 reds_handle_channel_event(reds, channel_event->event, channel_event->info);
85 }
86
channel_event(int event,SpiceChannelEventInfo * info)87 void MainDispatcher::channel_event(int event, SpiceChannelEventInfo *info)
88 {
89 MainDispatcherChannelEventMessage msg = {0,};
90
91 if (pthread_self() == thread_id) {
92 reds_handle_channel_event(reds, event, info);
93 return;
94 }
95 msg.event = event;
96 msg.info = info;
97 send_message(MAIN_DISPATCHER_CHANNEL_EVENT, &msg);
98 }
99
100
main_dispatcher_handle_migrate_complete(void * opaque,void * payload)101 static void main_dispatcher_handle_migrate_complete(void *opaque,
102 void *payload)
103 {
104 auto reds = (RedsState*) opaque;
105 auto mig_complete = (MainDispatcherMigrateSeamlessDstCompleteMessage*) payload;
106
107 reds_on_client_seamless_migrate_complete(reds, mig_complete->client);
108 mig_complete->client->unref();
109 }
110
main_dispatcher_handle_mm_time_latency(void * opaque,void * payload)111 static void main_dispatcher_handle_mm_time_latency(void *opaque,
112 void *payload)
113 {
114 auto reds = (RedsState*) opaque;
115 auto msg = (MainDispatcherMmTimeLatencyMessage*) payload;
116 reds_set_client_mm_time_latency(reds, msg->client, msg->latency);
117 msg->client->unref();
118 }
119
main_dispatcher_handle_client_disconnect(void * opaque,void * payload)120 static void main_dispatcher_handle_client_disconnect(void *opaque,
121 void *payload)
122 {
123 auto reds = (RedsState*) opaque;
124 auto msg = (MainDispatcherClientDisconnectMessage*) payload;
125
126 spice_debug("client=%p", msg->client);
127 reds_client_disconnect(reds, msg->client);
128 msg->client->unref();
129 }
130
seamless_migrate_dst_complete(RedClient * client)131 void MainDispatcher::seamless_migrate_dst_complete(RedClient *client)
132 {
133 MainDispatcherMigrateSeamlessDstCompleteMessage msg;
134
135 if (pthread_self() == thread_id) {
136 reds_on_client_seamless_migrate_complete(reds, client);
137 return;
138 }
139
140 msg.client = red::add_ref(client);
141 send_message(MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE, &msg);
142 }
143
set_mm_time_latency(RedClient * client,uint32_t latency)144 void MainDispatcher::set_mm_time_latency(RedClient *client, uint32_t latency)
145 {
146 MainDispatcherMmTimeLatencyMessage msg;
147
148 if (pthread_self() == thread_id) {
149 reds_set_client_mm_time_latency(reds, client, latency);
150 return;
151 }
152
153 msg.client = red::add_ref(client);
154 msg.latency = latency;
155 send_message(MAIN_DISPATCHER_SET_MM_TIME_LATENCY, &msg);
156 }
157
client_disconnect(RedClient * client)158 void MainDispatcher::client_disconnect(RedClient *client)
159 {
160 MainDispatcherClientDisconnectMessage msg;
161
162 if (!client->is_disconnecting()) {
163 spice_debug("client %p", client);
164 msg.client = red::add_ref(client);
165 send_message(MAIN_DISPATCHER_CLIENT_DISCONNECT, &msg);
166 } else {
167 spice_debug("client %p already during disconnection", client);
168 }
169 }
170
171 /*
172 * FIXME:
173 * Reds routines shouldn't be exposed. Instead reds.cpp should register the callbacks,
174 * and the corresponding operations should be made only via main_dispatcher.
175 */
MainDispatcher(RedsState * init_reds)176 MainDispatcher::MainDispatcher(RedsState *init_reds):
177 Dispatcher(MAIN_DISPATCHER_NUM_MESSAGES),
178 reds(init_reds),
179 thread_id(pthread_self())
180 {
181 set_opaque(reds);
182
183 watch = create_watch(reds_get_core_interface(reds));
184 register_handler(MAIN_DISPATCHER_CHANNEL_EVENT,
185 main_dispatcher_handle_channel_event,
186 sizeof(MainDispatcherChannelEventMessage), false);
187 register_handler(MAIN_DISPATCHER_MIGRATE_SEAMLESS_DST_COMPLETE,
188 main_dispatcher_handle_migrate_complete,
189 sizeof(MainDispatcherMigrateSeamlessDstCompleteMessage), false);
190 register_handler(MAIN_DISPATCHER_SET_MM_TIME_LATENCY,
191 main_dispatcher_handle_mm_time_latency,
192 sizeof(MainDispatcherMmTimeLatencyMessage), false);
193 register_handler(MAIN_DISPATCHER_CLIENT_DISCONNECT,
194 main_dispatcher_handle_client_disconnect,
195 sizeof(MainDispatcherClientDisconnectMessage), false);
196 }
197
~MainDispatcher()198 MainDispatcher::~MainDispatcher()
199 {
200 red_watch_remove(watch);
201 }
202