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