aboutsummaryrefslogtreecommitdiff
path: root/src/core
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2025-08-25 06:56:09 -0700
committerGarrett D'Amore <garrett@damore.org>2025-08-25 10:03:03 -0700
commitc17d1cfebc016ed790df74f0eeb539a4a71fadda (patch)
tree4b5eeee07ae35c1d25adc2bb8575c115b92f05ec /src/core
parentb1ece6af107958d9d3935586778184763a44f5ee (diff)
downloadnng-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.h23
-rw-r--r--src/core/dialer.c2
-rw-r--r--src/core/idhash.c97
-rw-r--r--src/core/idhash.h19
-rw-r--r--src/core/listener.c2
-rw-r--r--src/core/pipe.c7
-rw-r--r--src/core/socket.c4
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 *);