Blame server/shadow/shadow_mcevent.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * FreeRDP: A Remote Desktop Protocol Implementation
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2015 Jiang Zihao <zihao.jiang@yahoo.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <assert.h>
Packit 1fb8d4
#include <freerdp/log.h>
Packit 1fb8d4
#include "shadow.h"
Packit 1fb8d4
Packit 1fb8d4
#define TAG SERVER_TAG("shadow.mcevent")
Packit 1fb8d4
Packit Service 5a9772
struct rdp_shadow_multiclient_event
Packit Service 5a9772
{
Packit Service 5a9772
	HANDLE event;        /* Kickoff event */
Packit Service 5a9772
	HANDLE barrierEvent; /* Represents that all clients have consumed event */
Packit Service 5a9772
	HANDLE doneEvent;    /* Event handling finished. Server could continue */
Packit Service 5a9772
	wArrayList* subscribers;
Packit Service 5a9772
	CRITICAL_SECTION lock;
Packit Service 5a9772
	int consuming;
Packit Service 5a9772
	int waiting;
Packit Service 5a9772
Packit Service 5a9772
	/* For debug */
Packit Service 5a9772
	int eventid;
Packit Service 5a9772
};
Packit Service 5a9772
Packit 1fb8d4
struct rdp_shadow_multiclient_subscriber
Packit 1fb8d4
{
Packit 1fb8d4
	rdpShadowMultiClientEvent* ref;
Packit 1fb8d4
	BOOL pleaseHandle; /* Indicate if server expects my handling in this turn */
Packit 1fb8d4
};
Packit 1fb8d4
Packit Service 5a9772
rdpShadowMultiClientEvent* shadow_multiclient_new(void)
Packit 1fb8d4
{
Packit Service 5a9772
	rdpShadowMultiClientEvent* event =
Packit Service 5a9772
	    (rdpShadowMultiClientEvent*)calloc(1, sizeof(rdpShadowMultiClientEvent));
Packit 1fb8d4
	if (!event)
Packit 1fb8d4
		goto out_error;
Packit 1fb8d4
Packit 1fb8d4
	event->event = CreateEvent(NULL, TRUE, FALSE, NULL);
Packit 1fb8d4
	if (!event->event)
Packit 1fb8d4
		goto out_free;
Packit 1fb8d4
Packit 1fb8d4
	event->barrierEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Packit 1fb8d4
	if (!event->barrierEvent)
Packit 1fb8d4
		goto out_free_event;
Packit 1fb8d4
Packit 1fb8d4
	event->doneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
Packit 1fb8d4
	if (!event->doneEvent)
Packit 1fb8d4
		goto out_free_barrierEvent;
Packit 1fb8d4
Packit 1fb8d4
	event->subscribers = ArrayList_New(TRUE);
Packit 1fb8d4
	if (!event->subscribers)
Packit 1fb8d4
		goto out_free_doneEvent;
Packit 1fb8d4
Packit 1fb8d4
	if (!InitializeCriticalSectionAndSpinCount(&(event->lock), 4000))
Packit 1fb8d4
		goto out_free_subscribers;
Packit 1fb8d4
Packit 1fb8d4
	event->consuming = 0;
Packit 1fb8d4
	event->waiting = 0;
Packit 1fb8d4
	event->eventid = 0;
Packit 1fb8d4
	SetEvent(event->doneEvent);
Packit 1fb8d4
	return event;
Packit 1fb8d4
Packit 1fb8d4
out_free_subscribers:
Packit 1fb8d4
	ArrayList_Free(event->subscribers);
Packit 1fb8d4
out_free_doneEvent:
Packit 1fb8d4
	CloseHandle(event->doneEvent);
Packit 1fb8d4
out_free_barrierEvent:
Packit 1fb8d4
	CloseHandle(event->barrierEvent);
Packit 1fb8d4
out_free_event:
Packit 1fb8d4
	CloseHandle(event->event);
Packit 1fb8d4
out_free:
Packit 1fb8d4
	free(event);
Packit 1fb8d4
out_error:
Packit Service 5a9772
	return (rdpShadowMultiClientEvent*)NULL;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void shadow_multiclient_free(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!event)
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	DeleteCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	ArrayList_Free(event->subscribers);
Packit 1fb8d4
	CloseHandle(event->doneEvent);
Packit 1fb8d4
	CloseHandle(event->barrierEvent);
Packit 1fb8d4
	CloseHandle(event->event);
Packit 1fb8d4
	free(event);
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void _Publish(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	wArrayList* subscribers;
Packit 1fb8d4
	struct rdp_shadow_multiclient_subscriber* subscriber = NULL;
Packit 1fb8d4
	int i;
Packit 1fb8d4
Packit 1fb8d4
	subscribers = event->subscribers;
Packit 1fb8d4
Packit 1fb8d4
	assert(event->consuming == 0);
Packit 1fb8d4
Packit 1fb8d4
	/* Count subscribing clients */
Packit 1fb8d4
	ArrayList_Lock(subscribers);
Packit 1fb8d4
	for (i = 0; i < ArrayList_Count(subscribers); i++)
Packit 1fb8d4
	{
Packit Service 5a9772
		subscriber = (struct rdp_shadow_multiclient_subscriber*)ArrayList_GetItem(subscribers, i);
Packit 1fb8d4
		/* Set flag to subscriber: I acknowledge and please handle */
Packit 1fb8d4
		subscriber->pleaseHandle = TRUE;
Packit 1fb8d4
		event->consuming++;
Packit 1fb8d4
	}
Packit 1fb8d4
	ArrayList_Unlock(subscribers);
Packit 1fb8d4
Packit 1fb8d4
	if (event->consuming > 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		event->eventid = (event->eventid & 0xff) + 1;
Packit 1fb8d4
		WLog_VRB(TAG, "Server published event %d. %d clients.\n", event->eventid, event->consuming);
Packit 1fb8d4
		ResetEvent(event->doneEvent);
Packit 1fb8d4
		SetEvent(event->event);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static void _WaitForSubscribers(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	if (event->consuming > 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Wait for clients done */
Packit 1fb8d4
		WLog_VRB(TAG, "Server wait event %d. %d clients.\n", event->eventid, event->consuming);
Packit 1fb8d4
		LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
		WaitForSingleObject(event->doneEvent, INFINITE);
Packit 1fb8d4
		EnterCriticalSection(&(event->lock));
Packit 1fb8d4
		WLog_VRB(TAG, "Server quit event %d. %d clients.\n", event->eventid, event->consuming);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Last subscriber should have already reset the event */
Packit 1fb8d4
	assert(WaitForSingleObject(event->event, 0) != WAIT_OBJECT_0);
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void shadow_multiclient_publish(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!event)
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&(event->lock));
Packit 1fb8d4
	_Publish(event);
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
void shadow_multiclient_wait(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!event)
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&(event->lock));
Packit 1fb8d4
	_WaitForSubscribers(event);
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
void shadow_multiclient_publish_and_wait(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!event)
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&(event->lock));
Packit 1fb8d4
	_Publish(event);
Packit 1fb8d4
	_WaitForSubscribers(event);
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static BOOL _Consume(struct rdp_shadow_multiclient_subscriber* subscriber, BOOL wait)
Packit 1fb8d4
{
Packit 1fb8d4
	rdpShadowMultiClientEvent* event = subscriber->ref;
Packit 1fb8d4
	BOOL ret = FALSE;
Packit 1fb8d4
Packit Service 5a9772
	if (WaitForSingleObject(event->event, 0) == WAIT_OBJECT_0 && subscriber->pleaseHandle)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Consume my share. Server is waiting for us */
Packit 1fb8d4
		event->consuming--;
Packit 1fb8d4
		ret = TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	assert(event->consuming >= 0);
Packit 1fb8d4
Packit 1fb8d4
	if (event->consuming == 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Last client reset event before notify clients to continue */
Packit 1fb8d4
		ResetEvent(event->event);
Packit 1fb8d4
Packit 1fb8d4
		if (event->waiting > 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* Notify other clients to continue */
Packit 1fb8d4
			SetEvent(event->barrierEvent);
Packit 1fb8d4
		}
Packit 1fb8d4
		else
Packit 1fb8d4
		{
Packit 1fb8d4
			/* Only one client. Notify server directly */
Packit 1fb8d4
			SetEvent(event->doneEvent);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
	else /* (event->consuming > 0) */
Packit 1fb8d4
	{
Packit Service 5a9772
		if (wait)
Packit 1fb8d4
		{
Packit Service 5a9772
			/*
Packit 1fb8d4
			 * This client need to wait. That means the client will
Packit 1fb8d4
			 * continue waiting for other clients to finish.
Packit 1fb8d4
			 * The last client should reset barrierEvent.
Packit 1fb8d4
			 */
Packit 1fb8d4
			event->waiting++;
Packit 1fb8d4
			LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
			WaitForSingleObject(event->barrierEvent, INFINITE);
Packit 1fb8d4
			EnterCriticalSection(&(event->lock));
Packit 1fb8d4
			event->waiting--;
Packit 1fb8d4
			if (event->waiting == 0)
Packit 1fb8d4
			{
Packit 1fb8d4
				/*
Packit 1fb8d4
				 * This is last client waiting for barrierEvent.
Packit Service 5a9772
				 * We can now discard barrierEvent and notify
Packit 1fb8d4
				 * server to continue.
Packit 1fb8d4
				 */
Packit 1fb8d4
				ResetEvent(event->barrierEvent);
Packit 1fb8d4
				SetEvent(event->doneEvent);
Packit 1fb8d4
			}
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return ret;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
void* shadow_multiclient_get_subscriber(rdpShadowMultiClientEvent* event)
Packit 1fb8d4
{
Packit 1fb8d4
	struct rdp_shadow_multiclient_subscriber* subscriber;
Packit 1fb8d4
Packit 1fb8d4
	if (!event)
Packit 1fb8d4
		return NULL;
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&(event->lock));
Packit 1fb8d4
Packit Service 5a9772
	subscriber = (struct rdp_shadow_multiclient_subscriber*)calloc(
Packit Service 5a9772
	    1, sizeof(struct rdp_shadow_multiclient_subscriber));
Packit 1fb8d4
	if (!subscriber)
Packit 1fb8d4
		goto out_error;
Packit 1fb8d4
Packit 1fb8d4
	subscriber->ref = event;
Packit 1fb8d4
	subscriber->pleaseHandle = FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (ArrayList_Add(event->subscribers, subscriber) < 0)
Packit 1fb8d4
		goto out_free;
Packit 1fb8d4
Packit Service 5a9772
	WLog_VRB(TAG, "Get subscriber %p. Wait event %d. %d clients.\n", (void*)subscriber,
Packit Service 5a9772
	         event->eventid, event->consuming);
Packit 1fb8d4
	(void)_Consume(subscriber, TRUE);
Packit Service 5a9772
	WLog_VRB(TAG, "Get subscriber %p. Quit event %d. %d clients.\n", (void*)subscriber,
Packit Service 5a9772
	         event->eventid, event->consuming);
Packit 1fb8d4
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	return subscriber;
Packit 1fb8d4
Packit 1fb8d4
out_free:
Packit 1fb8d4
	free(subscriber);
Packit 1fb8d4
out_error:
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
	return NULL;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
/*
Packit 1fb8d4
 * Consume my share and release my register
Packit 1fb8d4
 * If we have update event and pleaseHandle flag
Packit 1fb8d4
 * We need to consume. Anyway we need to clear
Packit 1fb8d4
 * pleaseHandle flag
Packit 1fb8d4
 */
Packit 1fb8d4
void shadow_multiclient_release_subscriber(void* subscriber)
Packit 1fb8d4
{
Packit 1fb8d4
	struct rdp_shadow_multiclient_subscriber* s;
Packit 1fb8d4
	rdpShadowMultiClientEvent* event;
Packit 1fb8d4
Packit 1fb8d4
	if (!subscriber)
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
Packit 1fb8d4
	event = s->ref;
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&(event->lock));
Packit 1fb8d4
Packit Service 5a9772
	WLog_VRB(TAG, "Release Subscriber %p. Drop event %d. %d clients.\n", subscriber, event->eventid,
Packit Service 5a9772
	         event->consuming);
Packit 1fb8d4
	(void)_Consume(s, FALSE);
Packit Service 5a9772
	WLog_VRB(TAG, "Release Subscriber %p. Quit event %d. %d clients.\n", subscriber, event->eventid,
Packit Service 5a9772
	         event->consuming);
Packit 1fb8d4
Packit 1fb8d4
	ArrayList_Remove(event->subscribers, subscriber);
Packit 1fb8d4
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	free(subscriber);
Packit 1fb8d4
Packit 1fb8d4
	return;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL shadow_multiclient_consume(void* subscriber)
Packit 1fb8d4
{
Packit 1fb8d4
	struct rdp_shadow_multiclient_subscriber* s;
Packit 1fb8d4
	rdpShadowMultiClientEvent* event;
Packit 1fb8d4
	BOOL ret = FALSE;
Packit 1fb8d4
Packit 1fb8d4
	if (!subscriber)
Packit 1fb8d4
		return ret;
Packit 1fb8d4
Packit 1fb8d4
	s = (struct rdp_shadow_multiclient_subscriber*)subscriber;
Packit 1fb8d4
	event = s->ref;
Packit 1fb8d4
Packit 1fb8d4
	EnterCriticalSection(&(event->lock));
Packit 1fb8d4
Packit Service 5a9772
	WLog_VRB(TAG, "Subscriber %p wait event %d. %d clients.\n", subscriber, event->eventid,
Packit Service 5a9772
	         event->consuming);
Packit 1fb8d4
	ret = _Consume(s, TRUE);
Packit Service 5a9772
	WLog_VRB(TAG, "Subscriber %p quit event %d. %d clients.\n", subscriber, event->eventid,
Packit Service 5a9772
	         event->consuming);
Packit 1fb8d4
Packit 1fb8d4
	LeaveCriticalSection(&(event->lock));
Packit 1fb8d4
Packit 1fb8d4
	return ret;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
HANDLE shadow_multiclient_getevent(void* subscriber)
Packit 1fb8d4
{
Packit 1fb8d4
	if (!subscriber)
Packit 1fb8d4
		return (HANDLE)NULL;
Packit 1fb8d4
Packit 1fb8d4
	return ((struct rdp_shadow_multiclient_subscriber*)subscriber)->ref->event;
Packit 1fb8d4
}