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->event)
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
275 /* a round dropped while we waited may be followed by a new one already:
276 * its own last client tells the server */
277 if (event->consuming == 0)
278 (void)SetEvent(event->doneEvent);
279 }
280 }
281 }
282
283 return ret;
284}
285
286void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
287{
288 struct rdp_shadow_multiclient_subscriber* subscriber = nullptr;
289
290 if (!event)
291 return nullptr;
292
293 EnterCriticalSection(&(event->lock));
294
295 subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
296 1, sizeof(struct rdp_shadow_multiclient_subscriber));
297 if (!subscriber)
298 goto out_error;
299
300 subscriber->ref = event;
301 subscriber->pleaseHandle = FALSE;
302
303 if (!ArrayList_Append(event->subscribers, subscriber))
304 goto out_free;
305
306 WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
307 event->eventid, event->consuming);
308 (void)Consume(subscriber, TRUE);
309 WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
310 event->eventid, event->consuming);
311
312 LeaveCriticalSection(&(event->lock));
313
314 return subscriber;
315
316out_free:
317 free(subscriber);
318out_error:
319 LeaveCriticalSection(&(event->lock));
320 return nullptr;
321}
322
323/*
324 * Consume my share and release my register
325 * If we have update event and pleaseHandle flag
326 * We need to consume. Anyway we need to clear
327 * pleaseHandle flag
328 */
329void shadow_multiclient_release_subscriber(void* subscriber)
330{
331 struct rdp_shadow_multiclient_subscriber* s = nullptr;
332 rdpShadowMultiClientEvent* event = nullptr;
333
334 if (!subscriber)
335 return;
336
337 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
338 event = s->ref;
339
340 EnterCriticalSection(&(event->lock));
341
342 WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
343 event->consuming);
344 (void)Consume(s, FALSE);
345 WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
346 event->consuming);
347
348 ArrayList_Remove(event->subscribers, subscriber);
349
350 LeaveCriticalSection(&(event->lock));
351
352 free(subscriber);
353}
354
355BOOL shadow_multiclient_consume(void* subscriber)
356{
357 struct rdp_shadow_multiclient_subscriber* s = nullptr;
358 rdpShadowMultiClientEvent* event = nullptr;
359 BOOL ret = FALSE;
360
361 if (!subscriber)
362 return ret;
363
364 s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
365 event = s->ref;
366
367 EnterCriticalSection(&(event->lock));
368
369 WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
370 event->consuming);
371 ret = Consume(s, TRUE);
372 WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
373 event->consuming);
374
375 LeaveCriticalSection(&(event->lock));
376
377 return ret;
378}
379
380HANDLE shadow_multiclient_getevent(void* subscriber)
381{
382 if (!subscriber)
383 return (HANDLE) nullptr;
384
385 return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
386}