3 // Circular/Ring buffer implementation
5 // Created by Michael Tyson on 10/12/2011.
6 // Copyright 2011-2012 A Tasty Pixel. All rights reserved.
9 #include "TPCircularBuffer.h"
10 #include <mach/mach.h>
13 #define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
14 static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) {
15 if ( result != ERR_SUCCESS ) {
16 printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
22 bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
24 // Keep trying until we get our buffer, needed to handle race conditions
28 buffer->length = round_page(length); // We need whole page sizes
30 // Temporarily allocate twice the length, so we have the contiguous address space to
31 // support a second instance of the buffer directly after
32 vm_address_t bufferAddress;
33 kern_return_t result = vm_allocate(mach_task_self(),
36 VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
37 if ( result != ERR_SUCCESS ) {
38 if ( retries-- == 0 ) {
39 reportResult(result, "Buffer allocation");
42 // Try again if we fail
46 // Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half...
47 result = vm_deallocate(mach_task_self(),
48 bufferAddress + buffer->length,
50 if ( result != ERR_SUCCESS ) {
51 if ( retries-- == 0 ) {
52 reportResult(result, "Buffer deallocation");
55 // If this fails somehow, deallocate the whole region and try again
56 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
60 // Re-map the buffer to the address space immediately after the buffer
61 vm_address_t virtualAddress = bufferAddress + buffer->length;
62 vm_prot_t cur_prot, max_prot;
63 result = vm_remap(mach_task_self(),
64 &virtualAddress, // mirror target
65 buffer->length, // size of mirror
67 0, // force remapping to virtualAddress
68 mach_task_self(), // same task
69 bufferAddress, // mirror source
70 0, // MAP READ-WRITE, NOT COPY
71 &cur_prot, // unused protection struct
72 &max_prot, // unused protection struct
74 if ( result != ERR_SUCCESS ) {
75 if ( retries-- == 0 ) {
76 reportResult(result, "Remap buffer memory");
79 // If this remap failed, we hit a race condition, so deallocate and try again
80 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
84 if ( virtualAddress != bufferAddress+buffer->length ) {
85 // If the memory is not contiguous, clean up both allocated buffers and try again
86 if ( retries-- == 0 ) {
87 printf("Couldn't map buffer memory to end of buffer\n");
91 vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
92 vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
96 buffer->buffer = (void*)bufferAddress;
97 buffer->fillCount = 0;
98 buffer->head = buffer->tail = 0;
105 void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
106 vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
107 memset(buffer, 0, sizeof(TPCircularBuffer));
110 void TPCircularBufferClear(TPCircularBuffer *buffer) {
112 if ( TPCircularBufferTail(buffer, &fillCount) ) {
113 TPCircularBufferConsume(buffer, fillCount);