156 lines
5.2 KiB
C
156 lines
5.2 KiB
C
#include "sim_client.h"
|
|
#include "host.h"
|
|
#include "arena.h"
|
|
#include "util.h"
|
|
#include "arena.h"
|
|
|
|
#define CHANNEL_LOOKUP_BUCKETS 4096
|
|
|
|
/* Offset in bytes from start of store struct to start of clients array (assume adjacently allocated) */
|
|
#define STORE_CLIENTS_OFFSET (sizeof(struct sim_client_store) + (sizeof(struct sim_client_store) % alignof(struct sim_client)))
|
|
|
|
/* Accessed via client_nil() */
|
|
READONLY struct sim_client _g_sim_client_nil = { .valid = false };
|
|
READONLY struct sim_client_store _g_sim_client_store_nil = { .valid = false };
|
|
|
|
struct sim_client_channel_lookup_bucket {
|
|
struct sim_client *first;
|
|
struct sim_client *last;
|
|
};
|
|
|
|
/* ========================== *
|
|
* Store
|
|
* ========================== */
|
|
|
|
struct sim_client_store *sim_client_store_alloc(u64 arena_reserve)
|
|
{
|
|
struct arena arena = arena_alloc(arena_reserve);
|
|
u64 num_channel_lookup_buckets = CHANNEL_LOOKUP_BUCKETS;
|
|
struct sim_client_channel_lookup_bucket *channel_lookup_buckets = arena_push_array_zero(&arena, struct sim_client_channel_lookup_bucket, num_channel_lookup_buckets);
|
|
|
|
struct sim_client_store *store = arena_push_zero(&arena, struct sim_client_store);
|
|
store->clients = arena_dry_push(&arena, struct sim_client);
|
|
ASSERT((u8 *)store->clients - (u8 *)store == STORE_CLIENTS_OFFSET); /* Offset must be correct */
|
|
|
|
store->arena = arena;
|
|
store->num_channel_lookup_buckets = num_channel_lookup_buckets;
|
|
store->channel_lookup_buckets = channel_lookup_buckets;
|
|
|
|
return store;
|
|
}
|
|
|
|
void sim_client_store_release(struct sim_client_store *store)
|
|
{
|
|
arena_release(&store->arena);
|
|
}
|
|
|
|
struct sim_client_store *client_store_from_client(struct sim_client *client)
|
|
{
|
|
if (client->valid) {
|
|
u64 first_client_addr = (u64)(client - client->handle.idx);
|
|
struct sim_client_store *client_store = (struct sim_client_store *)(first_client_addr - STORE_CLIENTS_OFFSET);
|
|
ASSERT(client_store->clients == (struct sim_client *)first_client_addr);
|
|
return client_store;
|
|
} else {
|
|
return client_store_nil();
|
|
}
|
|
}
|
|
|
|
/* ========================== *
|
|
* Client
|
|
* ========================== */
|
|
|
|
INTERNAL u64 hash_from_channel_id(struct host_channel_id channel_id)
|
|
{
|
|
return hash_fnv64(HASH_FNV64_BASIS, STRING_FROM_STRUCT(&channel_id));
|
|
}
|
|
|
|
struct sim_client *sim_client_from_handle(struct sim_client_store *store, struct sim_client_handle handle)
|
|
{
|
|
if (handle.gen != 0 && handle.idx < store->num_reserved) {
|
|
struct sim_client *client = &store->clients[handle.idx];
|
|
if (client->handle.gen == handle.gen) {
|
|
return client;
|
|
}
|
|
}
|
|
return client_nil();
|
|
}
|
|
|
|
struct sim_client *sim_client_from_channel_id(struct sim_client_store *store, struct host_channel_id channel_id)
|
|
{
|
|
struct sim_client *res = client_nil();
|
|
u64 channel_hash = hash_from_channel_id(channel_id);
|
|
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
|
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
|
for (struct sim_client *client = bucket->first; client; client = client->next_hash) {
|
|
if (client->channel_hash == channel_hash) {
|
|
res = client;
|
|
break;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
struct sim_client *sim_client_alloc(struct sim_client_store *store, struct host_channel_id channel_id)
|
|
{
|
|
struct sim_client *client = NULL;
|
|
struct sim_client_handle handle = ZI;
|
|
if (store->first_free_client) {
|
|
client = store->first_free_client;
|
|
store->first_free_client = client->next_free;
|
|
handle = client->handle;
|
|
++handle.gen;
|
|
} else {
|
|
client = arena_push(&store->arena, struct sim_client);
|
|
handle.gen = 1;
|
|
handle.idx = store->num_reserved;
|
|
++store->num_reserved;
|
|
}
|
|
++store->num_allocated;
|
|
*client = _g_sim_client_nil;
|
|
client->valid = true;
|
|
client->handle = handle;
|
|
|
|
u64 channel_hash = hash_from_channel_id(channel_id);
|
|
client->channel_id = channel_id;
|
|
client->channel_hash = channel_hash;
|
|
|
|
/* Insert into channel lookup */
|
|
u64 bucket_index = channel_hash % store->num_channel_lookup_buckets;
|
|
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
|
if (bucket->last) {
|
|
bucket->last->next_hash = client;
|
|
client->prev_hash = bucket->last;
|
|
} else {
|
|
bucket->first = client;
|
|
}
|
|
bucket->last = client;
|
|
return client;
|
|
}
|
|
|
|
void sim_client_release(struct sim_client *client)
|
|
{
|
|
struct sim_client_store *store = client_store_from_client(client);
|
|
client->valid = false;
|
|
++client->handle.gen;
|
|
client->next_free = store->first_free_client;
|
|
store->first_free_client = client;
|
|
--store->num_allocated;
|
|
|
|
/* Remove from channel lookup */
|
|
u64 bucket_index = client->channel_hash % store->num_channel_lookup_buckets;
|
|
struct sim_client_channel_lookup_bucket *bucket = &store->channel_lookup_buckets[bucket_index];
|
|
struct sim_client *prev = client->prev_hash;
|
|
struct sim_client *next = client->next_hash;
|
|
if (prev) {
|
|
prev->next_hash = next;
|
|
} else {
|
|
bucket->first = next;
|
|
}
|
|
if (next) {
|
|
next->prev_hash = prev;
|
|
} else {
|
|
bucket->last = prev;
|
|
}
|
|
}
|