Refcounted modules;

This allows them to be initialized/destroyed from multiple threads in
any order.  Previously, the first thread to require a module had to be
the last thread to use the module, otherwise it would be destroyed too
early.

There are still a few issues.  If the main thread doesn't require a
module, it won't pick up the conf.lua settings.  Also graphics isn't
handling the shader cache writing properly.  And I think this breaks the
headset-graphics refcounting.  But these will be fixed in future
commits.
This commit is contained in:
bjorn 2023-11-23 17:07:44 -08:00
parent 3a16e0c290
commit 08d6b2ad28
18 changed files with 76 additions and 86 deletions

View File

@ -346,13 +346,11 @@ int luaopen_lovr_audio(lua_State* L) {
}
lua_pop(L, 1);
if (lovrAudioInit(spatializer, sampleRate)) {
luax_atexit(L, lovrAudioDestroy);
if (start) {
lovrAudioSetDevice(AUDIO_PLAYBACK, NULL, 0, NULL, AUDIO_SHARED);
lovrAudioStart(AUDIO_PLAYBACK);
}
if (lovrAudioInit(spatializer, sampleRate) && start) {
lovrAudioSetDevice(AUDIO_PLAYBACK, NULL, 0, NULL, AUDIO_SHARED);
lovrAudioStart(AUDIO_PLAYBACK);
}
luax_atexit(L, lovrAudioDestroy);
return 1;
}

View File

@ -280,9 +280,7 @@ int luaopen_lovr_event(lua_State* L) {
lua_pushcfunction(L, nextEvent);
pollRef = luaL_ref(L, LUA_REGISTRYINDEX);
if (lovrEventInit()) {
luax_atexit(L, lovrEventDestroy);
}
lovrEventInit();
luax_atexit(L, lovrEventDestroy);
return 1;
}

View File

@ -561,6 +561,13 @@ static int libLoaderAllInOne(lua_State* L) {
extern const luaL_Reg lovrFile[];
int luaopen_lovr_filesystem(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrFilesystem);
luax_registertype(L, File);
luax_registerloader(L, luaLoader, 2);
luax_registerloader(L, libLoader, 3);
luax_registerloader(L, libLoaderAllInOne, 4);
const char* archive = NULL;
lua_getglobal(L, "arg");
@ -571,15 +578,7 @@ int luaopen_lovr_filesystem(lua_State* L) {
}
lua_pop(L, 1);
if (lovrFilesystemInit(archive)) {
luax_atexit(L, lovrFilesystemDestroy);
}
lua_newtable(L);
luax_register(L, lovrFilesystem);
luax_registertype(L, File);
luax_registerloader(L, luaLoader, 2);
luax_registerloader(L, libLoader, 3);
luax_registerloader(L, libLoaderAllInOne, 4);
lovrFilesystemInit(archive);
luax_atexit(L, lovrFilesystemDestroy);
return 1;
}

View File

@ -359,17 +359,14 @@ static int l_lovrGraphicsInitialize(lua_State* L) {
config.cacheData = luax_readfile(".lovrshadercache", &config.cacheSize);
}
if (lovrGraphicsInit(&config)) {
luax_atexit(L, lovrGraphicsDestroy);
lovrGraphicsInit(&config);
luax_atexit(L, lovrGraphicsDestroy);
// Finalizers run in the opposite order they were added, so this has to go last
if (shaderCache) {
luax_atexit(L, luax_writeshadercache);
}
if (shaderCache) { // Finalizers run in the opposite order they were added, so this has to go last
luax_atexit(L, luax_writeshadercache);
}
free(config.cacheData);
return 0;
}

View File

@ -387,9 +387,8 @@ int luaopen_lovr_math(lua_State* L) {
lua_pop(L, 1);
// Module
if (lovrMathInit()) {
luax_atexit(L, lovrMathDestroy);
}
lovrMathInit();
luax_atexit(L, lovrMathDestroy);
// Each Lua state gets its own thread-local Pool
pool = lovrPoolCreate();

View File

@ -178,8 +178,7 @@ int luaopen_lovr_physics(lua_State* L) {
luax_registertype(L, CylinderShape);
luax_registertype(L, MeshShape);
luax_registertype(L, TerrainShape);
if (lovrPhysicsInit()) {
luax_atexit(L, lovrPhysicsDestroy);
}
lovrPhysicsInit();
luax_atexit(L, lovrPhysicsDestroy);
return 1;
}

View File

@ -88,8 +88,7 @@ int luaopen_lovr_thread(lua_State* L) {
luax_register(L, lovrThreadModule);
luax_registertype(L, Thread);
luax_registertype(L, Channel);
if (lovrThreadModuleInit()) {
luax_atexit(L, lovrThreadModuleDestroy);
}
lovrThreadModuleInit();
luax_atexit(L, lovrThreadModuleDestroy);
return 1;
}

View File

@ -45,8 +45,7 @@ static const luaL_Reg lovrTimer[] = {
int luaopen_lovr_timer(lua_State* L) {
lua_newtable(L);
luax_register(L, lovrTimer);
if (lovrTimerInit()) {
luax_atexit(L, lovrTimerDestroy);
}
lovrTimerInit();
luax_atexit(L, lovrTimerDestroy);
return 1;
}

View File

@ -4,6 +4,7 @@
#include "core/maf.h"
#include "util.h"
#include "lib/miniaudio/miniaudio.h"
#include <stdatomic.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
@ -41,7 +42,7 @@ struct Source {
};
static struct {
bool initialized;
uint32_t ref;
ma_mutex lock;
ma_context context;
ma_device devices[2];
@ -193,7 +194,7 @@ static Spatializer* spatializers[] = {
// Entry
bool lovrAudioInit(const char* spatializer, uint32_t sampleRate) {
if (state.initialized) return false;
if (atomic_fetch_add(&state.ref, 1)) return false;
state.sampleRate = sampleRate;
@ -221,12 +222,11 @@ bool lovrAudioInit(const char* spatializer, uint32_t sampleRate) {
state.absorption[2] = .0182f;
quat_identity(state.orientation);
return state.initialized = true;
return true;
}
void lovrAudioDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
for (size_t i = 0; i < 2; i++) {
ma_device_uninit(&state.devices[i]);
free(state.deviceInfo[i]);

View File

@ -1,11 +1,12 @@
#include "event/event.h"
#include "thread/thread.h"
#include "util.h"
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
static struct {
bool initialized;
uint32_t ref;
arr_t(Event) events;
size_t head;
} state;
@ -20,13 +21,13 @@ void lovrVariantDestroy(Variant* variant) {
}
bool lovrEventInit(void) {
if (state.initialized) return false;
if (atomic_fetch_add(&state.ref, 1)) return false;
arr_init(&state.events, arr_alloc);
return state.initialized = true;
return true;
}
void lovrEventDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
for (size_t i = state.head; i < state.events.length; i++) {
Event* event = &state.events.data[i];
switch (event->type) {

View File

@ -3,6 +3,7 @@
#include "core/os.h"
#include "util.h"
#include "lib/miniz/miniz_tinfl.h"
#include <stdatomic.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
@ -67,7 +68,7 @@ struct File {
};
static struct {
bool initialized;
uint32_t ref;
Archive* archives;
size_t savePathLength;
char savePath[1024];
@ -132,8 +133,7 @@ static bool sanitize(const char* path, char* buffer, size_t* length) {
}
bool lovrFilesystemInit(const char* archive) {
if (state.initialized) return false;
state.initialized = true;
if (atomic_fetch_add(&state.ref, 1)) return false;
lovrFilesystemSetRequirePath("?.lua;?/init.lua");
@ -197,7 +197,7 @@ bool lovrFilesystemInit(const char* archive) {
}
void lovrFilesystemDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
Archive* archive = state.archives;
while (archive) {
Archive* next = archive->next;

View File

@ -14,6 +14,7 @@
#include "monkey.h"
#include "shaders.h"
#include <math.h>
#include <stdatomic.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@ -547,7 +548,7 @@ typedef struct {
} ScratchTexture;
static struct {
bool initialized;
uint32_t ref;
bool active;
bool presentable;
bool timingEnabled;
@ -618,7 +619,7 @@ static void onMessage(void* context, const char* message, bool severe);
// Entry
bool lovrGraphicsInit(GraphicsConfig* config) {
if (state.initialized) return false;
if (atomic_fetch_add(&state.ref, 1)) return false;
gpu_config gpu = {
.debug = config->debug,
@ -803,12 +804,11 @@ bool lovrGraphicsInit(GraphicsConfig* config) {
#ifdef LOVR_USE_GLSLANG
glslang_initialize_process();
#endif
state.initialized = true;
return true;
}
void lovrGraphicsDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
#ifndef LOVR_DISABLE_HEADSET
// If there's an active headset session it needs to be stopped so it can clean up its Pass and
// swapchain textures before gpu_destroy is called. This is really hacky and should be solved
@ -889,7 +889,7 @@ void lovrGraphicsDestroy(void) {
}
bool lovrGraphicsIsInitialized(void) {
return state.initialized;
return state.ref;
}
void lovrGraphicsGetDevice(GraphicsDevice* device) {

View File

@ -1,12 +1,12 @@
#include "headset/headset.h"
#include "util.h"
#include <stdatomic.h>
HeadsetInterface* lovrHeadsetInterface = NULL;
static bool initialized = false;
static uint32_t ref;
bool lovrHeadsetInit(HeadsetConfig* config) {
if (initialized) return false;
initialized = true;
if (atomic_fetch_add(&ref, 1)) return false;
for (size_t i = 0; i < config->driverCount; i++) {
HeadsetInterface* interface = NULL;
@ -35,12 +35,12 @@ bool lovrHeadsetInit(HeadsetConfig* config) {
}
void lovrHeadsetDestroy(void) {
if (!initialized) return;
initialized = false;
if (atomic_fetch_sub(&ref, 1) != 1) return;
if (lovrHeadsetInterface) {
lovrHeadsetInterface->destroy();
lovrHeadsetInterface = NULL;
}
ref = 0;
}
void lovrLayerDestroy(void* ref) {

View File

@ -3,12 +3,12 @@
#include "util.h"
#include "lib/noise/simplexnoise1234.h"
#include <math.h>
#include <string.h>
#include <stdatomic.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <math.h>
struct Curve {
uint32_t ref;
@ -31,20 +31,20 @@ struct RandomGenerator {
};
static struct {
bool initialized;
uint32_t ref;
RandomGenerator* generator;
} state;
bool lovrMathInit(void) {
if (state.initialized) return false;
if (atomic_fetch_add(&state.ref, 1)) return false;
state.generator = lovrRandomGeneratorCreate();
Seed seed = { .b64 = (uint64_t) time(0) };
lovrRandomGeneratorSetSeed(state.generator, seed);
return state.initialized = true;
return true;
}
void lovrMathDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
lovrRelease(state.generator, lovrRandomGeneratorDestroy);
memset(&state, 0, sizeof(state));
}

View File

@ -1,6 +1,7 @@
#include "physics.h"
#include "util.h"
#include <ode/ode.h>
#include <stdatomic.h>
#include <stdlib.h>
struct World {
@ -138,21 +139,20 @@ static void onInfoMessage(int num, const char* format, va_list args) {
lovrLog(LOG_INFO, "PHY", message);
}
static bool initialized = false;
static uint32_t ref;
bool lovrPhysicsInit(void) {
if (initialized) return false;
if (atomic_fetch_add(&ref, 1)) return false;
dInitODE();
dSetErrorHandler(onErrorMessage);
dSetDebugHandler(onDebugMessage);
dSetMessageHandler(onInfoMessage);
return initialized = true;
return true;
}
void lovrPhysicsDestroy(void) {
if (!initialized) return;
if (atomic_fetch_sub(&ref, 1) != 1) return;
dCloseODE();
initialized = false;
}
World* lovrWorldCreate(float xg, float yg, float zg, bool allowSleep, const char** tags, uint32_t tagCount) {

View File

@ -2,10 +2,11 @@
#include "event/event.h"
#include "core/os.h"
#include "util.h"
#include <stdatomic.h>
#include <string.h>
static struct {
bool initialized;
uint32_t ref;
bool keyRepeat;
bool prevKeyState[OS_KEY_COUNT];
bool keyState[OS_KEY_COUNT];
@ -84,20 +85,19 @@ static void onQuit(void) {
}
bool lovrSystemInit(void) {
if (state.initialized) return false;
if (atomic_fetch_add(&state.ref, 1)) return false;
os_on_key(onKey);
os_on_text(onText);
os_on_mouse_button(onMouseButton);
os_on_mouse_move(onMouseMove);
os_on_mousewheel_move(onWheelMove);
os_on_permission(onPermission);
state.initialized = true;
os_get_mouse_position(&state.mouseX, &state.mouseY);
return true;
}
void lovrSystemDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
os_on_key(NULL);
os_on_text(NULL);
os_on_permission(NULL);

View File

@ -4,6 +4,7 @@
#include "core/os.h"
#include "util.h"
#include <math.h>
#include <stdatomic.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
@ -32,20 +33,20 @@ struct Channel {
};
static struct {
bool initialized;
uint32_t ref;
mtx_t channelLock;
map_t channels;
} state;
bool lovrThreadModuleInit(void) {
if (state.initialized) return false;
if (atomic_fetch_add(&state.ref, 1)) return false;
mtx_init(&state.channelLock, mtx_plain);
map_init(&state.channels, 0);
return state.initialized = true;
return true;
}
void lovrThreadModuleDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1)) return;
for (size_t i = 0; i < state.channels.size; i++) {
if (state.channels.values[i] != MAP_NIL) {
lovrRelease((Channel*) (uintptr_t) state.channels.values[i], lovrChannelDestroy);
@ -53,7 +54,7 @@ void lovrThreadModuleDestroy(void) {
}
mtx_destroy(&state.channelLock);
map_free(&state.channels);
state.initialized = false;
memset(&state, 0, sizeof(state));
}
Channel* lovrThreadGetChannel(const char* name) {

View File

@ -1,9 +1,10 @@
#include "timer/timer.h"
#include "core/os.h"
#include <stdatomic.h>
#include <string.h>
static struct {
bool initialized;
uint32_t ref;
double epoch;
double lastTime;
double time;
@ -14,14 +15,13 @@ static struct {
} state;
bool lovrTimerInit(void) {
if (state.initialized) return false;
state.initialized = true;
if (atomic_fetch_add(&state.ref, 1)) return false;
state.epoch = os_get_time();
return true;
}
void lovrTimerDestroy(void) {
if (!state.initialized) return;
if (atomic_fetch_sub(&state.ref, 1) != 1) return;
memset(&state, 0, sizeof(state));
}