ring buffer works on windows

This commit is contained in:
Andrew Kelley 2015-07-06 16:02:20 -07:00
parent 2b0744816f
commit 5048d7aa57
6 changed files with 143 additions and 41 deletions

View file

@ -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")

View file

@ -50,8 +50,14 @@
#include <mmsystem.h>
#else
#include <pthread.h>
#include <unistd.h>
#include <sys/mman.h>
#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
}

View file

@ -9,6 +9,7 @@
#define SOUNDIO_OS_HPP
#include <stdbool.h>
#include <stddef.h>
// 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

View file

@ -8,17 +8,9 @@
#include "ring_buffer.hpp"
#include "soundio.hpp"
#include "util.hpp"
#include "os.hpp"
#include <sys/mman.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <string.h>
#include <errno.h>
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
struct SoundIoRingBuffer *soundio_ring_buffer_create(struct SoundIo *soundio, int requested_capacity) {
SoundIoRingBuffer *rb = create<SoundIoRingBuffer>();
@ -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);
}

View file

@ -12,7 +12,7 @@
struct SoundIoRingBuffer {
char *address;
long capacity;
int capacity;
atomic_long write_offset;
atomic_long read_offset;
};

View file

@ -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);