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