Blame channels/rdpsnd/client/ios/TPCircularBuffer.c

Packit 1fb8d4
//
Packit 1fb8d4
//  TPCircularBuffer.c
Packit 1fb8d4
//  Circular/Ring buffer implementation
Packit 1fb8d4
//
Packit 1fb8d4
//  https://github.com/michaeltyson/TPCircularBuffer
Packit 1fb8d4
//
Packit 1fb8d4
//  Created by Michael Tyson on 10/12/2011.
Packit 1fb8d4
//
Packit 1fb8d4
//  Copyright (C) 2012-2013 A Tasty Pixel
Packit 1fb8d4
//
Packit 1fb8d4
//  This software is provided 'as-is', without any express or implied
Packit 1fb8d4
//  warranty.  In no event will the authors be held liable for any damages
Packit 1fb8d4
//  arising from the use of this software.
Packit 1fb8d4
//
Packit 1fb8d4
//  Permission is granted to anyone to use this software for any purpose,
Packit 1fb8d4
//  including commercial applications, and to alter it and redistribute it
Packit 1fb8d4
//  freely, subject to the following restrictions:
Packit 1fb8d4
//
Packit 1fb8d4
//  1. The origin of this software must not be misrepresented; you must not
Packit 1fb8d4
//     claim that you wrote the original software. If you use this software
Packit 1fb8d4
//     in a product, an acknowledgment in the product documentation would be
Packit 1fb8d4
//     appreciated but is not required.
Packit 1fb8d4
//
Packit 1fb8d4
//  2. Altered source versions must be plainly marked as such, and must not be
Packit 1fb8d4
//     misrepresented as being the original software.
Packit 1fb8d4
//
Packit 1fb8d4
//  3. This notice may not be removed or altered from any source distribution.
Packit 1fb8d4
//
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/wlog.h>
Packit 1fb8d4
Packit 1fb8d4
#include "TPCircularBuffer.h"
Packit 1fb8d4
#include "rdpsnd_main.h"
Packit 1fb8d4
Packit 1fb8d4
#include <mach/mach.h>
Packit 1fb8d4
#include <stdio.h>
Packit 1fb8d4
Packit Service 5a9772
#define reportResult(result, operation) (_reportResult((result), (operation), __FILE__, __LINE__))
Packit Service 5a9772
static inline bool _reportResult(kern_return_t result, const char* operation, const char* file,
Packit Service 5a9772
                                 int line)
Packit Service 5a9772
{
Packit Service 5a9772
	if (result != ERR_SUCCESS)
Packit Service 5a9772
	{
Packit Service 5a9772
		WLog_DBG(TAG, "%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
Packit Service 5a9772
		return false;
Packit Service 5a9772
	}
Packit Service 5a9772
	return true;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
bool TPCircularBufferInit(TPCircularBuffer* buffer, int length)
Packit Service 5a9772
{
Packit Service 5a9772
Packit Service 5a9772
	// Keep trying until we get our buffer, needed to handle race conditions
Packit Service 5a9772
	int retries = 3;
Packit Service 5a9772
	while (true)
Packit Service 5a9772
	{
Packit Service 5a9772
Packit Service 5a9772
		buffer->length = round_page(length); // We need whole page sizes
Packit Service 5a9772
Packit Service 5a9772
		// Temporarily allocate twice the length, so we have the contiguous address space to
Packit Service 5a9772
		// support a second instance of the buffer directly after
Packit Service 5a9772
		vm_address_t bufferAddress;
Packit Service 5a9772
		kern_return_t result = vm_allocate(mach_task_self(), &bufferAddress, buffer->length * 2,
Packit Service 5a9772
		                                   VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
Packit Service 5a9772
		if (result != ERR_SUCCESS)
Packit Service 5a9772
		{
Packit Service 5a9772
			if (retries-- == 0)
Packit Service 5a9772
			{
Packit Service 5a9772
				reportResult(result, "Buffer allocation");
Packit Service 5a9772
				return false;
Packit Service 5a9772
			}
Packit Service 5a9772
			// Try again if we fail
Packit Service 5a9772
			continue;
Packit Service 5a9772
		}
Packit Service 5a9772
Packit Service 5a9772
		// Now replace the second half of the allocation with a virtual copy of the first half.
Packit Service 5a9772
		// Deallocate the second half...
Packit Service 5a9772
		result = vm_deallocate(mach_task_self(), bufferAddress + buffer->length, buffer->length);
Packit Service 5a9772
		if (result != ERR_SUCCESS)
Packit Service 5a9772
		{
Packit Service 5a9772
			if (retries-- == 0)
Packit Service 5a9772
			{
Packit Service 5a9772
				reportResult(result, "Buffer deallocation");
Packit Service 5a9772
				return false;
Packit Service 5a9772
			}
Packit Service 5a9772
			// If this fails somehow, deallocate the whole region and try again
Packit Service 5a9772
			vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
Packit Service 5a9772
			continue;
Packit Service 5a9772
		}
Packit Service 5a9772
Packit Service 5a9772
		// Re-map the buffer to the address space immediately after the buffer
Packit Service 5a9772
		vm_address_t virtualAddress = bufferAddress + buffer->length;
Packit Service 5a9772
		vm_prot_t cur_prot, max_prot;
Packit Service 5a9772
		result = vm_remap(mach_task_self(),
Packit Service 5a9772
		                  &virtualAddress,  // mirror target
Packit Service 5a9772
		                  buffer->length,   // size of mirror
Packit Service 5a9772
		                  0,                // auto alignment
Packit Service 5a9772
		                  0,                // force remapping to virtualAddress
Packit Service 5a9772
		                  mach_task_self(), // same task
Packit Service 5a9772
		                  bufferAddress,    // mirror source
Packit Service 5a9772
		                  0,                // MAP READ-WRITE, NOT COPY
Packit Service 5a9772
		                  &cur_prot,        // unused protection struct
Packit Service 5a9772
		                  &max_prot,        // unused protection struct
Packit Service 5a9772
		                  VM_INHERIT_DEFAULT);
Packit Service 5a9772
		if (result != ERR_SUCCESS)
Packit Service 5a9772
		{
Packit Service 5a9772
			if (retries-- == 0)
Packit Service 5a9772
			{
Packit Service 5a9772
				reportResult(result, "Remap buffer memory");
Packit Service 5a9772
				return false;
Packit Service 5a9772
			}
Packit Service 5a9772
			// If this remap failed, we hit a race condition, so deallocate and try again
Packit Service 5a9772
			vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
Packit Service 5a9772
			continue;
Packit Service 5a9772
		}
Packit Service 5a9772
Packit Service 5a9772
		if (virtualAddress != bufferAddress + buffer->length)
Packit Service 5a9772
		{
Packit Service 5a9772
			// If the memory is not contiguous, clean up both allocated buffers and try again
Packit Service 5a9772
			if (retries-- == 0)
Packit Service 5a9772
			{
Packit Service 5a9772
				WLog_DBG(TAG, "Couldn't map buffer memory to end of buffer");
Packit Service 5a9772
				return false;
Packit Service 5a9772
			}
Packit Service 5a9772
Packit Service 5a9772
			vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
Packit Service 5a9772
			vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
Packit Service 5a9772
			continue;
Packit Service 5a9772
		}
Packit Service 5a9772
Packit Service 5a9772
		buffer->buffer = (void*)bufferAddress;
Packit Service 5a9772
		buffer->fillCount = 0;
Packit Service 5a9772
		buffer->head = buffer->tail = 0;
Packit Service 5a9772
Packit Service 5a9772
		return true;
Packit Service 5a9772
	}
Packit Service 5a9772
	return false;
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
void TPCircularBufferCleanup(TPCircularBuffer* buffer)
Packit Service 5a9772
{
Packit Service 5a9772
	vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
Packit Service 5a9772
	memset(buffer, 0, sizeof(TPCircularBuffer));
Packit 1fb8d4
}
Packit 1fb8d4
Packit Service 5a9772
void TPCircularBufferClear(TPCircularBuffer* buffer)
Packit Service 5a9772
{
Packit Service 5a9772
	int32_t fillCount;
Packit Service 5a9772
	if (TPCircularBufferTail(buffer, &fillCount))
Packit Service 5a9772
	{
Packit Service 5a9772
		TPCircularBufferConsume(buffer, fillCount);
Packit Service 5a9772
	}
Packit 1fb8d4
}