diff --git a/doc/Makefile.in b/doc/Makefile.in index 0c519a2e..c89c9502 100644 --- a/doc/Makefile.in +++ b/doc/Makefile.in @@ -107,7 +107,9 @@ OBJECTS=CK_ARRAY_FOREACH \ ck_rhs_grow \ ck_rhs_rebuild \ ck_rhs_count \ + ck_rhs_map_size \ ck_rhs_reset \ + ck_rhs_reset_preallocated \ ck_rhs_reset_size \ ck_rhs_stat \ ck_rwcohort \ diff --git a/doc/ck_rhs_map_size b/doc/ck_rhs_map_size new file mode 100644 index 00000000..2cafcdc5 --- /dev/null +++ b/doc/ck_rhs_map_size @@ -0,0 +1,63 @@ +.\" +.\" Copyright 2012-2013 Samy Al Bahra. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd April 26, 2026 +.Dt CK_RHS_MAP_SIZE 3 +.Sh NAME +.Nm ck_rhs_map_size +.Nd return the backing map allocation size for a hash set capacity +.Sh LIBRARY +Concurrency Kit (libck, \-lck) +.Sh SYNOPSIS +.In ck_rhs.h +.Ft size_t +.Fn ck_rhs_map_size "ck_rhs_t *hs" "unsigned long size" +.Sh DESCRIPTION +The +.Fn ck_rhs_map_size 3 +function returns the number of bytes required for backing memory +capable of storing a hash set with enough capacity for +.Fa size +entries using the configuration of +.Fa hs . +.Pp +This is intended to size caller-managed backing memory for +.Xr ck_rhs_reset_preallocated 3 . +.Sh RETURN VALUES +The +.Fn ck_rhs_map_size 3 +function returns the required backing allocation size in bytes. +.Sh ERRORS +Behavior is undefined if +.Fa hs +is uninitialized. +.Sh SEE ALSO +.Xr ck_rhs_init 3 , +.Xr ck_rhs_reset 3 , +.Xr ck_rhs_reset_size 3 , +.Xr ck_rhs_reset_preallocated 3 +.Pp +Additional information available at http://concurrencykit.org/ diff --git a/doc/ck_rhs_reset b/doc/ck_rhs_reset index a750d85f..28179d94 100644 --- a/doc/ck_rhs_reset +++ b/doc/ck_rhs_reset @@ -67,6 +67,8 @@ thread. .Xr ck_rhs_set 3 , .Xr ck_rhs_fas 3 , .Xr ck_rhs_remove 3 , +.Xr ck_rhs_map_size 3 , +.Xr ck_rhs_reset_preallocated 3 , .Xr ck_rhs_reset_size 3 , .Xr ck_rhs_grow 3 , .Xr ck_rhs_gc 3 , diff --git a/doc/ck_rhs_reset_preallocated b/doc/ck_rhs_reset_preallocated new file mode 100644 index 00000000..aa85dad3 --- /dev/null +++ b/doc/ck_rhs_reset_preallocated @@ -0,0 +1,77 @@ +.\" +.\" Copyright 2012-2013 Samy Al Bahra. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" +.Dd April 26, 2026 +.Dt CK_RHS_RESET_PREALLOCATED 3 +.Sh NAME +.Nm ck_rhs_reset_preallocated +.Nd remove all keys from a hash set using caller-provided backing memory +.Sh LIBRARY +Concurrency Kit (libck, \-lck) +.Sh SYNOPSIS +.In ck_rhs.h +.Ft void +.Fn ck_rhs_reset_preallocated "ck_rhs_t *hs" "unsigned long size" "void *memory" +.Sh DESCRIPTION +The +.Fn ck_rhs_reset_preallocated 3 +function removes all keys stored in the hash set pointed to by the +.Fa hs +argument and creates a new generation of the hash set that is backed by +the caller-provided +.Fa memory +buffer. +.Pp +The +.Fa memory +argument must be non-NULL and must refer to a region of at least +.Fn ck_rhs_map_size 3 +bytes for the requested +.Fa size +and hash set configuration. The allocation referenced by +.Fa memory +must be safe to reclaim through the +.Li free +callback configured for +.Fa hs . +Ownership of this backing memory is transferred to the hash set and it +will later be reclaimed through that callback. +.Sh RETURN VALUES +This function does not return a value. +.Sh ERRORS +Behavior is undefined if +.Fa hs +is uninitialized. Behavior is undefined if +.Fa memory +is NULL or too small for the requested capacity. Behavior is undefined +if this function is called by a non-writer thread. +.Sh SEE ALSO +.Xr ck_rhs_init 3 , +.Xr ck_rhs_map_size 3 , +.Xr ck_rhs_reset 3 , +.Xr ck_rhs_reset_size 3 +.Pp +Additional information available at http://concurrencykit.org/ diff --git a/doc/ck_rhs_reset_size b/doc/ck_rhs_reset_size index 6e9913e8..621495a4 100644 --- a/doc/ck_rhs_reset_size +++ b/doc/ck_rhs_reset_size @@ -70,11 +70,13 @@ thread. .Xr ck_rhs_set 3 , .Xr ck_rhs_fas 3 , .Xr ck_rhs_remove 3 , +.Xr ck_rhs_map_size 3 , .Xr ck_rhs_grow 3 , .Xr ck_rhs_gc 3 , .Xr ck_rhs_rebuild 3 , .Xr ck_rhs_count 3 , .Xr ck_rhs_reset 3 , +.Xr ck_rhs_reset_preallocated 3 , .Xr ck_rhs_stat 3 .Pp Additional information available at http://concurrencykit.org/ diff --git a/include/ck_rhs.h b/include/ck_rhs.h index 2a21a731..f47f0439 100644 --- a/include/ck_rhs.h +++ b/include/ck_rhs.h @@ -126,8 +126,10 @@ bool ck_rhs_grow(ck_rhs_t *, unsigned long); bool ck_rhs_rebuild(ck_rhs_t *); bool ck_rhs_gc(ck_rhs_t *); unsigned long ck_rhs_count(ck_rhs_t *); +size_t ck_rhs_map_size(ck_rhs_t *, unsigned long); bool ck_rhs_reset(ck_rhs_t *); bool ck_rhs_reset_size(ck_rhs_t *, unsigned long); +void ck_rhs_reset_preallocated(ck_rhs_t *, unsigned long, void *); void ck_rhs_stat(ck_rhs_t *, struct ck_rhs_stat *); bool ck_rhs_set_load_factor(ck_rhs_t *, unsigned int); diff --git a/regressions/ck_rhs/validate/serial.c b/regressions/ck_rhs/validate/serial.c index 92caf18c..140d8e7b 100644 --- a/regressions/ck_rhs/validate/serial.c +++ b/regressions/ck_rhs/validate/serial.c @@ -295,6 +295,39 @@ run_test(unsigned int is, unsigned int ad) return; } +static void +test_reset_preallocated(void) +{ + ck_rhs_t hs; + size_t size; + void *buffer; + unsigned long h; + + if (ck_rhs_init(&hs, CK_RHS_MODE_SPMC | CK_RHS_MODE_OBJECT, + hs_hash, hs_compare, &my_allocator, 16, 6602834) == false) { + ck_error("ck_rhs_init (preallocated)\n"); + } + + h = test[0][0]; + if (ck_rhs_put(&hs, h, test[0]) == false) + ck_error("ck_rhs_put (preallocated)\n"); + + size = ck_rhs_map_size(&hs, 64); + buffer = hs_malloc(size); + if (buffer == NULL) + ck_error("hs_malloc (preallocated)\n"); + + ck_rhs_reset_preallocated(&hs, 64, buffer); + if (ck_rhs_count(&hs) != 0) + ck_error("ck_rhs_reset_preallocated failed to clear entries\n"); + + if (ck_rhs_put(&hs, h, test[0]) == false) + ck_error("ck_rhs_put after ck_rhs_reset_preallocated\n"); + + ck_rhs_destroy(&hs); + return; +} + int main(void) { @@ -305,6 +338,7 @@ main(void) break; } + test_reset_preallocated(); return 0; } diff --git a/src/ck_rhs.c b/src/ck_rhs.c index 81e69217..9f281699 100644 --- a/src/ck_rhs.c +++ b/src/ck_rhs.c @@ -333,31 +333,44 @@ ck_rhs_destroy(struct ck_rhs *hs) return; } -static struct ck_rhs_map * -ck_rhs_map_create(struct ck_rhs *hs, unsigned long entries) +static unsigned long +ck_rhs_map_capacity(unsigned long entries) { - struct ck_rhs_map *map; - unsigned long size, n_entries, limit; + unsigned long n_entries; n_entries = ck_internal_power_2(entries); if (n_entries < CK_RHS_PROBE_L1) n_entries = CK_RHS_PROBE_L1; + return n_entries; +} + +size_t +ck_rhs_map_size(struct ck_rhs *hs, unsigned long entries) +{ + unsigned long n_entries; + + n_entries = ck_rhs_map_capacity(entries); + if (hs->mode & CK_RHS_MODE_READ_MOSTLY) - size = sizeof(struct ck_rhs_map) + + return sizeof(struct ck_rhs_map) + (sizeof(void *) * n_entries + sizeof(struct ck_rhs_no_entry_desc) * n_entries + 2 * CK_MD_CACHELINE - 1); else - size = sizeof(struct ck_rhs_map) + + return sizeof(struct ck_rhs_map) + (sizeof(struct ck_rhs_entry_desc) * n_entries + CK_MD_CACHELINE - 1); - map = hs->m->malloc(size); - if (map == NULL) - return NULL; - map->read_mostly = !!(hs->mode & CK_RHS_MODE_READ_MOSTLY); +} - map->size = size; +static void +ck_rhs_map_init(struct ck_rhs *hs, struct ck_rhs_map *map, unsigned long entries) +{ + unsigned long n_entries, limit; + + n_entries = ck_rhs_map_capacity(entries); + map->read_mostly = !!(hs->mode & CK_RHS_MODE_READ_MOSTLY); + map->size = ck_rhs_map_size(hs, n_entries); /* We should probably use a more intelligent heuristic for default probe length. */ limit = ck_internal_max(n_entries >> (CK_RHS_PROBE_L1_SHIFT + 2), CK_RHS_PROBE_L1_DEFAULT); if (limit > UINT_MAX) @@ -394,6 +407,21 @@ ck_rhs_map_create(struct ck_rhs *hs, unsigned long entries) /* Commit entries purge with respect to map publication. */ ck_pr_fence_store(); + return; +} + +static struct ck_rhs_map * +ck_rhs_map_create(struct ck_rhs *hs, unsigned long entries) +{ + struct ck_rhs_map *map; + size_t size; + + size = ck_rhs_map_size(hs, entries); + + map = hs->m->malloc(size); + if (map == NULL) + return NULL; + ck_rhs_map_init(hs, map, entries); return map; } @@ -421,6 +449,22 @@ ck_rhs_reset(struct ck_rhs *hs) return ck_rhs_reset_size(hs, previous->capacity); } +void +ck_rhs_reset_preallocated(struct ck_rhs *hs, + unsigned long capacity, + void *memory) +{ + struct ck_rhs_map *map, *previous; + + previous = hs->map; + map = memory; + ck_rhs_map_init(hs, map, capacity); + + ck_pr_store_ptr(&hs->map, map); + ck_rhs_map_destroy(hs->m, previous, true); + return; +} + static inline unsigned long ck_rhs_map_probe_next(struct ck_rhs_map *map, unsigned long offset,