summaryrefslogtreecommitdiffstats
path: root/server/shadow/shadow_mcevent.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-04 01:24:41 +0000
commita9bcc81f821d7c66f623779fa5147e728eb3c388 (patch)
tree98676963bcdd537ae5908a067a8eb110b93486a6 /server/shadow/shadow_mcevent.c
parentInitial commit. (diff)
downloadfreerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.tar.xz
freerdp3-a9bcc81f821d7c66f623779fa5147e728eb3c388.zip
Adding upstream version 3.3.0+dfsg1.upstream/3.3.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'server/shadow/shadow_mcevent.c')
-rw-r--r--server/shadow/shadow_mcevent.c355
1 files changed, 355 insertions, 0 deletions
diff --git a/server/shadow/shadow_mcevent.c b/server/shadow/shadow_mcevent.c
new file mode 100644
index 0000000..4609cee
--- /dev/null
+++ b/server/shadow/shadow_mcevent.c
@@ -0,0 +1,355 @@
+/**
+ * FreeRDP: A Remote Desktop Protocol Implementation
+ *
+ * Copyright 2015 Jiang Zihao <zihao.jiang@yahoo.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <freerdp/config.h>
+
+#include <winpr/assert.h>
+#include <freerdp/log.h>
+#include "shadow.h"
+
+#define TAG SERVER_TAG("shadow.mcevent")
+
+struct rdp_shadow_multiclient_event
+{
+ HANDLE event; /* Kickoff event */
+ HANDLE barrierEvent; /* Represents that all clients have consumed event */
+ HANDLE doneEvent; /* Event handling finished. Server could continue */
+ wArrayList* subscribers;
+ CRITICAL_SECTION lock;
+ int consuming;
+ int waiting;
+
+ /* For debug */
+ int eventid;
+};
+
+struct rdp_shadow_multiclient_subscriber
+{
+ rdpShadowMultiClientEvent* ref;
+ BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
+};
+
+rdpShadowMultiClientEvent* shadow_multiclient_new(void)
+{
+ rdpShadowMultiClientEvent* event =
+ (rdpShadowMultiClientEvent*)calloc(1, sizeof(rdpShadowMultiClientEvent));
+ if (!event)
+ goto out_error;
+
+ event->event = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!event->event)
+ goto out_free;
+
+ event->barrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!event->barrierEvent)
+ goto out_free_event;
+
+ event->doneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!event->doneEvent)
+ goto out_free_barrierEvent;
+
+ event->subscribers = ArrayList_New(TRUE);
+ if (!event->subscribers)
+ goto out_free_doneEvent;
+
+ if (!InitializeCriticalSectionAndSpinCount(&(event->lock), 4000))
+ goto out_free_subscribers;
+
+ event->consuming = 0;
+ event->waiting = 0;
+ event->eventid = 0;
+ SetEvent(event->doneEvent);
+ return event;
+
+out_free_subscribers:
+ ArrayList_Free(event->subscribers);
+out_free_doneEvent:
+ CloseHandle(event->doneEvent);
+out_free_barrierEvent:
+ CloseHandle(event->barrierEvent);
+out_free_event:
+ CloseHandle(event->event);
+out_free:
+ free(event);
+out_error:
+ return (rdpShadowMultiClientEvent*)NULL;
+}
+
+void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ DeleteCriticalSection(&(event->lock));
+
+ ArrayList_Free(event->subscribers);
+ CloseHandle(event->doneEvent);
+ CloseHandle(event->barrierEvent);
+ CloseHandle(event->event);
+ free(event);
+
+ return;
+}
+
+static void _Publish(rdpShadowMultiClientEvent* event)
+{
+ wArrayList* subscribers = NULL;
+ struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
+
+ subscribers = event->subscribers;
+
+ WINPR_ASSERT(event->consuming == 0);
+
+ /* Count subscribing clients */
+ ArrayList_Lock(subscribers);
+ for (size_t i = 0; i < ArrayList_Count(subscribers); i++)
+ {
+ subscriber = (struct rdp_shadow_multiclient_subscriber*)ArrayList_GetItem(subscribers, i);
+ /* Set flag to subscriber: I acknowledge and please handle */
+ subscriber->pleaseHandle = TRUE;
+ event->consuming++;
+ }
+ ArrayList_Unlock(subscribers);
+
+ if (event->consuming > 0)
+ {
+ event->eventid = (event->eventid & 0xff) + 1;
+ WLog_VRB(TAG, "Server published event %d. %d clients.\n", event->eventid, event->consuming);
+ ResetEvent(event->doneEvent);
+ SetEvent(event->event);
+ }
+
+ return;
+}
+
+static void _WaitForSubscribers(rdpShadowMultiClientEvent* event)
+{
+ if (event->consuming > 0)
+ {
+ /* Wait for clients done */
+ WLog_VRB(TAG, "Server wait event %d. %d clients.\n", event->eventid, event->consuming);
+ LeaveCriticalSection(&(event->lock));
+ WaitForSingleObject(event->doneEvent, INFINITE);
+ EnterCriticalSection(&(event->lock));
+ WLog_VRB(TAG, "Server quit event %d. %d clients.\n", event->eventid, event->consuming);
+ }
+
+ /* Last subscriber should have already reset the event */
+ WINPR_ASSERT(WaitForSingleObject(event->event, 0) != WAIT_OBJECT_0);
+
+ return;
+}
+
+void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ EnterCriticalSection(&(event->lock));
+ _Publish(event);
+ LeaveCriticalSection(&(event->lock));
+
+ return;
+}
+void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ EnterCriticalSection(&(event->lock));
+ _WaitForSubscribers(event);
+ LeaveCriticalSection(&(event->lock));
+
+ return;
+}
+void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
+{
+ if (!event)
+ return;
+
+ EnterCriticalSection(&(event->lock));
+ _Publish(event);
+ _WaitForSubscribers(event);
+ LeaveCriticalSection(&(event->lock));
+
+ return;
+}
+
+static BOOL _Consume(struct rdp_shadow_multiclient_subscriber* subscriber, BOOL wait)
+{
+ rdpShadowMultiClientEvent* event = subscriber->ref;
+ BOOL ret = FALSE;
+
+ if (WaitForSingleObject(event->event, 0) == WAIT_OBJECT_0 && subscriber->pleaseHandle)
+ {
+ /* Consume my share. Server is waiting for us */
+ event->consuming--;
+ ret = TRUE;
+ }
+
+ WINPR_ASSERT(event->consuming >= 0);
+
+ if (event->consuming == 0)
+ {
+ /* Last client reset event before notify clients to continue */
+ ResetEvent(event->event);
+
+ if (event->waiting > 0)
+ {
+ /* Notify other clients to continue */
+ SetEvent(event->barrierEvent);
+ }
+ else
+ {
+ /* Only one client. Notify server directly */
+ SetEvent(event->doneEvent);
+ }
+ }
+ else /* (event->consuming > 0) */
+ {
+ if (wait)
+ {
+ /*
+ * This client need to wait. That means the client will
+ * continue waiting for other clients to finish.
+ * The last client should reset barrierEvent.
+ */
+ event->waiting++;
+ LeaveCriticalSection(&(event->lock));
+ WaitForSingleObject(event->barrierEvent, INFINITE);
+ EnterCriticalSection(&(event->lock));
+ event->waiting--;
+ if (event->waiting == 0)
+ {
+ /*
+ * This is last client waiting for barrierEvent.
+ * We can now discard barrierEvent and notify
+ * server to continue.
+ */
+ ResetEvent(event->barrierEvent);
+ SetEvent(event->doneEvent);
+ }
+ }
+ }
+
+ return ret;
+}
+
+void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
+{
+ struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
+
+ if (!event)
+ return NULL;
+
+ EnterCriticalSection(&(event->lock));
+
+ subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
+ 1, sizeof(struct rdp_shadow_multiclient_subscriber));
+ if (!subscriber)
+ goto out_error;
+
+ subscriber->ref = event;
+ subscriber->pleaseHandle = FALSE;
+
+ if (!ArrayList_Append(event->subscribers, subscriber))
+ goto out_free;
+
+ WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
+ event->eventid, event->consuming);
+ (void)_Consume(subscriber, TRUE);
+ WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
+ event->eventid, event->consuming);
+
+ LeaveCriticalSection(&(event->lock));
+
+ return subscriber;
+
+out_free:
+ free(subscriber);
+out_error:
+ LeaveCriticalSection(&(event->lock));
+ return NULL;
+}
+
+/*
+ * Consume my share and release my register
+ * If we have update event and pleaseHandle flag
+ * We need to consume. Anyway we need to clear
+ * pleaseHandle flag
+ */
+void shadow_multiclient_release_subscriber(void* subscriber)
+{
+ struct rdp_shadow_multiclient_subscriber* s = NULL;
+ rdpShadowMultiClientEvent* event = NULL;
+
+ if (!subscriber)
+ return;
+
+ s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
+ event = s->ref;
+
+ EnterCriticalSection(&(event->lock));
+
+ WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+ (void)_Consume(s, FALSE);
+ WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+
+ ArrayList_Remove(event->subscribers, subscriber);
+
+ LeaveCriticalSection(&(event->lock));
+
+ free(subscriber);
+
+ return;
+}
+
+BOOL shadow_multiclient_consume(void* subscriber)
+{
+ struct rdp_shadow_multiclient_subscriber* s = NULL;
+ rdpShadowMultiClientEvent* event = NULL;
+ BOOL ret = FALSE;
+
+ if (!subscriber)
+ return ret;
+
+ s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
+ event = s->ref;
+
+ EnterCriticalSection(&(event->lock));
+
+ WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+ ret = _Consume(s, TRUE);
+ WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
+ event->consuming);
+
+ LeaveCriticalSection(&(event->lock));
+
+ return ret;
+}
+
+HANDLE shadow_multiclient_getevent(void* subscriber)
+{
+ if (!subscriber)
+ return (HANDLE)NULL;
+
+ return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
+}