#import #include #include #include #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, int32_t length) { return _TPCircularBufferInit(buffer, length, sizeof(TPCircularBuffer)); } bool _TPCircularBufferInit(TPCircularBuffer *buffer, int32_t length, size_t structSize) { assert(length > 0); if ( structSize != sizeof(TPCircularBuffer) ) { fprintf(stderr, "TPCircularBuffer: Header version mismatch. Check for old versions of TPCircularBuffer in your project\n"); abort(); } // Keep trying until we get our buffer, needed to handle race conditions int retries = 3; while ( true ) { buffer->length = (int32_t)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); } }