/*
- * Copyright 2024-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2024-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
{
struct ht_mutable_data_st *newmd = NULL;
struct ht_mutable_data_st *oldmd = NULL;
+ CRYPTO_RCU_CB_ITEM *cbi = NULL;
newmd = OPENSSL_zalloc(sizeof(*newmd));
if (newmd == NULL)
newmd->neighborhood_mask = DEFAULT_NEIGH_LEN - 1;
- /* Swap the old and new mutable data sets */
if (!h->config.no_rcu) {
+ cbi = ossl_rcu_cb_item_new();
+ if (cbi == NULL) {
+ OPENSSL_free(newmd->neighborhood_ptr_to_free);
+ OPENSSL_free(newmd);
+ return 0;
+ }
+
+ /* Swap the old and new mutable data sets */
oldmd = ossl_rcu_deref(&h->md);
ossl_rcu_assign_ptr(&h->md, &newmd);
} else {
h->wpd.neighborhood_len = DEFAULT_NEIGH_LEN;
if (!h->config.no_rcu) {
- ossl_rcu_call(h->lock, free_oldmd, oldmd);
+ ossl_rcu_call(h->lock, cbi, free_oldmd, oldmd);
+ h->wpd.need_sync = 1;
} else {
free_oldmd(oldmd);
}
- h->wpd.need_sync = 1;
return 1;
}
{
struct ht_mutable_data_st *newmd;
struct ht_mutable_data_st *oldmd = ossl_rcu_deref(&h->md);
+ CRYPTO_RCU_CB_ITEM *cbi = NULL;
int rc = 0;
uint64_t oldi, oldj, newi, newj;
uint64_t oldhash;
}
}
}
+
+ /*
+ * Pre allocate the rcu callback item before assigning the newmd.
+ */
+ if (!h->config.no_rcu) {
+ cbi = ossl_rcu_cb_item_new();
+ if (cbi == NULL)
+ goto out_free;
+ }
+
/*
* Now that our entries are all hashed into the new bucket list
* update our bucket_len and target_max_load
*/
if (!h->config.no_rcu) {
ossl_rcu_assign_ptr(&h->md, &newmd);
- ossl_rcu_call(h->lock, free_old_neigh_table, oldmd);
+ ossl_rcu_call(h->lock, cbi, free_old_neigh_table, oldmd);
h->wpd.need_sync = 1;
} else {
h->md = newmd;
}
/* Do a replacement */
if (!h->config.no_rcu) {
+ CRYPTO_RCU_CB_ITEM *cbi = ossl_rcu_cb_item_new();
+ if (cbi == NULL)
+ return 0;
if (!CRYPTO_atomic_store(&md->neighborhoods[neigh_idx].entries[j].hash,
- hash, h->atomic_lock))
+ hash, h->atomic_lock)) {
+ ossl_rcu_cb_item_free(cbi);
return 0;
+ }
*olddata = (HT_VALUE *)md->neighborhoods[neigh_idx].entries[j].value;
ossl_rcu_assign_ptr(&md->neighborhoods[neigh_idx].entries[j].value,
&newval);
- ossl_rcu_call(h->lock, free_old_ht_value, *olddata);
+ ossl_rcu_call(h->lock, cbi, free_old_ht_value, *olddata);
} else {
md->neighborhoods[neigh_idx].entries[j].hash = hash;
*olddata = (HT_VALUE *)md->neighborhoods[neigh_idx].entries[j].value;
if (compare_hash(hash, h->md->neighborhoods[neigh_idx].entries[j].hash)
&& match_key(key, &v->value.key)) {
if (!h->config.no_rcu) {
+ CRYPTO_RCU_CB_ITEM *cbi = ossl_rcu_cb_item_new();
+ if (cbi == NULL)
+ break;
if (!CRYPTO_atomic_store(&h->md->neighborhoods[neigh_idx].entries[j].hash,
- 0, h->atomic_lock))
+ 0, h->atomic_lock)) {
+ ossl_rcu_cb_item_free(cbi);
break;
+ }
ossl_rcu_assign_ptr(&h->md->neighborhoods[neigh_idx].entries[j].value, &nv);
+ ossl_rcu_call(h->lock, cbi, free_old_entry, v);
} else {
h->md->neighborhoods[neigh_idx].entries[j].hash = 0;
h->md->neighborhoods[neigh_idx].entries[j].value = NULL;
+ free_old_entry(v);
}
h->wpd.value_count--;
+ h->wpd.need_sync = 1;
rc = 1;
break;
}
}
- if (rc == 1) {
- if (!h->config.no_rcu)
- ossl_rcu_call(h->lock, free_old_entry, v);
- else
- free_old_entry(v);
- h->wpd.need_sync = 1;
- }
return rc;
}
}
}
-int ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data)
+CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void)
{
- struct rcu_cb_item *new = OPENSSL_zalloc(sizeof(*new));
+ return OPENSSL_zalloc(sizeof(CRYPTO_RCU_CB_ITEM));
+}
- if (new == NULL)
- return 0;
+void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item)
+{
+ OPENSSL_free(item);
+}
- new->fn = cb;
- new->data = data;
- new->next = lock->cb_items;
- lock->cb_items = new;
- return 1;
+void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
+ rcu_cb_fn cb, void *data)
+{
+ item->fn = cb;
+ item->data = data;
+ item->next = lock->cb_items;
+ lock->cb_items = item;
}
void *ossl_rcu_uptr_deref(void **p)
}
}
+CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void)
+{
+ return OPENSSL_zalloc(sizeof(CRYPTO_RCU_CB_ITEM));
+}
+
+void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item)
+{
+ OPENSSL_free(item);
+}
+
/*
* Note: This call assumes its made under the protection of
* ossl_rcu_write_lock
*/
-int ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data)
+void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
+ rcu_cb_fn cb, void *data)
{
- struct rcu_cb_item *new = OPENSSL_zalloc(sizeof(*new));
-
- if (new == NULL)
- return 0;
-
- new->data = data;
- new->fn = cb;
-
- new->next = lock->cb_items;
- lock->cb_items = new;
-
- return 1;
+ item->fn = cb;
+ item->data = data;
+ item->next = lock->cb_items;
+ lock->cb_items = item;
}
void *ossl_rcu_uptr_deref(void **p)
return;
}
+CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void)
+{
+ return OPENSSL_zalloc(sizeof(CRYPTO_RCU_CB_ITEM));
+}
+
+void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item)
+{
+ OPENSSL_free(item);
+}
+
/*
* Note, must be called under the protection of ossl_rcu_write_lock
*/
-int ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data)
+void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
+ rcu_cb_fn cb, void *data)
{
- struct rcu_cb_item *new;
-
- new = OPENSSL_zalloc(sizeof(struct rcu_cb_item));
- if (new == NULL)
- return 0;
- new->data = data;
- new->fn = cb;
-
- new->next = lock->cb_items;
- lock->cb_items = new;
-
- return 1;
+ item->fn = cb;
+ item->data = data;
+ item->next = lock->cb_items;
+ lock->cb_items = item;
}
void *ossl_rcu_uptr_deref(void **p)
ossl_rcu_lock_free, ossl_rcu_read_lock,
ossl_rcu_read_unlock, ossl_rcu_write_lock,
ossl_rcu_write_unlock, ossl_synchronize_rcu,
+ossl_rcu_cb_item_new, ossl_rcu_cb_item_free,
ossl_rcu_call, ossl_rcu_deref,
ossl_rcu_assign_ptr, ossl_rcu_uptr_deref,
ossl_rcu_assign_uptr
void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock);
void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock);
void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock);
- void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data);
+ CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void);
+ void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item);
+ void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
+ rcu_cb_fn cb, void *data);
void *ossl_rcu_deref(void **p);
void ossl_rcu_uptr_deref(void **p);
void ossl_rcu_assign_ptr(void **p, void **v);
=item *
-ossl_rcu_call() enqueues a callback function to the lock, to be called
-when the next synchronization completes. Note: It is not guaranteed that the
-thread which enqueued the callback will be the thread which executes the
-callback
+ossl_rcu_cb_item_new() allocates a callback item suitable for use with
+ossl_rcu_call(). Returns NULL on allocation failure. The item is owned by
+the caller until it is passed to ossl_rcu_call(), at which point ownership
+transfers to the lock and the item must not be touched again by the caller.
+
+=item *
+
+ossl_rcu_cb_item_free() frees a callback item that was allocated by
+ossl_rcu_cb_item_new() but never passed to ossl_rcu_call(). Use this to
+release the item on the failure path of an operation that decided not to
+publish its update.
+
+=item *
+
+ossl_rcu_call() enqueues a callback function I<cb> to the lock, to be
+called with I<data> when the next synchronization completes. The caller
+must provide a callback item I<item> previously obtained from
+ossl_rcu_cb_item_new(). After this call the lock owns the item and will
+free it after invoking the callback. This function does not allocate and
+cannot fail, which lets callers allocate the item before performing any
+publish (assign_ptr) and bail cleanly if allocation fails. Note: it is
+not guaranteed that the thread which enqueued the callback will be the
+thread which executes the callback.
=item *
ossl_rcu_lock_new() returns a pointer to a newly created RCU lock structure.
+ossl_rcu_cb_item_new() returns a pointer to a newly created callback item,
+or NULL on allocation failure.
+
ossl_rcu_deref() and ossl_rcu_uptr_deref() return the value pointed
to by the passed in value v.
static void myinit(void)
{
- lock = ossl_rcu_lock_new(1);
+ lock = ossl_rcu_lock_new(1, NULL);
}
static int initlock(void)
return 1;
}
- static void writer_thread()
+ static void free_old_foo(void *data)
+ {
+ OPENSSL_free(data);
+ }
+
+ static int writer_thread(void)
{
struct foo *newfoo;
struct foo *oldfoo;
+ CRYPTO_RCU_CB_ITEM *cbi;
initlock();
* 1) create a new shared object
*/
newfoo = OPENSSL_zalloc(sizeof(struct foo));
+ if (newfoo == NULL)
+ return 0;
/*
- * acquire the write side lock
+ * 2) Pre allocate the rcu callback item before any publish.
+ */
+ cbi = ossl_rcu_cb_item_new();
+ if (cbi == NULL) {
+ OPENSSL_free(newfoo);
+ return 0;
+ }
+
+ /*
+ * 3) acquire the write side lock
*/
ossl_rcu_write_lock(lock);
/*
- * 2) read the old pointer
+ * 4) read the old pointer
*/
oldfoo = ossl_rcu_deref(&fooptr);
/*
- * 3) Copy the old pointer to the new object, and
+ * 5) Copy the old pointer to the new object, and
* make any needed adjustments
*/
memcpy(newfoo, oldfoo, sizeof(struct foo));
newfoo->aval++;
/*
- * 4) Update the shared pointer to the new value
+ * 6) Update the shared pointer to the new value
*/
ossl_rcu_assign_ptr(&fooptr, &newfoo);
/*
- * 5) Release the write side lock
+ * 7) Schedule the old pointer to be freed when readers are done.
*/
- ossl_rcu_write_unlock(lock);
+ ossl_rcu_call(lock, cbi, free_old_foo, oldfoo);
/*
- * 6) wait for any read side holds on the old data
- * to be released
+ * 8) Release the write side lock
*/
- ossl_synchronize_rcu(lock);
+ ossl_rcu_write_unlock(lock);
/*
- * 7) free the old pointer, now that there are no
- * further readers
+ * 9) wait for any read side holds on the old data
+ * to be released, after which free_old_foo will run
*/
- OPENSSL_free(oldfoo);
+ ossl_synchronize_rcu(lock);
+
+ return 1;
}
- static void reader_thread()
+ static void reader_thread(void)
{
struct foo *myfoo = NULL;
int a;
=head1 COPYRIGHT
-Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
+Copyright 2023-2026 The OpenSSL Project Authors. All Rights Reserved.
Licensed under the Apache License 2.0 (the "License"). You may not use
this file except in compliance with the License. You can obtain a copy
typedef struct rcu_lock_st CRYPTO_RCU_LOCK;
+typedef struct rcu_cb_item CRYPTO_RCU_CB_ITEM;
+
CRYPTO_RCU_LOCK *ossl_rcu_lock_new(int num_writers, OSSL_LIB_CTX *ctx);
void ossl_rcu_lock_free(CRYPTO_RCU_LOCK *lock);
int ossl_rcu_read_lock(CRYPTO_RCU_LOCK *lock);
void ossl_rcu_write_unlock(CRYPTO_RCU_LOCK *lock);
void ossl_rcu_read_unlock(CRYPTO_RCU_LOCK *lock);
void ossl_synchronize_rcu(CRYPTO_RCU_LOCK *lock);
-int ossl_rcu_call(CRYPTO_RCU_LOCK *lock, rcu_cb_fn cb, void *data);
+CRYPTO_RCU_CB_ITEM *ossl_rcu_cb_item_new(void);
+void ossl_rcu_cb_item_free(CRYPTO_RCU_CB_ITEM *item);
+void ossl_rcu_call(CRYPTO_RCU_LOCK *lock, CRYPTO_RCU_CB_ITEM *item,
+ rcu_cb_fn cb, void *data);
void *ossl_rcu_uptr_deref(void **p);
void ossl_rcu_assign_uptr(void **p, void **v);
#define ossl_rcu_deref(p) ossl_rcu_uptr_deref((void **)p)
/*
- * Copyright 2016-2025 The OpenSSL Project Authors. All Rights Reserved.
+ * Copyright 2016-2026 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
int count;
OSSL_TIME t1, t2;
uint64_t *old, *new;
+ CRYPTO_RCU_CB_ITEM *cbi = NULL;
t1 = ossl_time_now();
new = OPENSSL_zalloc(sizeof(uint64_t));
OPENSSL_assert(new != NULL);
*new = (uint64_t)0xBAD;
+
+ if (contention == 0) {
+ cbi = ossl_rcu_cb_item_new();
+ OPENSSL_assert(cbi != NULL);
+ }
+
if (contention == 0)
OSSL_sleep(1000);
ossl_rcu_write_lock(rcu_lock);
*new = global_ctr++;
ossl_rcu_assign_ptr(&writer_ptr, &new);
if (contention == 0)
- ossl_rcu_call(rcu_lock, free_old_rcu_data, old);
+ ossl_rcu_call(rcu_lock, cbi, free_old_rcu_data, old);
ossl_rcu_write_unlock(rcu_lock);
if (contention != 0) {
ossl_synchronize_rcu(rcu_lock);