1 /**
2 * FreeRDP: A Remote Desktop Protocol Implementation
3 *
4 * Copyright 2015 Jiang Zihao <zihao.jiang@yahoo.com>
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22
23 #include <assert.h>
24 #include <freerdp/log.h>
25 #include "shadow.h"
26
27 #define TAG SERVER_TAG("shadow.mcevent")
28
29 struct rdp_shadow_multiclient_event
30 {
31 HANDLE event; /* Kickoff event */
32 HANDLE barrierEvent; /* Represents that all clients have consumed event */
33 HANDLE doneEvent; /* Event handling finished. Server could continue */
34 wArrayList* subscribers;
35 CRITICAL_SECTION lock;
36 int consuming;
37 int waiting;
38
39 /* For debug */
40 int eventid;
41 };
42
43 struct rdp_shadow_multiclient_subscriber
44 {
45 rdpShadowMultiClientEvent* ref;
46 BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
47 };
48
shadow_multiclient_new(void)49 rdpShadowMultiClientEvent* shadow_multiclient_new(void)
50 {
51 rdpShadowMultiClientEvent* event =
52 (rdpShadowMultiClientEvent*)calloc(1, sizeof(rdpShadowMultiClientEvent));
53 if (!event)
54 goto out_error;
55
56 event->event = CreateEvent(NULL, TRUE, FALSE, NULL);
57 if (!event->event)
58 goto out_free;
59
60 event->barrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
61 if (!event->barrierEvent)
62 goto out_free_event;
63
64 event->doneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
65 if (!event->doneEvent)
66 goto out_free_barrierEvent;
67
68 event->subscribers = ArrayList_New(TRUE);
69 if (!event->subscribers)
70 goto out_free_doneEvent;
71
72 if (!InitializeCriticalSectionAndSpinCount(&(event->lock), 4000))
73 goto out_free_subscribers;
74
75 event->consuming = 0;
76 event->waiting = 0;
77 event->eventid = 0;
78 SetEvent(event->doneEvent);
79 return event;
80
81 out_free_subscribers:
82 ArrayList_Free(event->subscribers);
83 out_free_doneEvent:
84 CloseHandle(event->doneEvent);
85 out_free_barrierEvent:
86 CloseHandle(event->barrierEvent);
87 out_free_event:
88 CloseHandle(event->event);
89 out_free:
90 free(event);
91 out_error:
92 return (rdpShadowMultiClientEvent*)NULL;
93 }
94
shadow_multiclient_free(rdpShadowMultiClientEvent * event)95 void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
96 {
97 if (!event)
98 return;
99
100 DeleteCriticalSection(&(event->lock));
101
102 ArrayList_Free(event->subscribers);
103 CloseHandle(event->doneEvent);
104 CloseHandle(event->barrierEvent);
105 CloseHandle(event->event);
106 free(event);
107
108 return;
109 }
110
_Publish(rdpShadowMultiClientEvent * event)111 static void _Publish(rdpShadowMultiClientEvent* event)
112 {
113 wArrayList* subscribers;
114 struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
115 int i;
116
117 subscribers = event->subscribers;
118
119 assert(event->consuming == 0);
120
121 /* Count subscribing clients */
122 ArrayList_Lock(subscribers);
123 for (i = 0; i < ArrayList_Count(subscribers); i++)
124 {
125 subscriber = (struct rdp_shadow_multiclient_subscriber*)ArrayList_GetItem(subscribers, i);
126 /* Set flag to subscriber: I acknowledge and please handle */
127 subscriber->pleaseHandle = TRUE;
128 event->consuming++;
129 }
130 ArrayList_Unlock(subscribers);
131
132 if (event->consuming > 0)
133 {
134 event->eventid = (event->eventid & 0xff) + 1;
135 WLog_VRB(TAG, "Server published event %d. %d clients.\n", event->eventid, event->consuming);
136 ResetEvent(event->doneEvent);
137 SetEvent(event->event);
138 }
139
140 return;
141 }
142
_WaitForSubscribers(rdpShadowMultiClientEvent * event)143 static void _WaitForSubscribers(rdpShadowMultiClientEvent* event)
144 {
145 if (event->consuming > 0)
146 {
147 /* Wait for clients done */
148 WLog_VRB(TAG, "Server wait event %d. %d clients.\n", event->eventid, event->consuming);
149 LeaveCriticalSection(&(event->lock));
150 WaitForSingleObject(event->doneEvent, INFINITE);
151 EnterCriticalSection(&(event->lock));
152 WLog_VRB(TAG, "Server quit event %d. %d clients.\n", event->eventid, event->consuming);
153 }
154
155 /* Last subscriber should have already reset the event */
156 assert(WaitForSingleObject(event->event, 0) != WAIT_OBJECT_0);
157
158 return;
159 }
160
shadow_multiclient_publish(rdpShadowMultiClientEvent * event)161 void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
162 {
163 if (!event)
164 return;
165
166 EnterCriticalSection(&(event->lock));
167 _Publish(event);
168 LeaveCriticalSection(&(event->lock));
169
170 return;
171 }
shadow_multiclient_wait(rdpShadowMultiClientEvent * event)172 void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
173 {
174 if (!event)
175 return;
176
177 EnterCriticalSection(&(event->lock));
178 _WaitForSubscribers(event);
179 LeaveCriticalSection(&(event->lock));
180
181 return;
182 }
shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent * event)183 void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
184 {
185 if (!event)
186 return;
187
188 EnterCriticalSection(&(event->lock));
189 _Publish(event);
190 _WaitForSubscribers(event);
191 LeaveCriticalSection(&(event->lock));
192
193 return;
194 }
195
_Consume(struct rdp_shadow_multiclient_subscriber * subscriber,BOOL wait)196 static BOOL _Consume(struct rdp_shadow_multiclient_subscriber* subscriber, BOOL wait)
197 {
198 rdpShadowMultiClientEvent* event = subscriber->ref;
199 BOOL ret = FALSE;
200
201 if (WaitForSingleObject(event->event, 0) == WAIT_OBJECT_0 && subscriber->pleaseHandle)
202 {
203 /* Consume my share. Server is waiting for us */
204 event->consuming--;
205 ret = TRUE;
206 }
207
208 assert(event->consuming >= 0);
209
210 if (event->consuming == 0)
211 {
212 /* Last client reset event before notify clients to continue */
213 ResetEvent(event->event);
214
215 if (event->waiting > 0)
216 {
217 /* Notify other clients to continue */
218 SetEvent(event->barrierEvent);
219 }
220 else
221 {
222 /* Only one client. Notify server directly */
223 SetEvent(event->doneEvent);
224 }
225 }
226 else /* (event->consuming > 0) */
227 {
228 if (wait)
229 {
230 /*
231 * This client need to wait. That means the client will
232 * continue waiting for other clients to finish.
233 * The last client should reset barrierEvent.
234 */
235 event->waiting++;
236 LeaveCriticalSection(&(event->lock));
237 WaitForSingleObject(event->barrierEvent, INFINITE);
238 EnterCriticalSection(&(event->lock));
239 event->waiting--;
240 if (event->waiting == 0)
241 {
242 /*
243 * This is last client waiting for barrierEvent.
244 * We can now discard barrierEvent and notify
245 * server to continue.
246 */
247 ResetEvent(event->barrierEvent);
248 SetEvent(event->doneEvent);
249 }
250 }
251 }
252
253 return ret;
254 }
255
shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent * event)256 void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
257 {
258 struct rdp_shadow_multiclient_subscriber* subscriber;
259
260 if (!event)
261 return NULL;
262
263 EnterCriticalSection(&(event->lock));
264
265 subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
266 1, sizeof(struct rdp_shadow_multiclient_subscriber));
267 if (!subscriber)
268 goto out_error;
269
270 subscriber->ref = event;
271 subscriber->pleaseHandle = FALSE;
272
273 if (ArrayList_Add(event->subscribers, subscriber) < 0)
274 goto out_free;
275
276 WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
277 event->eventid, event->consuming);
278 (void)_Consume(subscriber, TRUE);
279 WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
280 event->eventid, event->consuming);
281
282 LeaveCriticalSection(&(event->lock));
283
284 return subscriber;
285
286 out_free:
287 free(subscriber);
288 out_error:
289 LeaveCriticalSection(&(event->lock));
290 return NULL;
291 }
292
293 /*
294 * Consume my share and release my register
295 * If we have update event and pleaseHandle flag
296 * We need to consume. Anyway we need to clear
297 * pleaseHandle flag
298 */
shadow_multiclient_release_subscriber(void * subscriber)299 void shadow_multiclient_release_subscriber(void* subscriber)
300 {
301 struct rdp_shadow_multiclient_subscriber* s;
302 rdpShadowMultiClientEvent* event;
303
304 if (!subscriber)
305 return;
306
307 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
308 event = s->ref;
309
310 EnterCriticalSection(&(event->lock));
311
312 WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
313 event->consuming);
314 (void)_Consume(s, FALSE);
315 WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
316 event->consuming);
317
318 ArrayList_Remove(event->subscribers, subscriber);
319
320 LeaveCriticalSection(&(event->lock));
321
322 free(subscriber);
323
324 return;
325 }
326
shadow_multiclient_consume(void * subscriber)327 BOOL shadow_multiclient_consume(void* subscriber)
328 {
329 struct rdp_shadow_multiclient_subscriber* s;
330 rdpShadowMultiClientEvent* event;
331 BOOL ret = FALSE;
332
333 if (!subscriber)
334 return ret;
335
336 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
337 event = s->ref;
338
339 EnterCriticalSection(&(event->lock));
340
341 WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
342 event->consuming);
343 ret = _Consume(s, TRUE);
344 WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
345 event->consuming);
346
347 LeaveCriticalSection(&(event->lock));
348
349 return ret;
350 }
351
shadow_multiclient_getevent(void * subscriber)352 HANDLE shadow_multiclient_getevent(void* subscriber)
353 {
354 if (!subscriber)
355 return (HANDLE)NULL;
356
357 return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
358 }
359