FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
shadow_mcevent.c
1
19#include <freerdp/config.h>
20
21#include <winpr/assert.h>
22#include <freerdp/log.h>
23#include "shadow.h"
24
25#define TAG SERVER_TAG("shadow.mcevent")
26
27struct rdp_shadow_multiclient_event
28{
29 HANDLE event; /* Kickoff event */
30 HANDLE barrierEvent; /* Represents that all clients have consumed event */
31 HANDLE doneEvent; /* Event handling finished. Server could continue */
32 wArrayList* subscribers;
34 int consuming;
35 int waiting;
36
37 /* For debug */
38 int eventid;
39};
40
41struct rdp_shadow_multiclient_subscriber
42{
43 rdpShadowMultiClientEvent* ref;
44 BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
45};
46
47rdpShadowMultiClientEvent* shadow_multiclient_new(void)
48{
49 rdpShadowMultiClientEvent* event =
50 (rdpShadowMultiClientEvent*)calloc(1, sizeof(rdpShadowMultiClientEvent));
51 if (!event)
52 goto out_error;
53
54 event->event = CreateEvent(NULL, TRUE, FALSE, NULL);
55 if (!event->event)
56 goto out_free;
57
58 event->barrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
59 if (!event->barrierEvent)
60 goto out_free_event;
61
62 event->doneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
63 if (!event->doneEvent)
64 goto out_free_barrierEvent;
65
66 event->subscribers = ArrayList_New(TRUE);
67 if (!event->subscribers)
68 goto out_free_doneEvent;
69
70 if (!InitializeCriticalSectionAndSpinCount(&(event->lock), 4000))
71 goto out_free_subscribers;
72
73 event->consuming = 0;
74 event->waiting = 0;
75 event->eventid = 0;
76 (void)SetEvent(event->doneEvent);
77 return event;
78
79out_free_subscribers:
80 ArrayList_Free(event->subscribers);
81out_free_doneEvent:
82 (void)CloseHandle(event->doneEvent);
83out_free_barrierEvent:
84 (void)CloseHandle(event->barrierEvent);
85out_free_event:
86 (void)CloseHandle(event->event);
87out_free:
88 free(event);
89out_error:
90 return (rdpShadowMultiClientEvent*)NULL;
91}
92
93void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
94{
95 if (!event)
96 return;
97
98 DeleteCriticalSection(&(event->lock));
99
100 ArrayList_Free(event->subscribers);
101 (void)CloseHandle(event->doneEvent);
102 (void)CloseHandle(event->barrierEvent);
103 (void)CloseHandle(event->event);
104 free(event);
105}
106
107static void Publish(rdpShadowMultiClientEvent* event)
108{
109 wArrayList* subscribers = NULL;
110 struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
111
112 subscribers = event->subscribers;
113
114 WINPR_ASSERT(event->consuming == 0);
115
116 /* Count subscribing clients */
117 ArrayList_Lock(subscribers);
118 for (size_t i = 0; i < ArrayList_Count(subscribers); i++)
119 {
120 subscriber = (struct rdp_shadow_multiclient_subscriber*)ArrayList_GetItem(subscribers, i);
121 /* Set flag to subscriber: I acknowledge and please handle */
122 subscriber->pleaseHandle = TRUE;
123 event->consuming++;
124 }
125 ArrayList_Unlock(subscribers);
126
127 if (event->consuming > 0)
128 {
129 event->eventid = (event->eventid & 0xff) + 1;
130 WLog_VRB(TAG, "Server published event %d. %d clients.\n", event->eventid, event->consuming);
131 (void)ResetEvent(event->doneEvent);
132 (void)SetEvent(event->event);
133 }
134}
135
136static void WaitForSubscribers(rdpShadowMultiClientEvent* event)
137{
138 if (event->consuming > 0)
139 {
140 /* Wait for clients done */
141 WLog_VRB(TAG, "Server wait event %d. %d clients.\n", event->eventid, event->consuming);
142 LeaveCriticalSection(&(event->lock));
143 (void)WaitForSingleObject(event->doneEvent, INFINITE);
144 EnterCriticalSection(&(event->lock));
145 WLog_VRB(TAG, "Server quit event %d. %d clients.\n", event->eventid, event->consuming);
146 }
147
148 /* Last subscriber should have already reset the event */
149 WINPR_ASSERT(WaitForSingleObject(event->event, 0) != WAIT_OBJECT_0);
150}
151
152void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
153{
154 if (!event)
155 return;
156
157 EnterCriticalSection(&(event->lock));
158 Publish(event);
159 LeaveCriticalSection(&(event->lock));
160}
161void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
162{
163 if (!event)
164 return;
165
166 EnterCriticalSection(&(event->lock));
167 WaitForSubscribers(event);
168 LeaveCriticalSection(&(event->lock));
169}
170void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
171{
172 if (!event)
173 return;
174
175 EnterCriticalSection(&(event->lock));
176 Publish(event);
177 WaitForSubscribers(event);
178 LeaveCriticalSection(&(event->lock));
179}
180
181static BOOL Consume(struct rdp_shadow_multiclient_subscriber* subscriber, BOOL wait)
182{
183 rdpShadowMultiClientEvent* event = subscriber->ref;
184 BOOL ret = FALSE;
185
186 if (WaitForSingleObject(event->event, 0) == WAIT_OBJECT_0 && subscriber->pleaseHandle)
187 {
188 /* Consume my share. Server is waiting for us */
189 event->consuming--;
190 ret = TRUE;
191 }
192
193 WINPR_ASSERT(event->consuming >= 0);
194
195 if (event->consuming == 0)
196 {
197 /* Last client reset event before notify clients to continue */
198 (void)ResetEvent(event->event);
199
200 if (event->waiting > 0)
201 {
202 /* Notify other clients to continue */
203 (void)SetEvent(event->barrierEvent);
204 }
205 else
206 {
207 /* Only one client. Notify server directly */
208 (void)SetEvent(event->doneEvent);
209 }
210 }
211 else /* (event->consuming > 0) */
212 {
213 if (wait)
214 {
215 /*
216 * This client need to wait. That means the client will
217 * continue waiting for other clients to finish.
218 * The last client should reset barrierEvent.
219 */
220 event->waiting++;
221 LeaveCriticalSection(&(event->lock));
222 (void)WaitForSingleObject(event->barrierEvent, INFINITE);
223 EnterCriticalSection(&(event->lock));
224 event->waiting--;
225 if (event->waiting == 0)
226 {
227 /*
228 * This is last client waiting for barrierEvent.
229 * We can now discard barrierEvent and notify
230 * server to continue.
231 */
232 (void)ResetEvent(event->barrierEvent);
233 (void)SetEvent(event->doneEvent);
234 }
235 }
236 }
237
238 return ret;
239}
240
241void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
242{
243 struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
244
245 if (!event)
246 return NULL;
247
248 EnterCriticalSection(&(event->lock));
249
250 subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
251 1, sizeof(struct rdp_shadow_multiclient_subscriber));
252 if (!subscriber)
253 goto out_error;
254
255 subscriber->ref = event;
256 subscriber->pleaseHandle = FALSE;
257
258 if (!ArrayList_Append(event->subscribers, subscriber))
259 goto out_free;
260
261 WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
262 event->eventid, event->consuming);
263 (void)Consume(subscriber, TRUE);
264 WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
265 event->eventid, event->consuming);
266
267 LeaveCriticalSection(&(event->lock));
268
269 return subscriber;
270
271out_free:
272 free(subscriber);
273out_error:
274 LeaveCriticalSection(&(event->lock));
275 return NULL;
276}
277
278/*
279 * Consume my share and release my register
280 * If we have update event and pleaseHandle flag
281 * We need to consume. Anyway we need to clear
282 * pleaseHandle flag
283 */
284void shadow_multiclient_release_subscriber(void* subscriber)
285{
286 struct rdp_shadow_multiclient_subscriber* s = NULL;
287 rdpShadowMultiClientEvent* event = NULL;
288
289 if (!subscriber)
290 return;
291
292 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
293 event = s->ref;
294
295 EnterCriticalSection(&(event->lock));
296
297 WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
298 event->consuming);
299 (void)Consume(s, FALSE);
300 WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
301 event->consuming);
302
303 ArrayList_Remove(event->subscribers, subscriber);
304
305 LeaveCriticalSection(&(event->lock));
306
307 free(subscriber);
308}
309
310BOOL shadow_multiclient_consume(void* subscriber)
311{
312 struct rdp_shadow_multiclient_subscriber* s = NULL;
313 rdpShadowMultiClientEvent* event = NULL;
314 BOOL ret = FALSE;
315
316 if (!subscriber)
317 return ret;
318
319 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
320 event = s->ref;
321
322 EnterCriticalSection(&(event->lock));
323
324 WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
325 event->consuming);
326 ret = Consume(s, TRUE);
327 WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
328 event->consuming);
329
330 LeaveCriticalSection(&(event->lock));
331
332 return ret;
333}
334
335HANDLE shadow_multiclient_getevent(void* subscriber)
336{
337 if (!subscriber)
338 return (HANDLE)NULL;
339
340 return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
341}