diff options
| author | Garrett D'Amore <garrett@damore.org> | 2025-08-25 06:56:09 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2025-08-25 10:03:03 -0700 |
| commit | c17d1cfebc016ed790df74f0eeb539a4a71fadda (patch) | |
| tree | 4b5eeee07ae35c1d25adc2bb8575c115b92f05ec /src/core | |
| parent | b1ece6af107958d9d3935586778184763a44f5ee (diff) | |
| download | nng-c17d1cfebc016ed790df74f0eeb539a4a71fadda.tar.gz nng-c17d1cfebc016ed790df74f0eeb539a4a71fadda.tar.bz2 nng-c17d1cfebc016ed790df74f0eeb539a4a71fadda.zip | |
fixes #2148 Old id_reg_map seems not be freed
This simplifies the code to just use a precompiled static list.
This should be lighter weight, and provably free from leaks.
Diffstat (limited to 'src/core')
| -rw-r--r-- | src/core/defs.h | 23 | ||||
| -rw-r--r-- | src/core/dialer.c | 2 | ||||
| -rw-r--r-- | src/core/idhash.c | 97 | ||||
| -rw-r--r-- | src/core/idhash.h | 19 | ||||
| -rw-r--r-- | src/core/listener.c | 2 | ||||
| -rw-r--r-- | src/core/pipe.c | 7 | ||||
| -rw-r--r-- | src/core/socket.c | 4 |
7 files changed, 83 insertions, 71 deletions
diff --git a/src/core/defs.h b/src/core/defs.h index 432c0be7..2ad489f6 100644 --- a/src/core/defs.h +++ b/src/core/defs.h @@ -1,5 +1,5 @@ // -// Copyright 2021 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitoar.com> // // This software is supplied under the terms of the MIT License, a @@ -67,7 +67,7 @@ typedef int32_t nni_duration; // Rel. time (ms). typedef void (*nni_cb)(void *); // Some default timing things. -#define NNI_TIME_NEVER ((nni_time) -1) +#define NNI_TIME_NEVER ((nni_time) - 1) #define NNI_TIME_ZERO ((nni_time) 0) #define NNI_SECOND (1000) @@ -239,4 +239,23 @@ typedef nni_type nni_opt_type; #endif // defined(__BYTE_ORDER) #endif // defined() endianness +// nni_alloc allocates memory. In most cases this can just be malloc(). +// However, you may provide a different allocator, for example it is +// possible to use a slab allocator or somesuch. It is permissible for this +// to return NULL if memory cannot be allocated. +extern void *nni_alloc(size_t); + +// nni_zalloc is just like nni_alloc, but ensures that memory is +// initialized to zero. It is a separate function because some platforms +// can use a more efficient zero-based allocation. +extern void *nni_zalloc(size_t); + +// nni_free frees memory allocated with nni_alloc or nni_zalloc. It takes +// a size because some allocators do not track size, or can operate more +// efficiently if the size is provided with the free call. Examples of this +// are slab allocators like this found in Solaris/illumos (see libumem). +// This routine does nothing if supplied with a NULL pointer and zero size. +// Most implementations can just call free() here. +extern void nni_free(void *, size_t); + #endif // CORE_DEFS_H diff --git a/src/core/dialer.c b/src/core/dialer.c index e422c642..bef110d7 100644 --- a/src/core/dialer.c +++ b/src/core/dialer.c @@ -22,7 +22,7 @@ static void dialer_connect_start(nni_dialer *); static void dialer_connect_cb(void *); static void dialer_timer_cb(void *); -static nni_id_map dialers = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, 0); +static nni_id_map dialers = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, false); static nni_mtx dialers_lk = NNI_MTX_INITIALIZER; uint32_t diff --git a/src/core/idhash.c b/src/core/idhash.c index 73763a07..d3d3ec10 100644 --- a/src/core/idhash.c +++ b/src/core/idhash.c @@ -1,5 +1,5 @@ // -// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // // This software is supplied under the terms of the MIT License, a @@ -8,7 +8,9 @@ // found online at https://opensource.org/licenses/MIT. // -#include "core/nng_impl.h" +#include "idhash.h" +#include "defs.h" +#include "nng_impl.h" #include <string.h> @@ -18,10 +20,19 @@ struct nni_id_entry { void *val; }; -static int id_reg_len = 0; -static int id_reg_num = 0; -static nni_id_map **id_reg_map = NULL; -static nni_mtx id_reg_mtx = NNI_MTX_INITIALIZER; +// Maximum number of static maps permitted. +// This static map initialization to be simpler, and +// avoids an unnecessary heap allocation, at the cost of +// being a compile time tunable. At present there are +// only a few (5) static idmaps defined. + +#ifndef NNI_MAX_STATIC_IDMAP +#define NNI_MAX_STATIC_IDMAP 10 +#endif + +static int id_reg_num = 0; +static nni_id_map *id_reg_map[NNI_MAX_STATIC_IDMAP]; +static nni_mtx id_reg_mtx = NNI_MTX_INITIALIZER; void nni_id_map_init(nni_id_map *m, uint64_t lo, uint64_t hi, bool randomize) @@ -34,20 +45,18 @@ nni_id_map_init(nni_id_map *m, uint64_t lo, uint64_t hi, bool randomize) } NNI_ASSERT(lo != 0); NNI_ASSERT(hi > lo); - m->id_entries = NULL; - m->id_count = 0; - m->id_load = 0; - m->id_cap = 0; - m->id_dyn_val = 0; - m->id_max_load = 0; - m->id_min_load = 0; // never shrink below this - m->id_min_val = lo; - m->id_max_val = hi; - if (randomize) { - m->id_flags = NNI_ID_FLAG_RANDOM; - } else { - m->id_flags = 0; - } + m->id_entries = NULL; + m->id_count = 0; + m->id_load = 0; + m->id_cap = 0; + m->id_dyn_val = 0; + m->id_max_load = 0; + m->id_min_load = 0; // never shrink below this + m->id_min_val = lo; + m->id_max_val = hi; + m->id_random = randomize; + m->id_static = false; + m->id_registered = false; } void @@ -64,7 +73,7 @@ nni_id_map_fini(nni_id_map *m) // Inspired by Python dict implementation. This probe will visit every // cell. We always hash consecutively assigned IDs. This requires that // the capacity is always a power of two. -#define ID_NEXT(m, j) ((((j) *5) + 1) & (m->id_cap - 1)) +#define ID_NEXT(m, j) ((((j) * 5) + 1) & (m->id_cap - 1)) #define ID_INDEX(m, j) ((j) & (m->id_cap - 1)) static size_t @@ -107,37 +116,20 @@ nni_id_get(nni_id_map *m, uint64_t id) return (m->id_entries[index].val); } -static int +static void id_map_register(nni_id_map *m) { - if ((m->id_flags & (NNI_ID_FLAG_STATIC | NNI_ID_FLAG_REGISTER)) != - NNI_ID_FLAG_STATIC) { - return (0); - } nni_mtx_lock(&id_reg_mtx); - if (id_reg_len <= id_reg_num) { - nni_id_map **mr; - int len = id_reg_len; - if (len < 10) { - len = 10; - } else { - len *= 2; - } - mr = nni_zalloc(sizeof(nni_id_map *) * len); - if (mr == NULL) { - nni_mtx_unlock(&id_reg_mtx); - return (NNG_ENOMEM); - } - id_reg_len = len; - if (id_reg_map != NULL) - memcpy( - mr, id_reg_map, id_reg_num * sizeof(nni_id_map *)); - id_reg_map = mr; + // This check occurs under the lock although it probably + // is not strictly necessary. This code is called infrequently. + if (!m->id_registered) { + // If this assertion fires, increase the tunable. + // It shouldn't happen unless we add a lot more static idmaps. + NNI_ASSERT(id_reg_num < NNI_MAX_STATIC_IDMAP); + m->id_registered = true; + id_reg_map[id_reg_num++] = m; } - id_reg_map[id_reg_num++] = m; - m->id_flags |= NNI_ID_FLAG_REGISTER; nni_mtx_unlock(&id_reg_mtx); - return (0); } void @@ -147,11 +139,9 @@ nni_id_map_sys_fini(void) for (int i = 0; i < id_reg_num; i++) { if (id_reg_map[i] != NULL) { nni_id_map_fini(id_reg_map[i]); + id_reg_map[i] = NULL; } } - nni_free(id_reg_map, sizeof(nni_id_map *) * id_reg_len); - id_reg_map = NULL; - id_reg_len = 0; id_reg_num = 0; nni_mtx_unlock(&id_reg_mtx); } @@ -164,7 +154,6 @@ id_resize(nni_id_map *m) uint32_t new_cap; uint32_t old_cap; uint32_t i; - int rv; if ((m->id_load < m->id_max_load) && (m->id_load >= m->id_min_load)) { // No resize needed. @@ -173,8 +162,8 @@ id_resize(nni_id_map *m) // if it is a statically declared map, register it so that we // will free it at finalization time - if ((rv = id_map_register(m)) != 0) { - return (rv); + if (m->id_static) { + id_map_register(m); } old_cap = m->id_cap; @@ -328,7 +317,7 @@ nni_id_alloc(nni_id_map *m, uint64_t *idp, void *val) return (NNG_ENOMEM); } if (m->id_dyn_val == 0) { - if (m->id_flags & NNI_ID_FLAG_RANDOM) { + if (m->id_random) { // NB: The range is inclusive. m->id_dyn_val = nni_random() % (m->id_max_val - m->id_min_val + 1) + diff --git a/src/core/idhash.h b/src/core/idhash.h index b07404aa..1d0c9b0e 100644 --- a/src/core/idhash.h +++ b/src/core/idhash.h @@ -1,5 +1,5 @@ // -// Copyright 2024 Staysail Systems, Inc. <info@staysail.tech> +// Copyright 2025 Staysail Systems, Inc. <info@staysail.tech> // Copyright 2018 Capitar IT Group BV <info@capitar.com> // // This software is supplied under the terms of the MIT License, a @@ -11,7 +11,7 @@ #ifndef CORE_IDHASH_H #define CORE_IDHASH_H -#include "core/defs.h" +#include "defs.h" // We find that we often want to have a list of things listed by a // numeric ID, which is generally monotonically increasing. This is @@ -30,12 +30,14 @@ typedef struct nni_id_entry nni_id_entry; // They are provided here to facilitate inlining in structures. // We can support at most 2^32 ~ 4 billion ~ entries. struct nni_id_map { - uint32_t id_flags; uint32_t id_cap; uint32_t id_count; uint32_t id_load; uint32_t id_min_load; // considers placeholders uint32_t id_max_load; + bool id_static; + bool id_registered; + bool id_random; uint64_t id_min_val; uint64_t id_max_val; uint64_t id_dyn_val; @@ -57,10 +59,13 @@ extern void nni_id_map_sys_fini(void); extern bool nni_id_visit(nni_id_map *, uint64_t *, void **, uint32_t *); extern uint32_t nni_id_count(const nni_id_map *); -#define NNI_ID_MAP_INITIALIZER(min, max, flags) \ - { \ - .id_min_val = (min), .id_max_val = (max), \ - .id_flags = ((flags) | NNI_ID_FLAG_STATIC) \ +#define NNI_ID_MAP_INITIALIZER(min, max, random) \ + { \ + .id_min_val = (min), \ + .id_max_val = (max), \ + .id_static = true, \ + .id_registered = false, \ + .id_random = random, \ } #endif // CORE_IDHASH_H diff --git a/src/core/listener.c b/src/core/listener.c index d1f3b497..cd10cb02 100644 --- a/src/core/listener.c +++ b/src/core/listener.c @@ -24,7 +24,7 @@ static void listener_accept_start(nni_listener *); static void listener_accept_cb(void *); static void listener_timer_cb(void *); -static nni_id_map listeners = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, 0); +static nni_id_map listeners = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, false); static nni_mtx listeners_lk = NNI_MTX_INITIALIZER; uint32_t diff --git a/src/core/pipe.c b/src/core/pipe.c index 94520c39..bfc272b3 100644 --- a/src/core/pipe.c +++ b/src/core/pipe.c @@ -9,8 +9,8 @@ // found online at https://opensource.org/licenses/MIT. // -#include "core/nng_impl.h" #include "nng/nng.h" +#include "nng_impl.h" #include "sockimpl.h" #include <stdio.h> @@ -20,9 +20,8 @@ // Operations on pipes (to the transport) are generally blocking operations, // performed in the context of the protocol. -static nni_id_map pipes = - NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, NNI_ID_FLAG_RANDOM); -static nni_mtx pipes_lk = NNI_MTX_INITIALIZER; +static nni_id_map pipes = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, true); +static nni_mtx pipes_lk = NNI_MTX_INITIALIZER; static void pipe_destroy(void *); static void pipe_reap(void *); diff --git a/src/core/socket.c b/src/core/socket.c index 0e2e72ea..12373a6a 100644 --- a/src/core/socket.c +++ b/src/core/socket.c @@ -123,8 +123,8 @@ struct nni_socket { static nni_list sock_list = NNI_LIST_INITIALIZER(sock_list, nni_sock, s_node); static nni_mtx sock_lk = NNI_MTX_INITIALIZER; -static nni_id_map sock_ids = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, 0); -static nni_id_map ctx_ids = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, 0); +static nni_id_map sock_ids = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, false); +static nni_id_map ctx_ids = NNI_ID_MAP_INITIALIZER(1, 0x7fffffff, false); static void nni_ctx_destroy(nni_ctx *); |
