diff --git a/CMakeLists.txt b/CMakeLists.txt index d27ab91..39b732c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -85,8 +85,8 @@ endif() set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "") set(CMAKE_CXX_IMPLICIT_LINK_DIRECTORIES "") -set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-unused-variable") -set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-unused-variable") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -Wno-unused-variable -Wno-unused-but-set-variable") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -Wno-unused-variable -Wno-unused-but-set-variable") set(LIB_CFLAGS "-std=c++11 -fno-exceptions -fno-rtti -Werror -Wall -Werror=strict-prototypes -Werror=old-style-definition -Werror=missing-prototypes -fdiagnostics-color=auto") diff --git a/src/os.cpp b/src/os.cpp index d457042..fb5eca2 100644 --- a/src/os.cpp +++ b/src/os.cpp @@ -50,8 +50,14 @@ #include #else + #include #include +#include +#ifndef MAP_ANONYMOUS +#define MAP_ANONYMOUS MAP_ANON +#endif + #endif #if defined(__FreeBSD__) || defined(__MACH__) @@ -116,14 +122,14 @@ struct SoundIoOsCond { #if defined(SOUNDIO_OS_WINDOWS) INIT_ONCE win32_init_once = INIT_ONCE_STATIC_INIT; +static double win32_time_resolution; +SYSTEM_INFO win32_system_info; #else atomic_bool initialized = ATOMIC_VAR_INIT(false); pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER; #endif -#if defined(SOUNDIO_OS_WINDOWS) -static double win32_time_resolution; -#endif +static int page_size; double soundio_os_get_time(void) { #if defined(SOUNDIO_OS_WINDOWS) @@ -558,6 +564,10 @@ static void internal_init(void) { } else { win32_panic("unable to initialize high precision timer: %s"); } + GetSystemInfo(&win32_system_info); + page_size = win32_system_info.dwAllocationGranularity; +#else + page_size = getpagesize(); #endif } @@ -589,3 +599,104 @@ void soundio_os_init(void) { assert_no_err(pthread_mutex_unlock(&init_mutex)); #endif } + +int soundio_os_page_size(void) { + return page_size; +} + +int soundio_os_create_mirrored_memory(size_t *capacity, char **out_address) { + *out_address = nullptr; + size_t requested_capacity = *capacity; + + size_t actual_capacity = (requested_capacity / page_size) * page_size; + if (actual_capacity < requested_capacity) + actual_capacity += page_size; + +#if defined(SOUNDIO_OS_WINDOWS) + BOOL ok; + HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, actual_capacity * 2, NULL); + if (!hMapFile) + return SoundIoErrorNoMem; + + for (;;) { + // find a free address space with the correct size + char *address = (char*)MapViewOfFile(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity * 2); + if (!address) { + ok = CloseHandle(hMapFile); + assert(ok); + return SoundIoErrorNoMem; + } + + // found a big enough address space. hopefully it will remain free + // while we map to it. if not, we'll try again. + ok = UnmapViewOfFile(address); + assert(ok); + + char *addr1 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_ALL_ACCESS, 0, 0, actual_capacity, address); + if (addr1 != address) { + DWORD err = GetLastError(); + if (err == ERROR_INVALID_ADDRESS) { + continue; + } else { + ok = CloseHandle(hMapFile); + assert(ok); + return SoundIoErrorNoMem; + } + } + + char *addr2 = (char*)MapViewOfFileEx(hMapFile, FILE_MAP_WRITE, 0, 0, + actual_capacity, address + actual_capacity); + if (addr2 != address + actual_capacity) { + ok = UnmapViewOfFile(addr1); + assert(ok); + + DWORD err = GetLastError(); + if (err == ERROR_INVALID_ADDRESS) { + continue; + } else { + ok = CloseHandle(hMapFile); + assert(ok); + return SoundIoErrorNoMem; + } + } + + *out_address = address; + break; + } +#else + char *address = (char*)mmap(NULL, actual_capacity * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + if (address == MAP_FAILED) + return SoundIoErrorNoMem; + + char *other_address = (char*)mmap(address, actual_capacity, PROT_READ|PROT_WRITE, + MAP_ANONYMOUS|MAP_FIXED|MAP_SHARED, -1, 0); + if (other_address != address) { + munmap(address, 2 * actual_capacity); + return SoundIoErrorNoMem; + } + + other_address = (char*)mmap(address + actual_capacity, actual_capacity, + PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_SHARED, -1, 0); + if (other_address != address + actual_capacity) { + munmap(address, 2 * actual_capacity); + return SoundIoErrorNoMem; + } + + *out_address = address; + +#endif + + *capacity = actual_capacity; + + return 0; +} + +void soundio_os_destroy_mirrored_memory(char *address, size_t capacity) { + if (!address) + return; +#if defined(SOUNDIO_OS_WINDOWS) +#else + int err = munmap(address, 2 * capacity); + assert(!err); +#endif +} diff --git a/src/os.hpp b/src/os.hpp index fd6b361..b86554a 100644 --- a/src/os.hpp +++ b/src/os.hpp @@ -9,6 +9,7 @@ #define SOUNDIO_OS_HPP #include +#include // safe to call from any thread(s) multiple times, but @@ -47,4 +48,14 @@ void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond, void soundio_os_cond_wait(struct SoundIoOsCond *cond, struct SoundIoOsMutex *locked_mutex); + +int soundio_os_page_size(void); + +// capacity is replaced with actual capacity which might be modified to be +// a multiple of the system page size +int soundio_os_create_mirrored_memory(size_t *capacity, char **out_address); +// capacity should be the actual capacity value that was given via +// soundio_os_create_mirrored_memory +void soundio_os_destroy_mirrored_memory(char *address, size_t capacity); + #endif diff --git a/src/ring_buffer.cpp b/src/ring_buffer.cpp index cda1591..55ba091 100644 --- a/src/ring_buffer.cpp +++ b/src/ring_buffer.cpp @@ -8,17 +8,9 @@ #include "ring_buffer.hpp" #include "soundio.hpp" #include "util.hpp" +#include "os.hpp" -#include #include -#include -#include -#include -#include - -#ifndef MAP_ANONYMOUS -#define MAP_ANONYMOUS MAP_ANON -#endif struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity) { SoundIoRingBuffer *rb = create(); @@ -83,34 +75,20 @@ void soundio_ring_buffer_clear(struct SoundIoRingBuffer *rb) { } int soundio_ring_buffer_init(struct SoundIoRingBuffer *rb, int requested_capacity) { - // round size up to the nearest power of two - int pow2_size = powf(2, ceilf(log2(requested_capacity))); - // at minimum must be page size - int page_size = getpagesize(); - rb->capacity = max(pow2_size, page_size); + int err; + size_t capacity = requested_capacity; + if ((err = soundio_os_create_mirrored_memory(&capacity, &rb->address))) { + soundio_ring_buffer_deinit(rb); + return err; + } + rb->capacity = capacity; rb->write_offset = 0; rb->read_offset = 0; - rb->address = (char*)mmap(NULL, rb->capacity * 2, PROT_NONE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); - if (rb->address == MAP_FAILED) - return SoundIoErrorNoMem; - - char *other_address = (char*)mmap(rb->address, rb->capacity, - PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_SHARED, -1, 0); - if (other_address != rb->address) - return SoundIoErrorNoMem; - - other_address = (char*)mmap(rb->address + rb->capacity, rb->capacity, - PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_FIXED|MAP_SHARED, -1, 0); - if (other_address != rb->address + rb->capacity) - return SoundIoErrorNoMem; - return 0; } void soundio_ring_buffer_deinit(struct SoundIoRingBuffer *rb) { - if (munmap(rb->address, 2 * rb->capacity)) - soundio_panic("munmap failed: %s", strerror(errno)); + soundio_os_destroy_mirrored_memory(rb->address, rb->capacity); } diff --git a/src/ring_buffer.hpp b/src/ring_buffer.hpp index 96d1953..801b8d8 100644 --- a/src/ring_buffer.hpp +++ b/src/ring_buffer.hpp @@ -12,7 +12,7 @@ struct SoundIoRingBuffer { char *address; - long capacity; + int capacity; atomic_long write_offset; atomic_long read_offset; }; diff --git a/test/unit_tests.cpp b/test/unit_tests.cpp index 9574735..3d2b951 100644 --- a/test/unit_tests.cpp +++ b/test/unit_tests.cpp @@ -51,14 +51,16 @@ static void test_ring_buffer_basic(void) { SoundIoRingBuffer *rb = soundio_ring_buffer_create(soundio, 10); assert(rb); - assert(soundio_ring_buffer_capacity(rb) == 4096); + int page_size = soundio_os_page_size(); + + assert(soundio_ring_buffer_capacity(rb) == page_size); char *write_ptr = soundio_ring_buffer_write_ptr(rb); int amt = sprintf(write_ptr, "hello") + 1; soundio_ring_buffer_advance_write_ptr(rb, amt); assert(soundio_ring_buffer_fill_count(rb) == amt); - assert(soundio_ring_buffer_free_count(rb) == 4096 - amt); + assert(soundio_ring_buffer_free_count(rb) == page_size - amt); char *read_ptr = soundio_ring_buffer_read_ptr(rb); @@ -69,8 +71,8 @@ static void test_ring_buffer_basic(void) { assert(soundio_ring_buffer_fill_count(rb) == 0); assert(soundio_ring_buffer_free_count(rb) == soundio_ring_buffer_capacity(rb)); - soundio_ring_buffer_advance_write_ptr(rb, 4094); - soundio_ring_buffer_advance_read_ptr(rb, 4094); + soundio_ring_buffer_advance_write_ptr(rb, page_size - 2); + soundio_ring_buffer_advance_read_ptr(rb, page_size - 2); amt = sprintf(soundio_ring_buffer_write_ptr(rb), "writing past the end") + 1; soundio_ring_buffer_advance_write_ptr(rb, amt);