FreeRDP
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 struct 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;
33  CRITICAL_SECTION lock;
34  int consuming;
35  int waiting;
36 
37  /* For debug */
38  int eventid;
39 };
40 
41 struct rdp_shadow_multiclient_subscriber
42 {
43  rdpShadowMultiClientEvent* ref;
44  BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
45 };
46 
47 rdpShadowMultiClientEvent* 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 
79 out_free_subscribers:
80  ArrayList_Free(event->subscribers);
81 out_free_doneEvent:
82  (void)CloseHandle(event->doneEvent);
83 out_free_barrierEvent:
84  (void)CloseHandle(event->barrierEvent);
85 out_free_event:
86  (void)CloseHandle(event->event);
87 out_free:
88  free(event);
89 out_error:
90  return (rdpShadowMultiClientEvent*)NULL;
91 }
92 
93 void 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 
107 static 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 
136 static 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 
152 void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
153 {
154  if (!event)
155  return;
156 
157  EnterCriticalSection(&(event->lock));
158  Publish(event);
159  LeaveCriticalSection(&(event->lock));
160 }
161 void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
162 {
163  if (!event)
164  return;
165 
166  EnterCriticalSection(&(event->lock));
167  WaitForSubscribers(event);
168  LeaveCriticalSection(&(event->lock));
169 }
170 void 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 
181 static 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 
241 void* 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 
271 out_free:
272  free(subscriber);
273 out_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  */
284 void 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 
310 BOOL 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 
335 HANDLE 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 }