Untested asset cache
- Added read-write lock implementation
This commit is contained in:
parent
bf2129f92b
commit
275f3b65f8
@ -67,6 +67,7 @@ runtime_lib = library('vyrt',
|
|||||||
|
|
||||||
'src/runtime/aio.c',
|
'src/runtime/aio.c',
|
||||||
'src/runtime/app.c',
|
'src/runtime/app.c',
|
||||||
|
'src/runtime/asset_cache.c',
|
||||||
'src/runtime/asset_dependencies.c',
|
'src/runtime/asset_dependencies.c',
|
||||||
'src/runtime/asset_loading.c',
|
'src/runtime/asset_loading.c',
|
||||||
'src/runtime/buffer_manager.c',
|
'src/runtime/buffer_manager.c',
|
||||||
@ -80,11 +81,13 @@ runtime_lib = library('vyrt',
|
|||||||
'src/runtime/text.c',
|
'src/runtime/text.c',
|
||||||
'src/runtime/threading_cond.c',
|
'src/runtime/threading_cond.c',
|
||||||
'src/runtime/threading_mutex.c',
|
'src/runtime/threading_mutex.c',
|
||||||
|
'src/runtime/threading_rwlock.c',
|
||||||
'src/runtime/threading_thread.c',
|
'src/runtime/threading_thread.c',
|
||||||
'src/runtime/uidtab.c',
|
'src/runtime/uidtab.c',
|
||||||
|
|
||||||
# Contrib Sources
|
# Contrib Sources
|
||||||
'contrib/xxhash/xxhash.c',
|
'contrib/xxhash/xxhash.c',
|
||||||
|
'contrib/lz4/lz4.c',
|
||||||
dependencies : [thread_dep, m_dep, windowing_dep],
|
dependencies : [thread_dep, m_dep, windowing_dep],
|
||||||
include_directories : incdir,
|
include_directories : incdir,
|
||||||
c_pch : 'pch/rt_pch.h')
|
c_pch : 'pch/rt_pch.h')
|
||||||
|
357
src/runtime/asset_cache.c
Normal file
357
src/runtime/asset_cache.c
Normal file
@ -0,0 +1,357 @@
|
|||||||
|
#include "assets.h"
|
||||||
|
#include "asset_dependencies.h"
|
||||||
|
#include "aio.h"
|
||||||
|
#include "buffer_manager.h"
|
||||||
|
#include "config.h"
|
||||||
|
#include "threading.h"
|
||||||
|
#include "uidtab.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
VY_CVAR_I(rt_AssetCacheSize, "Number of asset cache entries. Default: 1024.", 1024);
|
||||||
|
|
||||||
|
/* asset_loading.c */
|
||||||
|
extern vy_result DecompressAsset(void *compressed_buffer,
|
||||||
|
size_t compressed_buffer_size,
|
||||||
|
void **p_decompressed,
|
||||||
|
size_t *p_decompressed_size);
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
CACHE_ENTRY_STATE_FREE,
|
||||||
|
CACHE_ENTRY_STATE_LOADING,
|
||||||
|
CACHE_ENTRY_STATE_LOADED,
|
||||||
|
} vy_asset_cache_entry_state;
|
||||||
|
|
||||||
|
typedef struct vy_asset_cache_entry_s {
|
||||||
|
vy_asset_cache_entry_state state;
|
||||||
|
vy_aio_handle load;
|
||||||
|
void *buffer;
|
||||||
|
size_t size;
|
||||||
|
int refcount;
|
||||||
|
|
||||||
|
/* Reclaim list */
|
||||||
|
struct vy_asset_cache_entry_s *prev_reclaim;
|
||||||
|
struct vy_asset_cache_entry_s *next_reclaim;
|
||||||
|
} vy_asset_cache_entry;
|
||||||
|
|
||||||
|
static vy_uid *_uids;
|
||||||
|
static vy_asset_cache_entry *_entries;
|
||||||
|
static vy_asset_cache_entry *_first_reclaim;
|
||||||
|
static vy_asset_cache_entry *_last_reclaim;
|
||||||
|
|
||||||
|
/* Locked as writer when modifiying entries, as reader when searching */
|
||||||
|
static vy_rwlock _lock;
|
||||||
|
|
||||||
|
vy_result InitAssetCache(void) {
|
||||||
|
_entries = calloc((size_t)rt_AssetCacheSize.i, sizeof(vy_asset_cache_entry));
|
||||||
|
if (!_entries) {
|
||||||
|
return VY_BUFFER_ALLOC_FAILED;
|
||||||
|
}
|
||||||
|
_uids = calloc((size_t)rt_AssetCacheSize.i, sizeof(vy_uid));
|
||||||
|
if (!_uids) {
|
||||||
|
free(_entries);
|
||||||
|
return VY_BUFFER_ALLOC_FAILED;
|
||||||
|
}
|
||||||
|
vy_create_rwlock_result lock_res = vyCreateRWLock();
|
||||||
|
if (!lock_res.ok) {
|
||||||
|
free(_entries);
|
||||||
|
free(_uids);
|
||||||
|
return VY_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
_lock = lock_res.lock;
|
||||||
|
return VY_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ShutdownAssetCache(void) {
|
||||||
|
free(_entries);
|
||||||
|
free(_uids);
|
||||||
|
vyDestroyRWLock(&_lock);
|
||||||
|
_first_reclaim = NULL;
|
||||||
|
_last_reclaim = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ReleaseEntry(vy_asset_cache_entry *entry) {
|
||||||
|
if (entry->load != VY_AIO_INVALID_HANDLE) {
|
||||||
|
vyWaitForAIOCompletion(entry->load);
|
||||||
|
vyReleaseAIO(entry->load);
|
||||||
|
entry->load = VY_AIO_INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
vyReleaseBuffer(entry->buffer, entry->size);
|
||||||
|
entry->buffer = NULL;
|
||||||
|
entry->size = 0;
|
||||||
|
entry->next_reclaim = NULL;
|
||||||
|
entry->prev_reclaim = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GarbageCollect(void) {
|
||||||
|
vyLockWrite(&_lock);
|
||||||
|
vy_asset_cache_entry *entry = _first_reclaim;
|
||||||
|
while (entry) {
|
||||||
|
assert(entry->refcount == 0);
|
||||||
|
vy_asset_cache_entry *next = entry->next_reclaim;
|
||||||
|
if (entry->state == CACHE_ENTRY_STATE_LOADED) {
|
||||||
|
ReleaseEntry(entry);
|
||||||
|
_first_reclaim = next;
|
||||||
|
}
|
||||||
|
entry = next;
|
||||||
|
}
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
static vy_asset_cache_entry *GetEntry(vy_uid uid) {
|
||||||
|
/* Hash lookup */
|
||||||
|
unsigned int mod = (unsigned int)rt_AssetCacheSize.i - 1;
|
||||||
|
for (unsigned int i = 0; i < (unsigned int)rt_AssetCacheSize.i; ++i) {
|
||||||
|
unsigned int slot = (uid + i) & mod;
|
||||||
|
if (_uids[slot] == uid) {
|
||||||
|
return &_entries[slot];
|
||||||
|
} else if (_uids[slot] == VY_INVALID_UID) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool IsAssetLoaded(vy_uid uid) {
|
||||||
|
const vy_asset_cache_entry *entry = GetEntry(uid);
|
||||||
|
if (entry)
|
||||||
|
return entry->state == CACHE_ENTRY_STATE_LOADED ||
|
||||||
|
entry->state == CACHE_ENTRY_STATE_LOADING;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int InsertEntry(vy_uid uid) {
|
||||||
|
unsigned int mod = (unsigned int)rt_AssetCacheSize.i - 1;
|
||||||
|
for (unsigned int i = 0; i < (unsigned int)rt_AssetCacheSize.i; ++i) {
|
||||||
|
unsigned int slot = (uid + i) & mod;
|
||||||
|
if (_uids[slot] == 0) {
|
||||||
|
return (int)slot;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
static vy_result InsertAndLoadAssets(const vy_uid *uids, size_t count) {
|
||||||
|
vy_load_batch batch = {.num_loads = 0};
|
||||||
|
|
||||||
|
vy_result res = VY_SUCCESS;
|
||||||
|
|
||||||
|
count = (count < VY_LOAD_BATCH_MAX_SIZE) ? count : VY_LOAD_BATCH_MAX_SIZE;
|
||||||
|
vy_asset_cache_entry *load_entries[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
vyLockRead(&_lock);
|
||||||
|
bool needs_load = !IsAssetLoaded(uids[i]);
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
|
||||||
|
if (!needs_load)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
vyLockWrite(&_lock);
|
||||||
|
/* It's possible that another thread loaded the asset in the meantime */
|
||||||
|
if (!IsAssetLoaded(uids[i])) {
|
||||||
|
const vy_uid_data *data = vyGetUIDData(uids[i]);
|
||||||
|
if (!data) {
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
vyLog("ASSET_CACHE", "Failed to get uid data for uid %u", uids[i]);
|
||||||
|
res = VY_UNKNOWN_ASSET;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
void *compressed_data = vyAllocBuffer(data->size);
|
||||||
|
if (!compressed_data) {
|
||||||
|
/* Try again after garbage collection */
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
GarbageCollect();
|
||||||
|
compressed_data = vyAllocBuffer(data->size);
|
||||||
|
if (!compressed_data) {
|
||||||
|
vyLog("ASSET_CACHE",
|
||||||
|
"Failed to allocate intermediate buffer for uid %u",
|
||||||
|
uids[i]);
|
||||||
|
res = VY_BUFFER_ALLOC_FAILED;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
vyLockWrite(&_lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
int slot = InsertEntry(uids[i]);
|
||||||
|
if (slot == -1) {
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
vyLog("ASSET_CACHE", "Failed to insert new entry for uid %u", uids[i]);
|
||||||
|
res = VY_ASSET_CACHE_FULL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vy_asset_cache_entry *entry = &_entries[slot];
|
||||||
|
load_entries[batch.num_loads] = entry;
|
||||||
|
|
||||||
|
/* We set the refcount to 0, but don't insert the entry
|
||||||
|
* into the reclaim list, to ensure that its buffer does not get freed
|
||||||
|
* while the load still executes. Setting the refcount to 0 ensures
|
||||||
|
* that the count is correct, once the asset is accessed the first time. */
|
||||||
|
entry->state = CACHE_ENTRY_STATE_LOADING;
|
||||||
|
entry->refcount = 0;
|
||||||
|
entry->buffer = compressed_data;
|
||||||
|
entry->size = data->size;
|
||||||
|
entry->next_reclaim = NULL;
|
||||||
|
entry->prev_reclaim = NULL;
|
||||||
|
entry->load = VY_AIO_INVALID_HANDLE;
|
||||||
|
|
||||||
|
batch.loads[batch.num_loads].file = data->pkg_file;
|
||||||
|
batch.loads[batch.num_loads].num_bytes = data->size;
|
||||||
|
batch.loads[batch.num_loads].offset = data->offset;
|
||||||
|
batch.loads[batch.num_loads].dest = compressed_data;
|
||||||
|
++batch.num_loads;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
|
||||||
|
/* Dispatch the load */
|
||||||
|
vy_aio_handle handles[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
|
if ((res = vySubmitLoadBatch(&batch, handles)) != VY_SUCCESS) {
|
||||||
|
vyLog("ASSET_CACHE", "Failed to submit %u asset loads.", batch.num_loads);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set the aio handles of the inserted entries */
|
||||||
|
vyLockWrite(&_lock);
|
||||||
|
for (unsigned int i = 0; i < batch.num_loads; ++i) {
|
||||||
|
load_entries[batch.num_loads]->load = handles[i];
|
||||||
|
}
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool DecompressEntry(vy_uid uid, vy_asset_cache_entry *entry) {
|
||||||
|
vyReleaseAIO(entry->load);
|
||||||
|
entry->load = VY_AIO_INVALID_HANDLE;
|
||||||
|
|
||||||
|
void *decompressed_buffer;
|
||||||
|
size_t decompressed_size;
|
||||||
|
vy_result dec_res =
|
||||||
|
DecompressAsset(entry->buffer, entry->size, &decompressed_buffer, &decompressed_size);
|
||||||
|
if (dec_res == VY_SUCCESS) {
|
||||||
|
vyReleaseBuffer(entry->buffer, entry->size);
|
||||||
|
entry->buffer = decompressed_buffer;
|
||||||
|
entry->size = decompressed_size;
|
||||||
|
entry->state = CACHE_ENTRY_STATE_LOADED;
|
||||||
|
return true;
|
||||||
|
} else if (dec_res == VY_BUFFER_ALLOC_FAILED) {
|
||||||
|
GarbageCollect();
|
||||||
|
/* Try again */
|
||||||
|
if (DecompressAsset(entry->buffer, entry->size, &decompressed_buffer, &decompressed_size) ==
|
||||||
|
VY_SUCCESS) {
|
||||||
|
vyReleaseBuffer(entry->buffer, entry->size);
|
||||||
|
entry->buffer = decompressed_buffer;
|
||||||
|
entry->size = decompressed_size;
|
||||||
|
entry->state = CACHE_ENTRY_STATE_LOADED;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/* Don't do anything yet. We might be able to to do this later, once some
|
||||||
|
* buffers become free. */
|
||||||
|
vyLog("ASSET_CACHE", "Failed to decompress asset %u", uid);
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
vyLog("ASSET_CACHE", "Failed to decompress asset %u", uid);
|
||||||
|
ReleaseEntry(entry);
|
||||||
|
|
||||||
|
ptrdiff_t idx = entry - _entries;
|
||||||
|
_uids[idx] = VY_INVALID_UID;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CheckCompletedLoads(const vy_uid *uids, size_t count) {
|
||||||
|
for (size_t i = 0; i < count; ++i) {
|
||||||
|
vyLockRead(&_lock);
|
||||||
|
volatile vy_asset_cache_entry *entry = (volatile vy_asset_cache_entry *)GetEntry(uids[i]);
|
||||||
|
if (!entry) {
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
vyLog("ASSET_CACHE", "Passed unknown uid %u to CheckCompletedLoads()", uids[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry->state != CACHE_ENTRY_STATE_LOADING) {
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
bool load_finished = vyGetAIOState(entry->load) == VY_AIO_STATE_FINISHED;
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
|
||||||
|
if (load_finished) {
|
||||||
|
vyLockWrite(&_lock);
|
||||||
|
/* Ensure that no-one else handled this */
|
||||||
|
if (entry->state == CACHE_ENTRY_STATE_LOADING) {
|
||||||
|
DecompressEntry(uids[i], (vy_asset_cache_entry *)entry);
|
||||||
|
}
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT vy_get_asset_result vyGetAsset(vy_uid uid) {
|
||||||
|
vy_get_asset_result result = {
|
||||||
|
.result = VY_SUCCESS,
|
||||||
|
};
|
||||||
|
|
||||||
|
vyLockRead(&_lock);
|
||||||
|
bool needs_load = !IsAssetLoaded(uid);
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
|
||||||
|
if (needs_load) {
|
||||||
|
vy_uid load_uids[VY_LOAD_BATCH_MAX_SIZE];
|
||||||
|
size_t load_count = 1;
|
||||||
|
load_uids[0] = uid;
|
||||||
|
|
||||||
|
vy_asset_dependency_list deps = vyGetAssetDependencies(uid);
|
||||||
|
for (size_t i = 0; i < deps.count && i < VY_LOAD_BATCH_MAX_SIZE - 1; ++i) {
|
||||||
|
load_uids[i + 1] = deps.dependencies[i];
|
||||||
|
++load_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
result.result = InsertAndLoadAssets(load_uids, load_count);
|
||||||
|
if (result.result == VY_SUCCESS) {
|
||||||
|
CheckCompletedLoads(load_uids, load_count);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vyLockRead(&_lock);
|
||||||
|
volatile vy_asset_cache_entry *entry = GetEntry(uid);
|
||||||
|
if (entry) {
|
||||||
|
if (entry->state == CACHE_ENTRY_STATE_LOADED) {
|
||||||
|
++entry->refcount;
|
||||||
|
result.data = entry->buffer;
|
||||||
|
result.size = entry->size;
|
||||||
|
} else if (entry->state == CACHE_ENTRY_STATE_LOADING) {
|
||||||
|
/* Promote to write lock */
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
vyLockWrite(&_lock);
|
||||||
|
if (entry->state == CACHE_ENTRY_STATE_LOADING) {
|
||||||
|
assert(entry->load != VY_AIO_INVALID_HANDLE);
|
||||||
|
++entry->refcount;
|
||||||
|
if (vyWaitForAIOCompletion(entry->load) == VY_AIO_STATE_FINISHED) {
|
||||||
|
if (DecompressEntry(uid, (vy_asset_cache_entry *)entry)) {
|
||||||
|
result.data = entry->buffer;
|
||||||
|
result.size = entry->size;
|
||||||
|
} else {
|
||||||
|
result.result = VY_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ReleaseEntry((vy_asset_cache_entry *)entry);
|
||||||
|
vyLog("ASSET_CACHE", "Failed to load asset %u", uid);
|
||||||
|
result.result = VY_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vyUnlockWrite(&_lock);
|
||||||
|
|
||||||
|
/* To match the unlock below */
|
||||||
|
vyLockRead(&_lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vyUnlockRead(&_lock);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
@ -8,6 +8,40 @@
|
|||||||
|
|
||||||
#include "lz4/lz4.h"
|
#include "lz4/lz4.h"
|
||||||
|
|
||||||
|
vy_result DecompressAsset(void *compressed_buffer,
|
||||||
|
size_t compressed_buffer_size,
|
||||||
|
void **p_decompressed,
|
||||||
|
size_t *p_decompressed_size) {
|
||||||
|
|
||||||
|
const vy_package_asset_header *header = compressed_buffer;
|
||||||
|
|
||||||
|
size_t compressed_size = (compressed_buffer_size) - sizeof(*header);
|
||||||
|
XXH64_hash_t calculated_hash = XXH3_64bits((header + 1), compressed_size);
|
||||||
|
XXH64_hash_t file_hash = XXH64_hashFromCanonical(&header->checksum);
|
||||||
|
if (calculated_hash != file_hash) {
|
||||||
|
vyLog("core", "Checksum mismatch for asset");
|
||||||
|
return VY_LOAD_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t size = (size_t)header->decompressed_size;
|
||||||
|
void *decompressed_buffer = vyAllocBuffer(size);
|
||||||
|
if (!decompressed_buffer) {
|
||||||
|
return VY_BUFFER_ALLOC_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LZ4_decompress_safe((const char *)(header + 1),
|
||||||
|
(char *)decompressed_buffer,
|
||||||
|
(int)compressed_size,
|
||||||
|
(int)size) < 0) {
|
||||||
|
return VY_UNKNOWN_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
*p_decompressed = decompressed_buffer;
|
||||||
|
*p_decompressed_size = size;
|
||||||
|
return VY_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VY_DLLEXPORT vy_result vyLoadAssetDirect(vy_uid uid, void **p_buffer, size_t *p_size) {
|
VY_DLLEXPORT vy_result vyLoadAssetDirect(vy_uid uid, void **p_buffer, size_t *p_size) {
|
||||||
const vy_uid_data *data = vyGetUIDData(uid);
|
const vy_uid_data *data = vyGetUIDData(uid);
|
||||||
if (!data)
|
if (!data)
|
||||||
@ -28,35 +62,13 @@ VY_DLLEXPORT vy_result vyLoadAssetDirect(vy_uid uid, void **p_buffer, size_t *p_
|
|||||||
return VY_LOAD_FAILED;
|
return VY_LOAD_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
const vy_package_asset_header *header = compressed_buffer;
|
void *decompressed_buffer;
|
||||||
|
size_t decompressed_size;
|
||||||
size_t compressed_size = (data->size) - sizeof(*header);
|
vy_result res = DecompressAsset(compressed_buffer, data->size, &decompressed_buffer, &decompressed_size);
|
||||||
XXH64_hash_t calculated_hash = XXH3_64bits((header + 1), compressed_size);
|
|
||||||
XXH64_hash_t file_hash = XXH64_hashFromCanonical(&header->checksum);
|
|
||||||
if (calculated_hash != file_hash) {
|
|
||||||
vyLog("core", "Checksum mismatch for asset %u", uid);
|
|
||||||
vyReleaseBuffer(compressed_buffer, data->size);
|
|
||||||
return VY_LOAD_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t size = (size_t)header->decompressed_size;
|
|
||||||
void *decompressed_buffer = vyAllocBuffer(size);
|
|
||||||
if (!decompressed_buffer) {
|
|
||||||
vyReleaseBuffer(compressed_buffer, data->size);
|
|
||||||
return VY_BUFFER_ALLOC_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (LZ4_decompress_safe((const char *)(header + 1),
|
|
||||||
(char *)decompressed_buffer,
|
|
||||||
(int)compressed_size,
|
|
||||||
(int)size) < 0) {
|
|
||||||
vyReleaseBuffer(compressed_buffer, data->size);
|
|
||||||
return VY_LOAD_FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
vyReleaseBuffer(compressed_buffer, data->size);
|
vyReleaseBuffer(compressed_buffer, data->size);
|
||||||
*p_buffer = decompressed_buffer;
|
*p_buffer = decompressed_buffer;
|
||||||
*p_size = size;
|
*p_size = decompressed_size;
|
||||||
|
|
||||||
return VY_SUCCESS;
|
return res;
|
||||||
}
|
}
|
@ -23,9 +23,18 @@ enum {
|
|||||||
VY_UNKNOWN_ASSET = VY_SUCCESS + 1,
|
VY_UNKNOWN_ASSET = VY_SUCCESS + 1,
|
||||||
VY_BUFFER_ALLOC_FAILED,
|
VY_BUFFER_ALLOC_FAILED,
|
||||||
VY_LOAD_FAILED,
|
VY_LOAD_FAILED,
|
||||||
|
VY_ASSET_CACHE_FULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Load an asset without using the cache */
|
/* Load an asset without using the cache */
|
||||||
VY_DLLEXPORT vy_result vyLoadAssetDirect(vy_uid uid, void **buffer, size_t *size);
|
VY_DLLEXPORT vy_result vyLoadAssetDirect(vy_uid uid, void **buffer, size_t *size);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void *data;
|
||||||
|
size_t size;
|
||||||
|
vy_result result;
|
||||||
|
} vy_get_asset_result;
|
||||||
|
|
||||||
|
VY_DLLEXPORT vy_get_asset_result vyGetAsset(vy_uid uid);
|
||||||
|
|
||||||
#endif
|
#endif
|
@ -12,6 +12,7 @@ extern vy_cvar rt_WindowHeight;
|
|||||||
extern vy_cvar rt_BufferManagerMemory;
|
extern vy_cvar rt_BufferManagerMemory;
|
||||||
extern vy_cvar rt_FileTabCapacity;
|
extern vy_cvar rt_FileTabCapacity;
|
||||||
extern vy_cvar rt_MaxConcurrentAsyncIO;
|
extern vy_cvar rt_MaxConcurrentAsyncIO;
|
||||||
|
extern vy_cvar rt_AssetCacheSize;
|
||||||
|
|
||||||
void RegisterRuntimeCVars(void) {
|
void RegisterRuntimeCVars(void) {
|
||||||
vyRegisterCVAR(&rt_Renderer);
|
vyRegisterCVAR(&rt_Renderer);
|
||||||
@ -21,6 +22,7 @@ void RegisterRuntimeCVars(void) {
|
|||||||
vyRegisterCVAR(&rt_BufferManagerMemory);
|
vyRegisterCVAR(&rt_BufferManagerMemory);
|
||||||
vyRegisterCVAR(&rt_FileTabCapacity);
|
vyRegisterCVAR(&rt_FileTabCapacity);
|
||||||
vyRegisterCVAR(&rt_MaxConcurrentAsyncIO);
|
vyRegisterCVAR(&rt_MaxConcurrentAsyncIO);
|
||||||
|
vyRegisterCVAR(&rt_AssetCacheSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void SetMainThreadId(void);
|
extern void SetMainThreadId(void);
|
||||||
@ -31,6 +33,8 @@ extern vy_result InitFileTab(void);
|
|||||||
extern void ShutdownFileTab(void);
|
extern void ShutdownFileTab(void);
|
||||||
extern vy_result InitAIO(void);
|
extern vy_result InitAIO(void);
|
||||||
extern void ShutdownAIO(void);
|
extern void ShutdownAIO(void);
|
||||||
|
extern vy_result InitAssetCache(void);
|
||||||
|
extern void ShutdownAssetCache(void);
|
||||||
|
|
||||||
extern vy_result LoadUIDTable(void);
|
extern vy_result LoadUIDTable(void);
|
||||||
extern void ReleaseUIDTable(void);
|
extern void ReleaseUIDTable(void);
|
||||||
@ -58,6 +62,11 @@ VY_DLLEXPORT vy_result vyInitRuntime(void) {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((res = InitAssetCache()) != VY_SUCCESS) {
|
||||||
|
vyReportError("ASSETCACHE", "Init failed.");
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
if ((res = LoadUIDTable()) != VY_SUCCESS) {
|
if ((res = LoadUIDTable()) != VY_SUCCESS) {
|
||||||
vyLog("CORE", "LoadUIDTable returned result: %u", res);
|
vyLog("CORE", "LoadUIDTable returned result: %u", res);
|
||||||
}
|
}
|
||||||
@ -77,6 +86,7 @@ VY_DLLEXPORT vy_result vyInitRuntime(void) {
|
|||||||
VY_DLLEXPORT void vyShutdownRuntime(void) {
|
VY_DLLEXPORT void vyShutdownRuntime(void) {
|
||||||
ReleaseAssetDependencies();
|
ReleaseAssetDependencies();
|
||||||
ReleaseUIDTable();
|
ReleaseUIDTable();
|
||||||
|
ShutdownAssetCache();
|
||||||
ShutdownAIO();
|
ShutdownAIO();
|
||||||
ShutdownFileTab();
|
ShutdownFileTab();
|
||||||
ShutdownBufferManager();
|
ShutdownBufferManager();
|
||||||
|
@ -34,6 +34,29 @@ VY_DLLEXPORT void vyUnlockConditionVar(vy_condition_var *var, bool signal);
|
|||||||
/* The condition variable must be locked by the thread! */
|
/* The condition variable must be locked by the thread! */
|
||||||
VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var);
|
VY_DLLEXPORT void vyWaitOnConditionVar(vy_condition_var *var);
|
||||||
|
|
||||||
|
/* Read-Write Lock */
|
||||||
|
typedef struct {
|
||||||
|
volatile int reader_count;
|
||||||
|
vy_condition_var *cond;
|
||||||
|
} vy_rwlock;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
bool ok;
|
||||||
|
vy_rwlock lock;
|
||||||
|
} vy_create_rwlock_result;
|
||||||
|
|
||||||
|
VY_DLLEXPORT vy_create_rwlock_result vyCreateRWLock(void);
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyDestroyRWLock(vy_rwlock *lock);
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyLockRead(vy_rwlock *lock);
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyUnlockRead(vy_rwlock *lock);
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyLockWrite(vy_rwlock *lock);
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyUnlockWrite(vy_rwlock *lock);
|
||||||
|
|
||||||
/* Threads */
|
/* Threads */
|
||||||
|
|
||||||
typedef struct vy_thread_s vy_thread;
|
typedef struct vy_thread_s vy_thread;
|
||||||
|
43
src/runtime/threading_rwlock.c
Normal file
43
src/runtime/threading_rwlock.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
#include "threading.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
/* Based on: https://eli.thegreenplace.net/2019/implementing-reader-writer-locks/ */
|
||||||
|
|
||||||
|
VY_DLLEXPORT vy_create_rwlock_result vyCreateRWLock(void) {
|
||||||
|
vy_create_rwlock_result res;
|
||||||
|
res.lock.reader_count = 0;
|
||||||
|
res.lock.cond = vyCreateConditionVar();
|
||||||
|
res.ok = res.lock.cond != NULL;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyDestroyRWLock(vy_rwlock *lock) {
|
||||||
|
vyDestroyConditionVar(lock->cond);
|
||||||
|
lock->cond = NULL;
|
||||||
|
lock->reader_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyLockRead(vy_rwlock *lock) {
|
||||||
|
vyLockConditionVar(lock->cond);
|
||||||
|
++lock->reader_count;
|
||||||
|
vyUnlockConditionVar(lock->cond, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyUnlockRead(vy_rwlock *lock) {
|
||||||
|
vyLockConditionVar(lock->cond);
|
||||||
|
assert(lock->reader_count > 0);
|
||||||
|
--lock->reader_count;
|
||||||
|
bool signal = lock->reader_count == 0;
|
||||||
|
vyUnlockConditionVar(lock->cond, signal);
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyLockWrite(vy_rwlock *lock) {
|
||||||
|
vyLockConditionVar(lock->cond);
|
||||||
|
while (lock->reader_count > 0)
|
||||||
|
vyWaitOnConditionVar(lock->cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
VY_DLLEXPORT void vyUnlockWrite(vy_rwlock *lock) {
|
||||||
|
vyUnlockConditionVar(lock->cond, true);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user