Add a named thread-safe variable abstraction or a lock-free hash table
nicowilliams opened this issue · 0 comments
Imagine you're building an API that reads in and parses, say, certificates and keys, or a trust anchor store (so, certificates). It might be slow-ish -- perhaps too slow to do too frequently. And maybe you have to do this multiple times for multiple different certificate stores. It'd be nice to use a TSV (thread-safe variable) to hold an immutable loaded-store value, but to have one per-store. Now the certificate stores have names (e.g., path names), so it would be even nicer to not have to keep track of which TSV is for which store, just use the store's name to get to the TSV.
This:
int thread_safe_var_init_named(thread_safe_var *,
thread_safe_var_dtor_f,
void *, /*namespace*/
const char * /*name*/);
would be very nice. If called multiple times, it should not re-initialized a TSV, just output the existing TSV. Though perhaps an thread_safe_var_find_named();
would also be nice:
int thread_safe_var_find_named(thread_safe_var * /*TSV out*/,
void * /*namespace*/,
const char * /*name*/);
The namespace
would typically be the address of a static
(global) in the caller's object.
The name
would be a C string.
As well it would be nice to have:
int thread_safe_var_get_named(void *, /*namespace*/
const char * /*name*/
void ** /*value out*/,
uint64_t * /*version out*/);
and have that be fast even though a lookup would be needed. The lookup can be made fast by keeping a per-thread sorted array of name&TSV values that can be binary searched.
Then one could write:
static int namespace;
cert_store *cs;
(void) thread_safe_var_get_named(&namespace, "/etc/ssl/certs/foo", &cs, NULL);
or
static int namespace;
// ...
cert_store *cs;
if (cts->tsv_cs == NULL &&
thread_safe_var_init_named(&ctx->tsv_cs, cert_store_dtor, &namespace, cert_store_path))
err(1, "Could not initialize TSV for cert store %s", cert_store_path);
if (thread_safe_var_get(cts->tsv_cs, &cs, NULL))
/* This would be ENOMEM in pthread_set_specific() */
err(1, "Could not read the TSV for cert store %s", cert_store_path);
if (cs == NULL) {
if (load_cert_store(cert_store_path, tsv_cs) /* sets the TSV */)
err(1, "Could not load certificate store %s", cert_store_path);
} else if (cert_store_last_reload_time(cs) > 300) {
if (load_cert_store(cert_store_path, tsv_cs) /* sets the TSV */)
warn("Could not reload certificate store %s", cert_store_path);
if (thread_safe_var_get(cts->tsv_cs, &cs, NULL))
err(1, "Could not read the TSV for cert store %s", cert_store_path);
}
/* Use the certificate store in `cs` */
Alternatively a lock-free hash table where reading a key's value causes the value returned to be safe to use until the next key/value is read or until the reference is "put":
typedef struct ctp_hash_table_s *ctp_hash_table;
int ctp_hash_table_init(ctp_hash_table *, size_t);
void ctp_hash_table_destroy(ctp_hash_table *);
const void *ctp_hash_table_get(ctp_hash_table, const char *);
void ctp_hash_table_put(ctp_hash_table);
int ctp_hash_table_set(ctp_hash_table, const char *, const void *);