mirror of
https://github.com/Ryujinx/libsoundio.git
synced 2024-06-01 03:28:42 +02:00
748 lines
19 KiB
C
748 lines
19 KiB
C
/*
|
|
* Copyright (c) 2015 Andrew Kelley
|
|
*
|
|
* This file is part of libsoundio, which is MIT licensed.
|
|
* See http://opensource.org/licenses/MIT
|
|
*/
|
|
|
|
#if defined(__APPLE__)
|
|
#define _DARWIN_C_SOURCE
|
|
#undef _POSIX_C_SOURCE
|
|
#else
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include "os.h"
|
|
#include "soundio_internal.h"
|
|
#include "util.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <time.h>
|
|
#include <assert.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
|
|
#if defined(_WIN32)
|
|
#define SOUNDIO_OS_WINDOWS
|
|
|
|
#if !defined(NOMINMAX)
|
|
#define NOMINMAX
|
|
#endif
|
|
|
|
#if !defined(VC_EXTRALEAN)
|
|
#define VC_EXTRALEAN
|
|
#endif
|
|
|
|
#if !defined(WIN32_LEAN_AND_MEAN)
|
|
#define WIN32_LEAN_AND_MEAN
|
|
#endif
|
|
|
|
#if !defined(UNICODE)
|
|
#define UNICODE
|
|
#endif
|
|
|
|
// require Windows 7 or later
|
|
#if WINVER < 0x0601
|
|
#undef WINVER
|
|
#define WINVER 0x0601
|
|
#endif
|
|
#if _WIN32_WINNT < 0x0601
|
|
#undef _WIN32_WINNT
|
|
#define _WIN32_WINNT 0x0601
|
|
#endif
|
|
|
|
#include <windows.h>
|
|
#include <mmsystem.h>
|
|
#include <objbase.h>
|
|
|
|
#else
|
|
|
|
#include <pthread.h>
|
|
#include <unistd.h>
|
|
#include <sys/mman.h>
|
|
#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON)
|
|
#define MAP_ANONYMOUS MAP_ANON
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(__FreeBSD__) || defined(__MACH__)
|
|
#define SOUNDIO_OS_KQUEUE
|
|
#include <sys/types.h>
|
|
#include <sys/event.h>
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#if defined(__MACH__)
|
|
#include <mach/clock.h>
|
|
#include <mach/mach.h>
|
|
#endif
|
|
|
|
struct SoundIoOsThread {
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
HANDLE handle;
|
|
DWORD id;
|
|
#else
|
|
pthread_attr_t attr;
|
|
bool attr_init;
|
|
|
|
pthread_t id;
|
|
bool running;
|
|
#endif
|
|
void *arg;
|
|
void (*run)(void *arg);
|
|
};
|
|
|
|
struct SoundIoOsMutex {
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
CRITICAL_SECTION id;
|
|
#else
|
|
pthread_mutex_t id;
|
|
bool id_init;
|
|
#endif
|
|
};
|
|
|
|
#if defined(SOUNDIO_OS_KQUEUE)
|
|
static const uintptr_t notify_ident = 1;
|
|
struct SoundIoOsCond {
|
|
int kq_id;
|
|
};
|
|
#elif defined(SOUNDIO_OS_WINDOWS)
|
|
struct SoundIoOsCond {
|
|
CONDITION_VARIABLE id;
|
|
CRITICAL_SECTION default_cs_id;
|
|
};
|
|
#else
|
|
struct SoundIoOsCond {
|
|
pthread_cond_t id;
|
|
bool id_init;
|
|
|
|
pthread_condattr_t attr;
|
|
bool attr_init;
|
|
|
|
pthread_mutex_t default_mutex_id;
|
|
bool default_mutex_init;
|
|
};
|
|
#endif
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
static INIT_ONCE win32_init_once = INIT_ONCE_STATIC_INIT;
|
|
static double win32_time_resolution;
|
|
static SYSTEM_INFO win32_system_info;
|
|
#else
|
|
static bool initialized = false;
|
|
static pthread_mutex_t init_mutex = PTHREAD_MUTEX_INITIALIZER;
|
|
#if defined(__MACH__)
|
|
static clock_serv_t cclock;
|
|
#endif
|
|
#endif
|
|
|
|
static int page_size;
|
|
|
|
double soundio_os_get_time(void) {
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
unsigned __int64 time;
|
|
QueryPerformanceCounter((LARGE_INTEGER*) &time);
|
|
return time * win32_time_resolution;
|
|
#elif defined(__MACH__)
|
|
mach_timespec_t mts;
|
|
|
|
kern_return_t err = clock_get_time(cclock, &mts);
|
|
assert(!err);
|
|
|
|
double seconds = (double)mts.tv_sec;
|
|
seconds += ((double)mts.tv_nsec) / 1000000000.0;
|
|
|
|
return seconds;
|
|
#else
|
|
struct timespec tms;
|
|
clock_gettime(CLOCK_MONOTONIC, &tms);
|
|
double seconds = (double)tms.tv_sec;
|
|
seconds += ((double)tms.tv_nsec) / 1000000000.0;
|
|
return seconds;
|
|
#endif
|
|
}
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
static DWORD WINAPI run_win32_thread(LPVOID userdata) {
|
|
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
|
|
HRESULT err = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
|
|
assert(err == S_OK);
|
|
thread->run(thread->arg);
|
|
CoUninitialize();
|
|
return 0;
|
|
}
|
|
#else
|
|
static void assert_no_err(int err) {
|
|
assert(!err);
|
|
}
|
|
|
|
static void *run_pthread(void *userdata) {
|
|
struct SoundIoOsThread *thread = (struct SoundIoOsThread *)userdata;
|
|
thread->run(thread->arg);
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
int soundio_os_thread_create(
|
|
void (*run)(void *arg), void *arg,
|
|
void (*emit_rtprio_warning)(void),
|
|
struct SoundIoOsThread ** out_thread)
|
|
{
|
|
*out_thread = NULL;
|
|
|
|
struct SoundIoOsThread *thread = ALLOCATE(struct SoundIoOsThread, 1);
|
|
if (!thread) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorNoMem;
|
|
}
|
|
|
|
thread->run = run;
|
|
thread->arg = arg;
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
thread->handle = CreateThread(NULL, 0, run_win32_thread, thread, 0, &thread->id);
|
|
if (!thread->handle) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
if (emit_rtprio_warning) {
|
|
if (!SetThreadPriority(thread->handle, THREAD_PRIORITY_TIME_CRITICAL)) {
|
|
emit_rtprio_warning();
|
|
}
|
|
}
|
|
#else
|
|
int err;
|
|
if ((err = pthread_attr_init(&thread->attr))) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorNoMem;
|
|
}
|
|
thread->attr_init = true;
|
|
|
|
if (emit_rtprio_warning) {
|
|
int max_priority = sched_get_priority_max(SCHED_FIFO);
|
|
if (max_priority == -1) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
|
|
if ((err = pthread_attr_setschedpolicy(&thread->attr, SCHED_FIFO))) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
|
|
struct sched_param param;
|
|
param.sched_priority = max_priority;
|
|
if ((err = pthread_attr_setschedparam(&thread->attr, ¶m))) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
|
|
}
|
|
|
|
if ((err = pthread_create(&thread->id, &thread->attr, run_pthread, thread))) {
|
|
if (err == EPERM && emit_rtprio_warning) {
|
|
emit_rtprio_warning();
|
|
err = pthread_create(&thread->id, NULL, run_pthread, thread);
|
|
}
|
|
if (err) {
|
|
soundio_os_thread_destroy(thread);
|
|
return SoundIoErrorNoMem;
|
|
}
|
|
}
|
|
thread->running = true;
|
|
#endif
|
|
|
|
*out_thread = thread;
|
|
return 0;
|
|
}
|
|
|
|
void soundio_os_thread_destroy(struct SoundIoOsThread *thread) {
|
|
if (!thread)
|
|
return;
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
if (thread->handle) {
|
|
DWORD err = WaitForSingleObject(thread->handle, INFINITE);
|
|
assert(err != WAIT_FAILED);
|
|
BOOL ok = CloseHandle(thread->handle);
|
|
assert(ok);
|
|
}
|
|
#else
|
|
|
|
if (thread->running) {
|
|
assert_no_err(pthread_join(thread->id, NULL));
|
|
}
|
|
|
|
if (thread->attr_init) {
|
|
assert_no_err(pthread_attr_destroy(&thread->attr));
|
|
}
|
|
#endif
|
|
|
|
free(thread);
|
|
}
|
|
|
|
struct SoundIoOsMutex *soundio_os_mutex_create(void) {
|
|
struct SoundIoOsMutex *mutex = ALLOCATE(struct SoundIoOsMutex, 1);
|
|
if (!mutex) {
|
|
soundio_os_mutex_destroy(mutex);
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
InitializeCriticalSection(&mutex->id);
|
|
#else
|
|
int err;
|
|
if ((err = pthread_mutex_init(&mutex->id, NULL))) {
|
|
soundio_os_mutex_destroy(mutex);
|
|
return NULL;
|
|
}
|
|
mutex->id_init = true;
|
|
#endif
|
|
|
|
return mutex;
|
|
}
|
|
|
|
void soundio_os_mutex_destroy(struct SoundIoOsMutex *mutex) {
|
|
if (!mutex)
|
|
return;
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
DeleteCriticalSection(&mutex->id);
|
|
#else
|
|
if (mutex->id_init) {
|
|
assert_no_err(pthread_mutex_destroy(&mutex->id));
|
|
}
|
|
#endif
|
|
|
|
free(mutex);
|
|
}
|
|
|
|
void soundio_os_mutex_lock(struct SoundIoOsMutex *mutex) {
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
EnterCriticalSection(&mutex->id);
|
|
#else
|
|
assert_no_err(pthread_mutex_lock(&mutex->id));
|
|
#endif
|
|
}
|
|
|
|
void soundio_os_mutex_unlock(struct SoundIoOsMutex *mutex) {
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
LeaveCriticalSection(&mutex->id);
|
|
#else
|
|
assert_no_err(pthread_mutex_unlock(&mutex->id));
|
|
#endif
|
|
}
|
|
|
|
struct SoundIoOsCond * soundio_os_cond_create(void) {
|
|
struct SoundIoOsCond *cond = ALLOCATE(struct SoundIoOsCond, 1);
|
|
|
|
if (!cond) {
|
|
soundio_os_cond_destroy(cond);
|
|
return NULL;
|
|
}
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
InitializeConditionVariable(&cond->id);
|
|
InitializeCriticalSection(&cond->default_cs_id);
|
|
#elif defined(SOUNDIO_OS_KQUEUE)
|
|
cond->kq_id = kqueue();
|
|
if (cond->kq_id == -1)
|
|
return NULL;
|
|
#else
|
|
if (pthread_condattr_init(&cond->attr)) {
|
|
soundio_os_cond_destroy(cond);
|
|
return NULL;
|
|
}
|
|
cond->attr_init = true;
|
|
|
|
if (pthread_condattr_setclock(&cond->attr, CLOCK_MONOTONIC)) {
|
|
soundio_os_cond_destroy(cond);
|
|
return NULL;
|
|
}
|
|
|
|
if (pthread_cond_init(&cond->id, &cond->attr)) {
|
|
soundio_os_cond_destroy(cond);
|
|
return NULL;
|
|
}
|
|
cond->id_init = true;
|
|
|
|
if ((pthread_mutex_init(&cond->default_mutex_id, NULL))) {
|
|
soundio_os_cond_destroy(cond);
|
|
return NULL;
|
|
}
|
|
cond->default_mutex_init = true;
|
|
#endif
|
|
|
|
return cond;
|
|
}
|
|
|
|
void soundio_os_cond_destroy(struct SoundIoOsCond *cond) {
|
|
if (!cond)
|
|
return;
|
|
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
DeleteCriticalSection(&cond->default_cs_id);
|
|
#elif defined(SOUNDIO_OS_KQUEUE)
|
|
close(cond->kq_id);
|
|
#else
|
|
if (cond->id_init) {
|
|
assert_no_err(pthread_cond_destroy(&cond->id));
|
|
}
|
|
|
|
if (cond->attr_init) {
|
|
assert_no_err(pthread_condattr_destroy(&cond->attr));
|
|
}
|
|
if (cond->default_mutex_init) {
|
|
assert_no_err(pthread_mutex_destroy(&cond->default_mutex_id));
|
|
}
|
|
#endif
|
|
|
|
free(cond);
|
|
}
|
|
|
|
void soundio_os_cond_signal(struct SoundIoOsCond *cond,
|
|
struct SoundIoOsMutex *locked_mutex)
|
|
{
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
if (locked_mutex) {
|
|
WakeConditionVariable(&cond->id);
|
|
} else {
|
|
EnterCriticalSection(&cond->default_cs_id);
|
|
WakeConditionVariable(&cond->id);
|
|
LeaveCriticalSection(&cond->default_cs_id);
|
|
}
|
|
#elif defined(SOUNDIO_OS_KQUEUE)
|
|
struct kevent kev;
|
|
struct timespec timeout = { 0, 0 };
|
|
|
|
memset(&kev, 0, sizeof(kev));
|
|
kev.ident = notify_ident;
|
|
kev.filter = EVFILT_USER;
|
|
kev.fflags = NOTE_TRIGGER;
|
|
|
|
if (kevent(cond->kq_id, &kev, 1, NULL, 0, &timeout) == -1) {
|
|
if (errno == EINTR)
|
|
return;
|
|
if (errno == ENOENT)
|
|
return;
|
|
assert(0); // kevent signal error
|
|
}
|
|
#else
|
|
if (locked_mutex) {
|
|
assert_no_err(pthread_cond_signal(&cond->id));
|
|
} else {
|
|
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
|
|
assert_no_err(pthread_cond_signal(&cond->id));
|
|
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void soundio_os_cond_timed_wait(struct SoundIoOsCond *cond,
|
|
struct SoundIoOsMutex *locked_mutex, double seconds)
|
|
{
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
CRITICAL_SECTION *target_cs;
|
|
if (locked_mutex) {
|
|
target_cs = &locked_mutex->id;
|
|
} else {
|
|
target_cs = &cond->default_cs_id;
|
|
EnterCriticalSection(&cond->default_cs_id);
|
|
}
|
|
DWORD ms = seconds * 1000.0;
|
|
SleepConditionVariableCS(&cond->id, target_cs, ms);
|
|
if (!locked_mutex)
|
|
LeaveCriticalSection(&cond->default_cs_id);
|
|
#elif defined(SOUNDIO_OS_KQUEUE)
|
|
struct kevent kev;
|
|
struct kevent out_kev;
|
|
|
|
if (locked_mutex)
|
|
assert_no_err(pthread_mutex_unlock(&locked_mutex->id));
|
|
|
|
memset(&kev, 0, sizeof(kev));
|
|
kev.ident = notify_ident;
|
|
kev.filter = EVFILT_USER;
|
|
kev.flags = EV_ADD | EV_CLEAR;
|
|
|
|
// this time is relative
|
|
struct timespec timeout;
|
|
timeout.tv_nsec = (seconds * 1000000000L);
|
|
timeout.tv_sec = timeout.tv_nsec / 1000000000L;
|
|
timeout.tv_nsec = timeout.tv_nsec % 1000000000L;
|
|
|
|
if (kevent(cond->kq_id, &kev, 1, &out_kev, 1, &timeout) == -1) {
|
|
if (errno == EINTR)
|
|
return;
|
|
assert(0); // kevent wait error
|
|
}
|
|
if (locked_mutex)
|
|
assert_no_err(pthread_mutex_lock(&locked_mutex->id));
|
|
#else
|
|
pthread_mutex_t *target_mutex;
|
|
if (locked_mutex) {
|
|
target_mutex = &locked_mutex->id;
|
|
} else {
|
|
target_mutex = &cond->default_mutex_id;
|
|
assert_no_err(pthread_mutex_lock(target_mutex));
|
|
}
|
|
// this time is absolute
|
|
struct timespec tms;
|
|
clock_gettime(CLOCK_MONOTONIC, &tms);
|
|
tms.tv_nsec += (seconds * 1000000000L);
|
|
tms.tv_sec += tms.tv_nsec / 1000000000L;
|
|
tms.tv_nsec = tms.tv_nsec % 1000000000L;
|
|
int err;
|
|
if ((err = pthread_cond_timedwait(&cond->id, target_mutex, &tms))) {
|
|
assert(err != EPERM);
|
|
assert(err != EINVAL);
|
|
}
|
|
if (!locked_mutex)
|
|
assert_no_err(pthread_mutex_unlock(target_mutex));
|
|
#endif
|
|
}
|
|
|
|
void soundio_os_cond_wait(struct SoundIoOsCond *cond,
|
|
struct SoundIoOsMutex *locked_mutex)
|
|
{
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
CRITICAL_SECTION *target_cs;
|
|
if (locked_mutex) {
|
|
target_cs = &locked_mutex->id;
|
|
} else {
|
|
target_cs = &cond->default_cs_id;
|
|
EnterCriticalSection(&cond->default_cs_id);
|
|
}
|
|
SleepConditionVariableCS(&cond->id, target_cs, INFINITE);
|
|
if (!locked_mutex)
|
|
LeaveCriticalSection(&cond->default_cs_id);
|
|
#elif defined(SOUNDIO_OS_KQUEUE)
|
|
struct kevent kev;
|
|
struct kevent out_kev;
|
|
|
|
if (locked_mutex)
|
|
assert_no_err(pthread_mutex_unlock(&locked_mutex->id));
|
|
|
|
memset(&kev, 0, sizeof(kev));
|
|
kev.ident = notify_ident;
|
|
kev.filter = EVFILT_USER;
|
|
kev.flags = EV_ADD | EV_CLEAR;
|
|
|
|
if (kevent(cond->kq_id, &kev, 1, &out_kev, 1, NULL) == -1) {
|
|
if (errno == EINTR)
|
|
return;
|
|
assert(0); // kevent wait error
|
|
}
|
|
if (locked_mutex)
|
|
assert_no_err(pthread_mutex_lock(&locked_mutex->id));
|
|
#else
|
|
pthread_mutex_t *target_mutex;
|
|
if (locked_mutex) {
|
|
target_mutex = &locked_mutex->id;
|
|
} else {
|
|
target_mutex = &cond->default_mutex_id;
|
|
assert_no_err(pthread_mutex_lock(&cond->default_mutex_id));
|
|
}
|
|
int err;
|
|
if ((err = pthread_cond_wait(&cond->id, target_mutex))) {
|
|
assert(err != EPERM);
|
|
assert(err != EINVAL);
|
|
}
|
|
if (!locked_mutex)
|
|
assert_no_err(pthread_mutex_unlock(&cond->default_mutex_id));
|
|
#endif
|
|
}
|
|
|
|
static int internal_init(void) {
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
unsigned __int64 frequency;
|
|
if (QueryPerformanceFrequency((LARGE_INTEGER*) &frequency)) {
|
|
win32_time_resolution = 1.0 / (double) frequency;
|
|
} else {
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
GetSystemInfo(&win32_system_info);
|
|
page_size = win32_system_info.dwAllocationGranularity;
|
|
#else
|
|
page_size = sysconf(_SC_PAGESIZE);
|
|
#if defined(__MACH__)
|
|
host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
|
|
#endif
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int soundio_os_init(void) {
|
|
int err;
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
PVOID lpContext;
|
|
BOOL pending;
|
|
|
|
if (!InitOnceBeginInitialize(&win32_init_once, INIT_ONCE_ASYNC, &pending, &lpContext))
|
|
return SoundIoErrorSystemResources;
|
|
|
|
if (!pending)
|
|
return 0;
|
|
|
|
if ((err = internal_init()))
|
|
return err;
|
|
|
|
if (!InitOnceComplete(&win32_init_once, INIT_ONCE_ASYNC, NULL))
|
|
return SoundIoErrorSystemResources;
|
|
#else
|
|
assert_no_err(pthread_mutex_lock(&init_mutex));
|
|
if (initialized) {
|
|
assert_no_err(pthread_mutex_unlock(&init_mutex));
|
|
return 0;
|
|
}
|
|
initialized = true;
|
|
if ((err = internal_init()))
|
|
return err;
|
|
assert_no_err(pthread_mutex_unlock(&init_mutex));
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
int soundio_os_page_size(void) {
|
|
return page_size;
|
|
}
|
|
|
|
static inline size_t ceil_dbl_to_size_t(double x) {
|
|
const double truncation = (size_t)x;
|
|
return truncation + (truncation < x);
|
|
}
|
|
|
|
int soundio_os_init_mirrored_memory(struct SoundIoOsMirroredMemory *mem, size_t requested_capacity) {
|
|
size_t actual_capacity = ceil_dbl_to_size_t(requested_capacity / (double)page_size) * 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;
|
|
}
|
|
}
|
|
|
|
mem->priv = hMapFile;
|
|
mem->address = address;
|
|
break;
|
|
}
|
|
#else
|
|
char shm_path[] = "/dev/shm/soundio-XXXXXX";
|
|
char tmp_path[] = "/tmp/soundio-XXXXXX";
|
|
char *chosen_path;
|
|
|
|
int fd = mkstemp(shm_path);
|
|
if (fd < 0) {
|
|
fd = mkstemp(tmp_path);
|
|
if (fd < 0) {
|
|
return SoundIoErrorSystemResources;
|
|
} else {
|
|
chosen_path = tmp_path;
|
|
}
|
|
} else {
|
|
chosen_path = shm_path;
|
|
}
|
|
|
|
if (unlink(chosen_path)) {
|
|
close(fd);
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
|
|
if (ftruncate(fd, actual_capacity)) {
|
|
close(fd);
|
|
return SoundIoErrorSystemResources;
|
|
}
|
|
|
|
char *address = (char*)mmap(NULL, actual_capacity * 2, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
|
|
if (address == MAP_FAILED) {
|
|
close(fd);
|
|
return SoundIoErrorNoMem;
|
|
}
|
|
|
|
char *other_address = (char*)mmap(address, actual_capacity, PROT_READ|PROT_WRITE,
|
|
MAP_FIXED|MAP_SHARED, fd, 0);
|
|
if (other_address != address) {
|
|
munmap(address, 2 * actual_capacity);
|
|
close(fd);
|
|
return SoundIoErrorNoMem;
|
|
}
|
|
|
|
other_address = (char*)mmap(address + actual_capacity, actual_capacity,
|
|
PROT_READ|PROT_WRITE, MAP_FIXED|MAP_SHARED, fd, 0);
|
|
if (other_address != address + actual_capacity) {
|
|
munmap(address, 2 * actual_capacity);
|
|
close(fd);
|
|
return SoundIoErrorNoMem;
|
|
}
|
|
|
|
mem->address = address;
|
|
|
|
if (close(fd))
|
|
return SoundIoErrorSystemResources;
|
|
#endif
|
|
|
|
mem->capacity = actual_capacity;
|
|
return 0;
|
|
}
|
|
|
|
void soundio_os_deinit_mirrored_memory(struct SoundIoOsMirroredMemory *mem) {
|
|
if (!mem->address)
|
|
return;
|
|
#if defined(SOUNDIO_OS_WINDOWS)
|
|
BOOL ok;
|
|
ok = UnmapViewOfFile(mem->address);
|
|
assert(ok);
|
|
ok = UnmapViewOfFile(mem->address + mem->capacity);
|
|
assert(ok);
|
|
ok = CloseHandle((HANDLE)mem->priv);
|
|
assert(ok);
|
|
#else
|
|
int err = munmap(mem->address, 2 * mem->capacity);
|
|
assert(!err);
|
|
#endif
|
|
mem->address = NULL;
|
|
}
|