Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
341
third_party/FreeRDP/server/shadow/shadow_mcevent.c
vendored
Normal file
341
third_party/FreeRDP/server/shadow/shadow_mcevent.c
vendored
Normal file
@@ -0,0 +1,341 @@
|
||||
/**
|
||||
* 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(nullptr, TRUE, FALSE, nullptr);
|
||||
if (!event->event)
|
||||
goto out_free;
|
||||
|
||||
event->barrierEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
if (!event->barrierEvent)
|
||||
goto out_free_event;
|
||||
|
||||
event->doneEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
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;
|
||||
(void)SetEvent(event->doneEvent);
|
||||
return event;
|
||||
|
||||
out_free_subscribers:
|
||||
ArrayList_Free(event->subscribers);
|
||||
out_free_doneEvent:
|
||||
(void)CloseHandle(event->doneEvent);
|
||||
out_free_barrierEvent:
|
||||
(void)CloseHandle(event->barrierEvent);
|
||||
out_free_event:
|
||||
(void)CloseHandle(event->event);
|
||||
out_free:
|
||||
free(event);
|
||||
out_error:
|
||||
return (rdpShadowMultiClientEvent*)nullptr;
|
||||
}
|
||||
|
||||
void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
DeleteCriticalSection(&(event->lock));
|
||||
|
||||
ArrayList_Free(event->subscribers);
|
||||
(void)CloseHandle(event->doneEvent);
|
||||
(void)CloseHandle(event->barrierEvent);
|
||||
(void)CloseHandle(event->event);
|
||||
free(event);
|
||||
}
|
||||
|
||||
static void Publish(rdpShadowMultiClientEvent* event)
|
||||
{
|
||||
wArrayList* subscribers = nullptr;
|
||||
struct rdp_shadow_multiclient_subscriber* subscriber = nullptr;
|
||||
|
||||
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);
|
||||
(void)ResetEvent(event->doneEvent);
|
||||
(void)SetEvent(event->event);
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
(void)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);
|
||||
}
|
||||
|
||||
void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
EnterCriticalSection(&(event->lock));
|
||||
Publish(event);
|
||||
LeaveCriticalSection(&(event->lock));
|
||||
}
|
||||
void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
EnterCriticalSection(&(event->lock));
|
||||
WaitForSubscribers(event);
|
||||
LeaveCriticalSection(&(event->lock));
|
||||
}
|
||||
void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
|
||||
{
|
||||
if (!event)
|
||||
return;
|
||||
|
||||
EnterCriticalSection(&(event->lock));
|
||||
Publish(event);
|
||||
WaitForSubscribers(event);
|
||||
LeaveCriticalSection(&(event->lock));
|
||||
}
|
||||
|
||||
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 */
|
||||
(void)ResetEvent(event->event);
|
||||
|
||||
if (event->waiting > 0)
|
||||
{
|
||||
/* Notify other clients to continue */
|
||||
(void)SetEvent(event->barrierEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Only one client. Notify server directly */
|
||||
(void)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));
|
||||
(void)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.
|
||||
*/
|
||||
(void)ResetEvent(event->barrierEvent);
|
||||
(void)SetEvent(event->doneEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
|
||||
{
|
||||
struct rdp_shadow_multiclient_subscriber* subscriber = nullptr;
|
||||
|
||||
if (!event)
|
||||
return nullptr;
|
||||
|
||||
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 nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 = nullptr;
|
||||
rdpShadowMultiClientEvent* event = nullptr;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
BOOL shadow_multiclient_consume(void* subscriber)
|
||||
{
|
||||
struct rdp_shadow_multiclient_subscriber* s = nullptr;
|
||||
rdpShadowMultiClientEvent* event = nullptr;
|
||||
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) nullptr;
|
||||
|
||||
return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
|
||||
}
|
||||
Reference in New Issue
Block a user