diff options
Diffstat (limited to 'src/core/objhash.c')
| -rw-r--r-- | src/core/objhash.c | 451 |
1 files changed, 0 insertions, 451 deletions
diff --git a/src/core/objhash.c b/src/core/objhash.c deleted file mode 100644 index acdcd2c3..00000000 --- a/src/core/objhash.c +++ /dev/null @@ -1,451 +0,0 @@ -// -// Copyright 2016 Garrett D'Amore <garrett@damore.org> -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#include "core/objhash.h" -#include "core/nng_impl.h" - -#include <string.h> - -// The details of the nni_objhash are "private". -struct nni_objhash { - size_t oh_cap; - size_t oh_count; - size_t oh_load; - size_t oh_minload; // considers placeholders - size_t oh_maxload; - uint32_t oh_minval; - uint32_t oh_maxval; - uint32_t oh_dynval; - nni_mtx oh_lock; - nni_cv oh_cv; - nni_objhash_node *oh_nodes; - nni_objhash_ctor oh_ctor; - nni_objhash_dtor oh_dtor; -}; - -struct nni_objhash_node { - uint32_t on_id; // the key - uint32_t on_skips; // indicates - uint32_t on_refcnt; // reference count - void * on_val; // pointer to user data -}; - -int -nni_objhash_init( - nni_objhash **ohp, nni_objhash_ctor ctor, nni_objhash_dtor dtor) -{ - nni_objhash *oh; - int rv; - - if ((ctor == NULL) || (dtor == NULL)) { - return (NNG_EINVAL); - } - - if ((oh = NNI_ALLOC_STRUCT(oh)) == NULL) { - return (NNG_ENOMEM); - } - - if ((rv = nni_mtx_init(&oh->oh_lock)) != 0) { - NNI_FREE_STRUCT(oh); - return (rv); - } - - if ((rv = nni_cv_init(&oh->oh_cv, &oh->oh_lock)) != 0) { - nni_mtx_fini(&oh->oh_lock); - NNI_FREE_STRUCT(oh); - return (rv); - } - - oh->oh_nodes = NULL; - oh->oh_count = 0; - oh->oh_load = 0; - oh->oh_cap = 0; - oh->oh_maxload = 0; - oh->oh_minload = 0; // never shrink below this - oh->oh_minval = 1; - oh->oh_maxval = 0x7fffffff; - oh->oh_dynval = - nni_random() % (oh->oh_maxval - oh->oh_minval) + oh->oh_minval; - oh->oh_ctor = ctor; - oh->oh_dtor = dtor; - *ohp = oh; - - return (0); -} - -void -nni_objhash_fini(nni_objhash *oh) -{ - if (oh == NULL) { - return; - } - if (oh->oh_nodes != NULL) { - nni_free(oh->oh_nodes, oh->oh_cap * sizeof(nni_objhash_node)); - oh->oh_nodes = NULL; - oh->oh_cap = oh->oh_count = 0; - oh->oh_load = oh->oh_minload = oh->oh_maxload = 0; - } - nni_cv_fini(&oh->oh_cv); - nni_mtx_fini(&oh->oh_lock); - NNI_FREE_STRUCT(oh); -} - -// Inspired by Python dict implementation. This probe will visit every -// cell. We always hash consecutively assigned IDs. -#define NNI_OBJHASH_NEXTPROBE(h, j) ((((j) *5) + 1) & (h->oh_cap - 1)) - -// nni_objhash_find_node finds the object hash node associated with a given id. -// The object hash lock must be held by the caller. -static nni_objhash_node * -nni_objhash_find_node(nni_objhash *oh, uint32_t id) -{ - uint32_t index; - nni_objhash_node *node; - - if (oh->oh_count == 0) { - return (NULL); - } - - index = id & (oh->oh_cap - 1); - - for (;;) { - node = &oh->oh_nodes[index]; - - if ((node->on_val == NULL) && (node->on_skips == 0)) { - return (NULL); - } - if (node->on_id == id) { - return (node); - } - index = NNI_OBJHASH_NEXTPROBE(oh, index); - } -} - -// nni_objhash_find looks up the object, and bumps the reference on it. -// The caller should drop the reference when done by calling nni_objhash_unref. -int -nni_objhash_find(nni_objhash *oh, uint32_t id, void **valp) -{ - nni_objhash_node *node; - int rv; - - nni_mtx_lock(&oh->oh_lock); - node = nni_objhash_find_node(oh, id); - - if ((node != NULL) && (node->on_val != NULL)) { - if (valp != NULL) { - *valp = node->on_val; - } - node->on_refcnt++; - rv = 0; - } else { - rv = NNG_ENOENT; - } - nni_mtx_unlock(&oh->oh_lock); - return (rv); -} - -// Resize the object hash. This is called internally with the lock -// for the object hash held. Grow indicates that this is being called -// from a function that intends to add data, so extra space is needed. -static int -nni_objhash_resize(nni_objhash *oh, int grow) -{ - size_t newsize; - size_t oldsize; - nni_objhash_node *newnodes; - nni_objhash_node *oldnodes; - uint32_t i; - - if ((!grow) && (oh->oh_count == 0) && (oh->oh_cap != 0)) { - // Table is empty, and we are unrefing. Lets reclaim the - // space. Note that this means that allocations which - // fluctuate between one and zero are going to bang on the - // allocator a bit. Since such cases should not be very - // performance sensitive, this is probably okay. - nni_free(oh->oh_nodes, oh->oh_cap * sizeof(nni_objhash_node)); - oh->oh_cap = 0; - oh->oh_nodes = NULL; - oh->oh_minload = 0; - oh->oh_maxload = 0; - return (0); - } - - if ((oh->oh_load < oh->oh_maxload) && - (oh->oh_load >= oh->oh_minload)) { - // No resize needed. - return (0); - } - - oldsize = oh->oh_cap; - newsize = oh->oh_cap; - - newsize = 8; - while (newsize < (oh->oh_count * 2)) { - newsize *= 2; - } - - oldnodes = oh->oh_nodes; - newnodes = nni_alloc(sizeof(nni_objhash_node) * newsize); - if (newnodes == NULL) { - return (NNG_ENOMEM); - } - memset(newnodes, 0, sizeof(nni_objhash_node) * newsize); - - oh->oh_nodes = newnodes; - oh->oh_cap = newsize; - if (newsize > 8) { - oh->oh_minload = newsize / 8; - oh->oh_maxload = newsize * 2 / 3; - } else { - oh->oh_minload = 0; - oh->oh_maxload = 5; - } - for (i = 0; i < oldsize; i++) { - uint32_t index; - if (oldnodes[i].on_val == NULL) { - continue; - } - index = oldnodes[i].on_id & (newsize - 1); - for (;;) { - if (newnodes[index].on_val == NULL) { - oh->oh_load++; - newnodes[index].on_val = oldnodes[i].on_val; - newnodes[index].on_id = oldnodes[i].on_id; - newnodes[index].on_refcnt = - oldnodes[i].on_refcnt; - break; - } - newnodes[index].on_skips++; - index = NNI_OBJHASH_NEXTPROBE(oh, index); - } - } - if (oldsize != 0) { - nni_free(oldnodes, sizeof(nni_objhash_node) * oldsize); - } - return (0); -} - -void -nni_objhash_unref(nni_objhash *oh, uint32_t id) -{ - void * val; - uint32_t index; - nni_objhash_node *node; - nni_objhash_dtor dtor; - - nni_mtx_lock(&oh->oh_lock); - - dtor = oh->oh_dtor; - - node = nni_objhash_find_node(oh, id); - NNI_ASSERT(node != NULL); - val = node->on_val; - - NNI_ASSERT(node->on_refcnt > 0); - NNI_ASSERT(node->on_refcnt < 1000000); // reasonable limit, debug only - node->on_refcnt--; - - // If we have further references, we are done, except that if we have - // only one remaining reference, we might want to wake up another - // thread blocked in nni_objhash_unref_wait. - if (node->on_refcnt != 0) { - if (node->on_refcnt == 1) { - nni_cv_wake(&oh->oh_cv); - } - nni_mtx_unlock(&oh->oh_lock); - return; - } - - NNI_ASSERT(node->on_refcnt == 0); - index = id & (oh->oh_cap - 1); - for (;;) { - node = &oh->oh_nodes[index]; - if (node->on_id == id) { - break; - } - - NNI_ASSERT(node->on_skips != 0); - node->on_skips--; - if ((node->on_val == NULL) && (node->on_skips == 0)) { - oh->oh_load--; - } - index = NNI_OBJHASH_NEXTPROBE(oh, index); - } - - NNI_ASSERT(node->on_val != NULL); - NNI_ASSERT(node->on_refcnt == 0); - NNI_ASSERT(node->on_id == id); - - node->on_val = NULL; - oh->oh_count--; - if (node->on_skips == 0) { - oh->oh_load--; - } - // Reclaim the buffer if we want, but preserve the limits. - nni_objhash_resize(oh, 0); - - nni_mtx_unlock(&oh->oh_lock); - - // Now run the destructor. - dtor(val); -} - -void -nni_objhash_unref_wait(nni_objhash *oh, uint32_t id) -{ - void * val; - uint32_t index; - nni_objhash_node *node; - nni_objhash_dtor dtor; - - nni_mtx_lock(&oh->oh_lock); - - dtor = oh->oh_dtor; - - node = nni_objhash_find_node(oh, id); - NNI_ASSERT(node != NULL); - NNI_ASSERT(node->on_refcnt > 0); - val = node->on_val; - - while (node->on_refcnt != 1) { - nni_cv_wait(&oh->oh_cv); - // If the table resizes, it can invalidate our old node. - node = nni_objhash_find_node(oh, id); - } - node->on_refcnt--; - NNI_ASSERT(node->on_refcnt == 0); - - index = id & (oh->oh_cap - 1); - for (;;) { - node = &oh->oh_nodes[index]; - if (node->on_id == id) { - break; - } - - NNI_ASSERT(node->on_skips != 0); - node->on_skips--; - if ((node->on_val == NULL) && (node->on_skips == 0)) { - oh->oh_load--; - } - index = NNI_OBJHASH_NEXTPROBE(oh, index); - } - - NNI_ASSERT(node->on_val != NULL); - NNI_ASSERT(node->on_refcnt == 0); - NNI_ASSERT(node->on_id == id); - - node->on_val = NULL; - oh->oh_count--; - if (node->on_skips == 0) { - oh->oh_load--; - } - // Reclaim the buffer if we want, but preserve the limits. - nni_objhash_resize(oh, 0); - - nni_mtx_unlock(&oh->oh_lock); - - // Now run the destructor. - dtor(val); -} - -// Allocate a new object hash entry. Note that this will execute the -// constructor with the object hash lock held. Consequently, code that -// runs the constructor must not run for long periods of time, since that -// can block all other uses of the object hash. -int -nni_objhash_alloc(nni_objhash *oh, uint32_t *idp, void **valp) -{ - uint32_t id; - uint32_t index; - nni_objhash_node *node; - - nni_mtx_lock(&oh->oh_lock); - - if (oh->oh_count > (oh->oh_maxval - oh->oh_minval)) { - // Really more like ENOSPC.. the table is filled to max. - nni_mtx_unlock(&oh->oh_lock); - return (NNG_ENOMEM); - } - - nni_objhash_resize(oh, 1); - - for (;;) { - id = oh->oh_dynval; - oh->oh_dynval++; - if ((oh->oh_dynval > oh->oh_maxval) || - (oh->oh_dynval < oh->oh_minval)) { - oh->oh_dynval = oh->oh_minval; - } - - if (nni_objhash_find_node(oh, id) == NULL) { - // We can use this ID, great! - break; - } - } - - // We know the ID we're going to use, but we have to walk again, - // because we need to note whether we had to skip (probe), and mark - // them so they don't get nuked along the way. - // check to see if anything is located there. - index = id & (oh->oh_cap - 1); - for (;;) { - node = &oh->oh_nodes[index]; - if (node->on_val == NULL) { - break; - } - NNI_ASSERT(node->on_id != id); - node->on_skips++; - index = NNI_OBJHASH_NEXTPROBE(oh, index); - } - - NNI_ASSERT(node->on_refcnt == 0); - node->on_id = id; - node->on_refcnt++; - - node->on_val = oh->oh_ctor(id); - - if (node->on_val == NULL) { - // Constructor failed; walk *again* to undo the skip - // increments. - node->on_refcnt--; - index = id & (oh->oh_cap - 1); - for (;;) { - node = &oh->oh_nodes[index]; - if (node->on_val == NULL) { - NNI_ASSERT(node->on_id == id); - break; - } - NNI_ASSERT(node->on_skips != 0); - node->on_skips--; - index = NNI_OBJHASH_NEXTPROBE(oh, index); - } - - nni_mtx_unlock(&oh->oh_lock); - return (NNG_ENOMEM); // no other return from ctor - } - - oh->oh_count++; - if (node->on_skips == 0) { - oh->oh_load++; - } - *valp = node->on_val; - *idp = id; - - NNI_ASSERT(node->on_refcnt == 1); - - nni_mtx_unlock(&oh->oh_lock); - return (0); -} - -size_t -nni_objhash_count(nni_objhash *oh) -{ - return (oh->oh_count); -} |
