FreeRDP
Loading...
Searching...
No Matches
TPCircularBuffer.c
1//
2// TPCircularBuffer.c
3// Circular/Ring buffer implementation
4//
5// https://github.com/michaeltyson/TPCircularBuffer
6//
7// Created by Michael Tyson on 10/12/2011.
8//
9// Copyright (C) 2012-2013 A Tasty Pixel
10//
11// This software is provided 'as-is', without any express or implied
12// warranty. In no event will the authors be held liable for any damages
13// arising from the use of this software.
14//
15// Permission is granted to anyone to use this software for any purpose,
16// including commercial applications, and to alter it and redistribute it
17// freely, subject to the following restrictions:
18//
19// 1. The origin of this software must not be misrepresented; you must not
20// claim that you wrote the original software. If you use this software
21// in a product, an acknowledgment in the product documentation would be
22// appreciated but is not required.
23//
24// 2. Altered source versions must be plainly marked as such, and must not be
25// misrepresented as being the original software.
26//
27// 3. This notice may not be removed or altered from any source distribution.
28//
29
30#include <winpr/wlog.h>
31
32#include "TPCircularBuffer.h"
33#include "rdpsnd_main.h"
34
35#include <mach/mach.h>
36#include <stdio.h>
37
38#define reportResult(result, operation) (_reportResult((result), (operation), __FILE__, __LINE__))
39static inline bool _reportResult(kern_return_t result, const char* operation, const char* file,
40 int line)
41{
42 if (result != ERR_SUCCESS)
43 {
44 WLog_DBG(TAG, "%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
45 return false;
46 }
47 return true;
48}
49
50bool TPCircularBufferInit(TPCircularBuffer* buffer, int length)
51{
52
53 // Keep trying until we get our buffer, needed to handle race conditions
54 int retries = 3;
55 while (true)
56 {
57
58 buffer->length = round_page(length); // We need whole page sizes
59
60 // Temporarily allocate twice the length, so we have the contiguous address space to
61 // support a second instance of the buffer directly after
62 vm_address_t bufferAddress;
63 kern_return_t result = vm_allocate(mach_task_self(), &bufferAddress, buffer->length * 2,
64 VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
65 if (result != ERR_SUCCESS)
66 {
67 if (retries-- == 0)
68 {
69 reportResult(result, "Buffer allocation");
70 return false;
71 }
72 // Try again if we fail
73 continue;
74 }
75
76 // Now replace the second half of the allocation with a virtual copy of the first half.
77 // Deallocate the second half...
78 result = vm_deallocate(mach_task_self(), bufferAddress + buffer->length, buffer->length);
79 if (result != ERR_SUCCESS)
80 {
81 if (retries-- == 0)
82 {
83 reportResult(result, "Buffer deallocation");
84 return false;
85 }
86 // If this fails somehow, deallocate the whole region and try again
87 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
88 continue;
89 }
90
91 // Re-map the buffer to the address space immediately after the buffer
92 vm_address_t virtualAddress = bufferAddress + buffer->length;
93 vm_prot_t cur_prot, max_prot;
94 result = vm_remap(mach_task_self(),
95 &virtualAddress, // mirror target
96 buffer->length, // size of mirror
97 0, // auto alignment
98 0, // force remapping to virtualAddress
99 mach_task_self(), // same task
100 bufferAddress, // mirror source
101 0, // MAP READ-WRITE, NOT COPY
102 &cur_prot, // unused protection struct
103 &max_prot, // unused protection struct
104 VM_INHERIT_DEFAULT);
105 if (result != ERR_SUCCESS)
106 {
107 if (retries-- == 0)
108 {
109 reportResult(result, "Remap buffer memory");
110 return false;
111 }
112 // If this remap failed, we hit a race condition, so deallocate and try again
113 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
114 continue;
115 }
116
117 if (virtualAddress != bufferAddress + buffer->length)
118 {
119 // If the memory is not contiguous, clean up both allocated buffers and try again
120 if (retries-- == 0)
121 {
122 WLog_DBG(TAG, "Couldn't map buffer memory to end of buffer");
123 return false;
124 }
125
126 vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
127 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
128 continue;
129 }
130
131 buffer->buffer = (void*)bufferAddress;
132 buffer->fillCount = 0;
133 buffer->head = buffer->tail = 0;
134
135 return true;
136 }
137 return false;
138}
139
140void TPCircularBufferCleanup(TPCircularBuffer* buffer)
141{
142 vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
143 memset(buffer, 0, sizeof(TPCircularBuffer));
144}
145
146void TPCircularBufferClear(TPCircularBuffer* buffer)
147{
148 int32_t fillCount;
149 if (TPCircularBufferTail(buffer, &fillCount))
150 {
151 TPCircularBufferConsume(buffer, fillCount);
152 }
153}