power_play/src/asset_cache.c

220 lines
5.6 KiB
C

#include "asset_cache.h"
#include "sys.h"
#include "string.h"
#include "memory.h"
#include "arena.h"
#include "util.h"
#include "log.h"
#include "snc.h"
/* ========================== *
* Global state
* ========================== */
#define MAX_ASSETS 1024
#define ASSET_LOOKUP_TABLE_CAPACITY (MAX_ASSETS * 4)
GLOBAL struct {
struct snc_mutex lookup_mutex;
struct asset lookup[ASSET_LOOKUP_TABLE_CAPACITY];
u64 num_assets;
struct snc_mutex store_mutex;
struct arena *store_arena;
#if RTC
/* Array of len `num_assets` pointing into populated entries of `lookup`. */
struct asset *dbg_table[ASSET_LOOKUP_TABLE_CAPACITY];
u64 dbg_table_count;
struct snc_mutex dbg_table_mutex;
#endif
} G = ZI, DEBUG_ALIAS(G, G_asset_cache);
/* ========================== *
* Startup
* ========================== */
struct asset_cache_startup_receipt asset_cache_startup(void)
{
/* Init store */
G.store_arena = arena_alloc(GIBI(64));
return (struct asset_cache_startup_receipt) { 0 };
}
/* ========================== *
* Lookup
* ========================== */
INTERNAL void refresh_dbg_table(void)
{
#if RTC
struct snc_lock lock = snc_lock_e(&G.dbg_table_mutex);
MEMZERO_ARRAY(G.dbg_table);
G.dbg_table_count = 0;
for (u64 i = 0; i < countof(G.lookup); ++i) {
struct asset *asset = &G.lookup[i];
if (asset->hash != 0) {
G.dbg_table[G.dbg_table_count++] = asset;
}
}
snc_unlock(&lock);
#endif
}
/* Returns first matching slot or first empty slot if not found.
* Check returned slot->hash != 0 for presence. */
INTERNAL struct asset *asset_cache_get_slot_locked(struct snc_lock *lock, struct string key, u64 hash)
{
snc_assert_locked_e_or_s(lock, &G.lookup_mutex);
(UNUSED)lock;
u64 index = hash % countof(G.lookup);
while (1) {
struct asset *slot = &G.lookup[index];
if (slot->hash) {
/* Occupied */
if (hash == slot->hash && string_eq(key, slot->key)) {
/* Matched slot */
return slot;
} else {
++index;
if (index >= countof(G.lookup)) {
index = 0;
}
}
} else {
/* Empty slot */
return slot;
}
}
}
u64 asset_cache_hash(struct string key)
{
/* TODO: Better hash */
return hash_fnv64(HASH_FNV64_BASIS, key);
}
/* `key` text is copied by this function
*
* Returns existing asset entry or inserts a new one.
*
* If is_first_touch (out parameter) is set to 1, then the caller has
* inserted the asset into the cache.
*
* */
struct asset *asset_cache_touch(struct string key, u64 hash, b32 *is_first_touch)
{
struct asset *asset = 0;
/* Lookup */
{
struct snc_lock lock = snc_lock_s(&G.lookup_mutex);
asset = asset_cache_get_slot_locked(&lock, key, hash);
snc_unlock(&lock);
}
/* Insert if not found */
if (asset->hash) {
if (is_first_touch) {
*is_first_touch = 0;
}
} else {
struct snc_lock lock = snc_lock_e(&G.lookup_mutex);
/* Re-check asset presence in case it was inserted since lock */
asset = asset_cache_get_slot_locked(&lock, key, hash);
if (!asset->hash) {
if (G.num_assets >= MAX_ASSETS) {
sys_panic(LIT("Max assets reached"));
}
struct string key_stored = ZI;
{
/* Copy key to store */
struct asset_cache_store store = asset_cache_store_open();
key_stored = string_copy(store.arena, key);
asset_cache_store_close(&store);
}
/* Initialize asset data */
logf_info("Inserting asset cache entry for \"%F\"", FMT_STR(key));
*asset = (struct asset) {
.status = ASSET_STATUS_UNINITIALIZED,
.hash = hash,
.key = key_stored
};
snc_counter_add(&asset->counter, 1);
if (is_first_touch) {
*is_first_touch = 1;
}
++G.num_assets;
refresh_dbg_table();
}
snc_unlock(&lock);
}
return asset;
}
/* ========================== *
* Marking
* ========================== */
/* Call this once asset job has been created */
void asset_cache_mark_loading(struct asset *asset)
{
asset->status = ASSET_STATUS_LOADING;
}
/* Call this once asset job has finished */
void asset_cache_mark_ready(struct asset *asset, void *store_data)
{
asset->store_data = store_data;
asset->status = ASSET_STATUS_READY;
snc_counter_add(&asset->counter, -1);
}
/* ========================== *
* Job
* ========================== */
void asset_cache_wait(struct asset *asset)
{
snc_counter_wait(&asset->counter);
}
/* ========================== *
* Store
* ========================== */
/* NOTE: At the moment only one global asset store exists, however in the
* future there could be more based on asset lifetime. */
void *asset_cache_get_store_data(struct asset *asset)
{
if (asset->status == ASSET_STATUS_READY) {
return asset->store_data;
} else {
return 0;
}
}
/* Asset store should be opened to allocate memory to the store arena */
struct asset_cache_store asset_cache_store_open(void)
{
struct snc_lock lock = snc_lock_e(&G.store_mutex);
struct asset_cache_store store = {
.lock = lock,
.arena = G.store_arena
};
return store;
}
void asset_cache_store_close(struct asset_cache_store *store)
{
snc_unlock(&store->lock);
}