FreeRDP
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__))
39 static 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 
50 bool 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 
140 void 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 
146 void TPCircularBufferClear(TPCircularBuffer* buffer)
147 {
148  int32_t fillCount;
149  if (TPCircularBufferTail(buffer, &fillCount))
150  {
151  TPCircularBufferConsume(buffer, fillCount);
152  }
153 }