FreeRDP
Loading...
Searching...
No Matches
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
27/* Maximum time the publisher waits for all subscribers to consume a frame.
28 * Without this bound a single subscriber that stops consuming (e.g. a client
29 * stuck before its TLS handshake, or one stalled mid-send) blocks the
30 * subsystem/capture thread forever and, on the Windows subsystem, the
31 * listener thread with it (the clients list lock is held across the wait). */
32#define SHADOW_SUBSCRIBER_WAIT_TIMEOUT 1000ul
33
34struct rdp_shadow_multiclient_event
35{
36 HANDLE event; /* Kickoff event */
37 HANDLE barrierEvent; /* Represents that all clients have consumed event */
38 HANDLE doneEvent; /* Event handling finished. Server could continue */
39 wArrayList* subscribers;
41 int consuming;
42 int waiting;
43
44 /* For debug */
45 int eventid;
46};
47
48struct rdp_shadow_multiclient_subscriber
49{
50 rdpShadowMultiClientEvent* ref;
51 BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
52};
53
54rdpShadowMultiClientEvent* shadow_multiclient_new(void)
55{
56 rdpShadowMultiClientEvent* event = calloc(1, sizeof(rdpShadowMultiClientEvent));
57 if (!event)
58 goto fail;
59
60 if (!InitializeCriticalSectionAndSpinCount(&(event->lock), 4000))
61 goto fail;
62
63 event->event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
64 if (!event->event)
65 goto fail;
66
67 event->barrierEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
68 if (!event->barrierEvent)
69 goto fail;
70
71 event->doneEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
72 if (!event->doneEvent)
73 goto fail;
74
75 event->subscribers = ArrayList_New(TRUE);
76 if (!event->subscribers)
77 goto fail;
78
79 event->consuming = 0;
80 event->waiting = 0;
81 event->eventid = 0;
82 if (!SetEvent(event->doneEvent))
83 goto fail;
84 return event;
85
86fail:
87 shadow_multiclient_free(event);
88 return nullptr;
89}
90
91void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
92{
93 if (!event)
94 return;
95
96 DeleteCriticalSection(&(event->lock));
97
98 ArrayList_Free(event->subscribers);
99 if (event->doneEvent)
100 (void)CloseHandle(event->doneEvent);
101 if (event->barrierEvent)
102 (void)CloseHandle(event->barrierEvent);
103 if (event->barrierEvent)
104 (void)CloseHandle(event->event);
105 free(event);
106}
107
108static void Publish(rdpShadowMultiClientEvent* event)
109{
110 WINPR_ASSERT(event);
111 wArrayList* subscribers = event->subscribers;
112
113 WINPR_ASSERT(event->consuming == 0);
114
115 /* Count subscribing clients */
116 ArrayList_Lock(subscribers);
117 for (size_t i = 0; i < ArrayList_Count(subscribers); i++)
118 {
119 struct rdp_shadow_multiclient_subscriber* subscriber =
120 (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 DiscardRound(rdpShadowMultiClientEvent* event)
137{
138 wArrayList* subscribers = nullptr;
139
140 WINPR_ASSERT(event);
141
142 /* Called with event->lock held after the subscriber wait timed out.
143 * Reset the round state so that the publisher can continue: subscribers
144 * waking up late see pleaseHandle == FALSE and skip their consume, and
145 * clients blocked on the barrier event are released. */
146 subscribers = event->subscribers;
147 ArrayList_Lock(subscribers);
148 for (size_t i = 0; i < ArrayList_Count(subscribers); i++)
149 {
150 struct rdp_shadow_multiclient_subscriber* subscriber =
151 (struct rdp_shadow_multiclient_subscriber*)ArrayList_GetItem(subscribers, i);
152 subscriber->pleaseHandle = FALSE;
153 }
154 ArrayList_Unlock(subscribers);
155
156 if (event->waiting > 0)
157 {
158 (void)SetEvent(event->barrierEvent);
159 }
160 else
161 {
162 (void)SetEvent(event->doneEvent);
163 }
164
165 WLog_WARN(TAG, "Event %d dropped: %d subscriber(s) unresponsive for %lu ms", event->eventid,
166 event->consuming, SHADOW_SUBSCRIBER_WAIT_TIMEOUT);
167 event->consuming = 0;
168 (void)ResetEvent(event->event);
169}
170
171static void WaitForSubscribers(rdpShadowMultiClientEvent* event)
172{
173 DWORD status = WAIT_FAILED;
174
175 if (event->consuming > 0)
176 {
177 /* Wait for clients done */
178 WLog_VRB(TAG, "Server wait event %d. %d clients.\n", event->eventid, event->consuming);
179 LeaveCriticalSection(&(event->lock));
180 status = WaitForSingleObject(event->doneEvent, SHADOW_SUBSCRIBER_WAIT_TIMEOUT);
181 EnterCriticalSection(&(event->lock));
182
183 if (status != WAIT_OBJECT_0)
184 DiscardRound(event);
185
186 WLog_VRB(TAG, "Server quit event %d. %d clients.\n", event->eventid, event->consuming);
187 }
188
189 /* Last subscriber should have already reset the event */
190 WINPR_ASSERT(WaitForSingleObject(event->event, 0) != WAIT_OBJECT_0);
191}
192
193void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
194{
195 if (!event)
196 return;
197
198 EnterCriticalSection(&(event->lock));
199 Publish(event);
200 LeaveCriticalSection(&(event->lock));
201}
202void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
203{
204 if (!event)
205 return;
206
207 EnterCriticalSection(&(event->lock));
208 WaitForSubscribers(event);
209 LeaveCriticalSection(&(event->lock));
210}
211void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
212{
213 if (!event)
214 return;
215
216 EnterCriticalSection(&(event->lock));
217 Publish(event);
218 WaitForSubscribers(event);
219 LeaveCriticalSection(&(event->lock));
220}
221
222static BOOL Consume(struct rdp_shadow_multiclient_subscriber* subscriber, BOOL wait)
223{
224 rdpShadowMultiClientEvent* event = subscriber->ref;
225 BOOL ret = FALSE;
226
227 if (WaitForSingleObject(event->event, 0) == WAIT_OBJECT_0 && subscriber->pleaseHandle)
228 {
229 /* Consume my share. Server is waiting for us */
230 event->consuming--;
231 ret = TRUE;
232 }
233
234 WINPR_ASSERT(event->consuming >= 0);
235
236 if (event->consuming == 0)
237 {
238 /* Last client reset event before notify clients to continue */
239 (void)ResetEvent(event->event);
240
241 if (event->waiting > 0)
242 {
243 /* Notify other clients to continue */
244 (void)SetEvent(event->barrierEvent);
245 }
246 else
247 {
248 /* Only one client. Notify server directly */
249 (void)SetEvent(event->doneEvent);
250 }
251 }
252 else /* (event->consuming > 0) */
253 {
254 if (wait)
255 {
256 /*
257 * This client need to wait. That means the client will
258 * continue waiting for other clients to finish.
259 * The last client should reset barrierEvent.
260 */
261 event->waiting++;
262 LeaveCriticalSection(&(event->lock));
263 (void)WaitForSingleObject(event->barrierEvent, INFINITE);
264 EnterCriticalSection(&(event->lock));
265 event->waiting--;
266 if (event->waiting == 0)
267 {
268 /*
269 * This is last client waiting for barrierEvent.
270 * We can now discard barrierEvent and notify
271 * server to continue.
272 */
273 (void)ResetEvent(event->barrierEvent);
274 (void)SetEvent(event->doneEvent);
275 }
276 }
277 }
278
279 return ret;
280}
281
282void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
283{
284 struct rdp_shadow_multiclient_subscriber* subscriber = nullptr;
285
286 if (!event)
287 return nullptr;
288
289 EnterCriticalSection(&(event->lock));
290
291 subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
292 1, sizeof(struct rdp_shadow_multiclient_subscriber));
293 if (!subscriber)
294 goto out_error;
295
296 subscriber->ref = event;
297 subscriber->pleaseHandle = FALSE;
298
299 if (!ArrayList_Append(event->subscribers, subscriber))
300 goto out_free;
301
302 WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
303 event->eventid, event->consuming);
304 (void)Consume(subscriber, TRUE);
305 WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
306 event->eventid, event->consuming);
307
308 LeaveCriticalSection(&(event->lock));
309
310 return subscriber;
311
312out_free:
313 free(subscriber);
314out_error:
315 LeaveCriticalSection(&(event->lock));
316 return nullptr;
317}
318
319/*
320 * Consume my share and release my register
321 * If we have update event and pleaseHandle flag
322 * We need to consume. Anyway we need to clear
323 * pleaseHandle flag
324 */
325void shadow_multiclient_release_subscriber(void* subscriber)
326{
327 struct rdp_shadow_multiclient_subscriber* s = nullptr;
328 rdpShadowMultiClientEvent* event = nullptr;
329
330 if (!subscriber)
331 return;
332
333 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
334 event = s->ref;
335
336 EnterCriticalSection(&(event->lock));
337
338 WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
339 event->consuming);
340 (void)Consume(s, FALSE);
341 WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
342 event->consuming);
343
344 ArrayList_Remove(event->subscribers, subscriber);
345
346 LeaveCriticalSection(&(event->lock));
347
348 free(subscriber);
349}
350
351BOOL shadow_multiclient_consume(void* subscriber)
352{
353 struct rdp_shadow_multiclient_subscriber* s = nullptr;
354 rdpShadowMultiClientEvent* event = nullptr;
355 BOOL ret = FALSE;
356
357 if (!subscriber)
358 return ret;
359
360 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
361 event = s->ref;
362
363 EnterCriticalSection(&(event->lock));
364
365 WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
366 event->consuming);
367 ret = Consume(s, TRUE);
368 WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
369 event->consuming);
370
371 LeaveCriticalSection(&(event->lock));
372
373 return ret;
374}
375
376HANDLE shadow_multiclient_getevent(void* subscriber)
377{
378 if (!subscriber)
379 return (HANDLE) nullptr;
380
381 return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
382}