forked from videolan/vlc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTPCircularBuffer.c
115 lines (100 loc) · 4.5 KB
/
TPCircularBuffer.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//
// TPCircularBuffer.c
// Circular/Ring buffer implementation
//
// Created by Michael Tyson on 10/12/2011.
// Copyright 2011-2012 A Tasty Pixel. All rights reserved.
#include "audio_output/TPCircularBuffer.h"
#include <mach/mach.h>
#include <stdio.h>
#define reportResult(result,operation) (_reportResult((result),(operation),strrchr(__FILE__, '/')+1,__LINE__))
static inline bool _reportResult(kern_return_t result, const char *operation, const char* file, int line) {
if ( result != ERR_SUCCESS ) {
printf("%s:%d: %s: %s\n", file, line, operation, mach_error_string(result));
return false;
}
return true;
}
bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
// Keep trying until we get our buffer, needed to handle race conditions
int retries = 3;
while ( true ) {
buffer->length = round_page(length); // We need whole page sizes
// Temporarily allocate twice the length, so we have the contiguous address space to
// support a second instance of the buffer directly after
vm_address_t bufferAddress;
kern_return_t result = vm_allocate(mach_task_self(),
&bufferAddress,
buffer->length * 2,
VM_FLAGS_ANYWHERE); // allocate anywhere it'll fit
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Buffer allocation");
return false;
}
// Try again if we fail
continue;
}
// Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half...
result = vm_deallocate(mach_task_self(),
bufferAddress + buffer->length,
buffer->length);
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Buffer deallocation");
return false;
}
// If this fails somehow, deallocate the whole region and try again
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
continue;
}
// Re-map the buffer to the address space immediately after the buffer
vm_address_t virtualAddress = bufferAddress + buffer->length;
vm_prot_t cur_prot, max_prot;
result = vm_remap(mach_task_self(),
&virtualAddress, // mirror target
buffer->length, // size of mirror
0, // auto alignment
0, // force remapping to virtualAddress
mach_task_self(), // same task
bufferAddress, // mirror source
0, // MAP READ-WRITE, NOT COPY
&cur_prot, // unused protection struct
&max_prot, // unused protection struct
VM_INHERIT_DEFAULT);
if ( result != ERR_SUCCESS ) {
if ( retries-- == 0 ) {
reportResult(result, "Remap buffer memory");
return false;
}
// If this remap failed, we hit a race condition, so deallocate and try again
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
continue;
}
if ( virtualAddress != bufferAddress+buffer->length ) {
// If the memory is not contiguous, clean up both allocated buffers and try again
if ( retries-- == 0 ) {
printf("Couldn't map buffer memory to end of buffer\n");
return false;
}
vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
continue;
}
buffer->buffer = (void*)bufferAddress;
buffer->fillCount = 0;
buffer->head = buffer->tail = 0;
return true;
}
return false;
}
void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
vm_deallocate(mach_task_self(), (vm_address_t)buffer->buffer, buffer->length * 2);
memset(buffer, 0, sizeof(TPCircularBuffer));
}
void TPCircularBufferClear(TPCircularBuffer *buffer) {
int32_t fillCount;
if ( TPCircularBufferTail(buffer, &fillCount) ) {
TPCircularBufferConsume(buffer, fillCount);
}
}