Skip to content

Commit

Permalink
Patching TPCircularBuffer based on suggestion from landonf and maqr
Browse files Browse the repository at this point in the history
  • Loading branch information
jweinberg committed Apr 10, 2012
1 parent fc26b00 commit 655ea5e
Showing 1 changed file with 57 additions and 41 deletions.
98 changes: 57 additions & 41 deletions OpenEmu/TPCircularBuffer.c
Expand Up @@ -38,48 +38,64 @@ static inline bool _checkResult(kern_return_t result, const char *operation, con
}

bool TPCircularBufferInit(TPCircularBuffer *buffer, int length) {
assert(length);
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;
if ( !checkResult(vm_allocate(mach_task_self(), &bufferAddress, buffer->length * 2, TRUE /* (don't use the current bufferAddress value) */),
"Buffer allocation") ) return false;

// Now replace the second half of the allocation with a virtual copy of the first half. Deallocate the second half...
if ( !checkResult(vm_deallocate(mach_task_self(), bufferAddress + buffer->length, buffer->length),
"Buffer deallocation") ) return false;

// Then create a memory entry that refers to the buffer
vm_size_t entry_length = buffer->length;
mach_port_t memoryEntry;
if ( !checkResult(mach_make_memory_entry(mach_task_self(), &entry_length, bufferAddress, VM_PROT_READ|VM_PROT_WRITE, &memoryEntry, 0),
"Create memory entry") ) {
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
return false;
}

// And map the memory entry to the address space immediately after the buffer
vm_address_t virtualAddress = bufferAddress + buffer->length;
if ( !checkResult(vm_map(mach_task_self(), &virtualAddress, buffer->length, 0, FALSE, memoryEntry, 0, FALSE, VM_PROT_READ | VM_PROT_WRITE, VM_PROT_READ | VM_PROT_WRITE, VM_INHERIT_DEFAULT),
"Map buffer memory") ) {
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
return false;
}

if ( virtualAddress != bufferAddress+buffer->length ) {
printf("Couldn't map buffer memory to end of buffer\n");
vm_deallocate(mach_task_self(), virtualAddress, buffer->length);
vm_deallocate(mach_task_self(), bufferAddress, buffer->length);
return false;
// keep trying tuntil we get our buffer, needed to handle race conditions
while(1) {
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;
if ( !checkResult(vm_allocate(mach_task_self(),
&bufferAddress,
buffer->length * 2,
VM_FLAGS_ANYWHERE), // allocate anywhere it'll fit
"Buffer allocation") ) {
// 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...
if ( !checkResult(vm_deallocate(mach_task_self(),
bufferAddress + buffer->length,
buffer->length),
"Buffer deallocation") ) {
// 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;
if(!checkResult(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), "Remap buffer memory")) {
// 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
printf("Couldn't map buffer memory to end of buffer\n");
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;
}

buffer->buffer = (void*)bufferAddress;
buffer->fillCount = 0;
buffer->head = buffer->tail = 0;

return true;
}

void TPCircularBufferCleanup(TPCircularBuffer *buffer) {
Expand Down

0 comments on commit 655ea5e

Please sign in to comment.