From: Witold Kręcicki Date: Tue, 5 Nov 2019 18:52:20 +0000 (-0800) Subject: implement hazard pointer data structure X-Git-Tag: v9.15.6~21^2~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=aa57fa709085411ac9afb6bd1b71c362bfb7e55d;p=thirdparty%2Fbind9.git implement hazard pointer data structure this is a mechanism to allow safe lock-free data structures. --- diff --git a/lib/isc/Makefile.in b/lib/isc/Makefile.in index ae7ca482bf4..4006606263e 100644 --- a/lib/isc/Makefile.in +++ b/lib/isc/Makefile.in @@ -50,8 +50,8 @@ OBJS = pk11.@O@ pk11_result.@O@ \ backtrace.@O@ base32.@O@ base64.@O@ \ bind9.@O@ buffer.@O@ bufferlist.@O@ \ commandline.@O@ counter.@O@ crc64.@O@ error.@O@ entropy.@O@ \ - event.@O@ hash.@O@ ht.@O@ heap.@O@ hex.@O@ hmac.@O@ \ - httpd.@O@ iterated_hash.@O@ \ + event.@O@ hash.@O@ ht.@O@ heap.@O@ hex.@O@ \ + hmac.@O@ hp.@O@ httpd.@O@ iterated_hash.@O@ \ lex.@O@ lfsr.@O@ lib.@O@ log.@O@ \ md.@O@ mem.@O@ mutexblock.@O@ \ netaddr.@O@ netscope.@O@ nonce.@O@ openssl_shim.@O@ pool.@O@ \ @@ -69,8 +69,8 @@ SRCS = pk11.c pk11_result.c \ aes.c app.c assertions.c \ backtrace.c base32.c base64.c bind9.c \ buffer.c bufferlist.c commandline.c counter.c crc64.c \ - entropy.c error.c event.c hash.c ht.c heap.c hex.c hmac.c \ - httpd.c iterated_hash.c \ + entropy.c error.c event.c hash.c ht.c heap.c \ + hex.c hmac.c hp.c httpd.c iterated_hash.c \ lex.c lfsr.c lib.c log.c \ md.c mem.c mutexblock.c \ netaddr.c netscope.c nonce.c openssl_shim.c pool.c \ diff --git a/lib/isc/hp.c b/lib/isc/hp.c new file mode 100644 index 00000000000..4d14a54c08d --- /dev/null +++ b/lib/isc/hp.c @@ -0,0 +1,222 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Hazard Pointer implementation. + * + * This work is based on C++ code available from: + * https://github.com/pramalhe/ConcurrencyFreaks/ + * + * Copyright (c) 2014-2016, Pedro Ramalhete, Andreia Correia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Concurrency Freaks nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * 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. + */ + +#include + +#include +#include +#include +#include +#include + +#define HP_MAX_THREADS 128 +#define HP_MAX_HPS 4 /* This is named 'K' in the HP paper */ +#define CLPAD (128 / sizeof(uintptr_t)) +#define HP_THRESHOLD_R 0 /* This is named 'R' in the HP paper */ + +/* Maximum number of retired objects per thread */ +#define MAX_RETIRED (HP_MAX_THREADS * HP_MAX_HPS) + +#define TID_UNKNOWN -1 + +static atomic_int_fast32_t tid_v_base; +static bool tid_v_initialized; + +#if defined(HAVE_TLS) +#if defined(HAVE_THREAD_LOCAL) +#include +static thread_local int tid_v = TID_UNKNOWN; +#elif defined(HAVE___THREAD) +static __thread int tid_v = TID_UNKNOWN; +#elif defined(HAVE___DECLSPEC_THREAD) +static __declspec( thread ) int tid_v = TID_UNKNOWN; +#else /* if defined(HAVE_THREAD_LOCAL) */ +#error "Unknown method for defining a TLS variable!" +#endif /* if defined(HAVE_THREAD_LOCAL) */ +#else /* if defined(HAVE_TLS) */ +#error "Thread-local storage support is required!" +#endif /* if defined(HAVE_TLS) */ + +typedef struct retirelist { + int size; + uintptr_t list[MAX_RETIRED]; +} retirelist_t; + +struct isc_hp { + int max_hps; + isc_mem_t *mctx; + atomic_uintptr_t *hp[HP_MAX_THREADS]; + retirelist_t *rl[HP_MAX_THREADS]; + isc_hp_deletefunc_t *deletefunc; +}; + +static inline int +tid() { + if (!tid_v_initialized) { + atomic_init(&tid_v_base, 0); + tid_v_initialized = true; + } + if (tid_v == TID_UNKNOWN) { + tid_v = atomic_fetch_add(&tid_v_base, 1); + REQUIRE(tid_v < HP_MAX_THREADS); + } + + return (tid_v); +} + +isc_hp_t * +isc_hp_new(isc_mem_t *mctx, size_t max_hps, isc_hp_deletefunc_t *deletefunc) { + isc_hp_t *hp = isc_mem_get(mctx, sizeof(*hp)); + + if (max_hps == 0) { + max_hps = HP_MAX_HPS; + } + + *hp = (isc_hp_t){ + .max_hps = max_hps, + .deletefunc = deletefunc + }; + + isc_mem_attach(mctx, &hp->mctx); + + for (int i = 0; i < HP_MAX_THREADS; i++) { + hp->hp[i] = isc_mem_get(mctx, CLPAD * 2 * sizeof(hp->hp[i][0])); + hp->rl[i] = isc_mem_get(mctx, sizeof(*hp->rl[0])); + *hp->rl[i] = (retirelist_t) { .size = 0 }; + + for (int j = 0; j < hp->max_hps; j++) { + atomic_init(&hp->hp[i][j], 0); + } + } + + return (hp); +} + +void +isc_hp_destroy(isc_hp_t *hp) { + for (int i = 0; i < HP_MAX_THREADS; i++) { + isc_mem_put(hp->mctx, hp->hp[i], + CLPAD * 2 * sizeof(uintptr_t)); + + for (int j = 0; j < hp->rl[i]->size; j++) { + void *data = (void *)hp->rl[i]->list[j]; + hp->deletefunc(data); + } + + isc_mem_put(hp->mctx, hp->rl[i], sizeof(*hp->rl[0])); + } + + isc_mem_putanddetach(&hp->mctx, hp, sizeof(*hp)); +} + +void +isc_hp_clear(isc_hp_t *hp) { + for (int i = 0; i < hp->max_hps; i++) { + atomic_store_release(&hp->hp[tid()][i], 0); + } +} + +void isc_hp_clear_one(isc_hp_t *hp, int ihp) { + atomic_store_release(&hp->hp[tid()][ihp], 0); +} + +uintptr_t +isc_hp_protect(isc_hp_t *hp, int ihp, atomic_uintptr_t *atom) { + uintptr_t n = 0; + uintptr_t ret; + while ((ret = atomic_load(atom)) != n) { + atomic_store(&hp->hp[tid()][ihp], ret); + n = ret; + } + return (ret); +} + +uintptr_t +isc_hp_protect_ptr(isc_hp_t *hp, int ihp, atomic_uintptr_t ptr) { + atomic_store(&hp->hp[tid()][ihp], atomic_load(&ptr)); + return (atomic_load(&ptr)); +} + +uintptr_t +isc_hp_protect_release(isc_hp_t *hp, int ihp, atomic_uintptr_t ptr) { + atomic_store_release(&hp->hp[tid()][ihp], atomic_load(&ptr)); + return (atomic_load(&ptr)); +} + +void +isc_hp_retire(isc_hp_t *hp, uintptr_t ptr) { + hp->rl[tid()]->list[hp->rl[tid()]->size++] = ptr; + INSIST(hp->rl[tid()]->size < MAX_RETIRED); + + if (hp->rl[tid()]->size < HP_THRESHOLD_R) { + return; + } + + for (int iret = 0; iret < hp->rl[tid()]->size; iret++) { + uintptr_t obj = hp->rl[tid()]->list[iret]; + bool can_delete = true; + for (int itid = 0; + itid < HP_MAX_THREADS && can_delete; + itid++) + { + for (int ihp = hp->max_hps-1; ihp >= 0; ihp--) { + if (atomic_load(&hp->hp[itid][ihp]) == obj) { + can_delete = false; + break; + } + } + } + + if (can_delete) { + size_t bytes = (hp->rl[tid()]->size - iret) * + sizeof(hp->rl[tid()]->list[0]); + memmove(&hp->rl[tid()]->list[iret], + &hp->rl[tid()]->list[iret + 1], + bytes); + hp->rl[tid()]->size--; + hp->deletefunc((void *)obj); + } + } +} diff --git a/lib/isc/include/isc/Makefile.in b/lib/isc/include/isc/Makefile.in index a78f2c6d0b0..9b93fbe1f6d 100644 --- a/lib/isc/include/isc/Makefile.in +++ b/lib/isc/include/isc/Makefile.in @@ -23,7 +23,7 @@ HEADERS = aes.h app.h assertions.h atomic.h backtrace.h \ commandline.h counter.h crc64.h deprecated.h \ endian.h errno.h error.h event.h eventclass.h \ file.h formatcheck.h fsaccess.h fuzz.h \ - hash.h heap.h hex.h hmac.h ht.h httpd.h \ + hash.h heap.h hex.h hmac.h hp.h ht.h httpd.h \ interfaceiter.h iterated_hash.h \ lang.h lex.h lfsr.h lib.h likely.h list.h log.h \ magic.h md.h mem.h meminfo.h mutexblock.h \ diff --git a/lib/isc/include/isc/hp.h b/lib/isc/include/isc/hp.h new file mode 100644 index 00000000000..3cd542bd028 --- /dev/null +++ b/lib/isc/include/isc/hp.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +/* + * Hazard Pointer implementation. + * + * This work is based on C++ code available from: + * https://github.com/pramalhe/ConcurrencyFreaks/ + * + * Copyright (c) 2014-2016, Pedro Ramalhete, Andreia Correia + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of Concurrency Freaks nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 + * 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. + */ + +#include +#include +#include +#include +#include + +/*% + * Hazard pointers are a mechanism for protecting objects in memory + * from being deleted by other threads while in use. This allows + * safe lock-free data structures. + * + * This is an adaptation of the ConcurrencyFreaks implementation in C. + * More details available at https://github.com/pramalhe/ConcurrencyFreaks, + * in the file HazardPointers.hpp. + */ + +typedef void +(isc_hp_deletefunc_t)(void *); + +isc_hp_t * +isc_hp_new(isc_mem_t *mctx, size_t max_hps, isc_hp_deletefunc_t *deletefunc); +/*%< + * Create a new hazard pointer array of size 'max_hps' (or a reasonable + * default value if 'max_hps' is 0). The function 'deletefunc' will be + * used to delete objects protected by hazard pointers when it becomes + * safe to retire them. + */ + +void +isc_hp_destroy(isc_hp_t *hp); +/*%< + * Destroy a hazard pointer array and clean up all objects protected + * by hazard pointers. + */ + +void +isc_hp_clear(isc_hp_t *hp); +/*%< + * Clear all hazard pointers in the array for the current thread. + * + * Progress condition: wait-free bounded (by max_hps) + */ + +void +isc_hp_clear_one(isc_hp_t *hp, int ihp); +/*%< + * Clear a specified hazard pointer in the array for the current thread. + * + * Progress condition: wait-free population oblivious. + */ + +uintptr_t +isc_hp_protect(isc_hp_t *hp, int ihp, atomic_uintptr_t *atom); +/*%< + * Protect an object referenced by 'atom' with a hazard pointer for the + * current thread. + * + * Progress condition: lock-free. + */ + +uintptr_t +isc_hp_protect_ptr(isc_hp_t *hp, int ihp, atomic_uintptr_t ptr); +/*%< + * This returns the same value that is passed as ptr, which is sometimes + * useful. + * + * Progress condition: wait-free population oblivious. + */ + +uintptr_t +isc_hp_protect_release(isc_hp_t *hp, int ihp, atomic_uintptr_t ptr); +/*%< + * Same as isc_hp_protect_ptr(), but explicitly uses memory_order_release. + * + * Progress condition: wait-free population oblivious. + */ + +void +isc_hp_retire(isc_hp_t *hp, uintptr_t ptr); +/*%< + * Retire an object that is no longer in use by any thread, calling + * the delete function that was specified in isc_hp_new(). + * + * Progress condition: wait-free bounded (by the number of threads squared) + */ diff --git a/lib/isc/include/isc/types.h b/lib/isc/include/isc/types.h index 168076daf62..8b040198777 100644 --- a/lib/isc/include/isc/types.h +++ b/lib/isc/include/isc/types.h @@ -43,6 +43,8 @@ typedef ISC_LIST(isc_event_t) isc_eventlist_t; /*%< Event List */ typedef unsigned int isc_eventtype_t; /*%< Event Type */ typedef uint32_t isc_fsaccess_t; /*%< FS Access */ typedef struct isc_hash isc_hash_t; /*%< Hash */ +typedef struct isc_hp isc_hp_t; /*%< Hazard + pointer */ typedef struct isc_httpd isc_httpd_t; /*%< HTTP client */ typedef void (isc_httpdfree_t)(isc_buffer_t *, void *); /*%< HTTP free function */ typedef struct isc_httpdmgr isc_httpdmgr_t; /*%< HTTP manager */ diff --git a/lib/isc/win32/libisc.def.in b/lib/isc/win32/libisc.def.in index 6865f82f46e..db49db737a6 100644 --- a/lib/isc/win32/libisc.def.in +++ b/lib/isc/win32/libisc.def.in @@ -242,6 +242,13 @@ isc_heap_insert isc_hex_decodestring isc_hex_tobuffer isc_hex_totext +isc_hp_clear +isc_hp_destroy +isc_hp_protect +isc_hp_protect_ptr +isc_hp_protect_release +isc_hp_new +isc_hp_retire isc_hmac isc_hmac_new isc_hmac_free @@ -252,6 +259,13 @@ isc_hmac_final isc_hmac_get_md_type isc_hmac_get_size isc_hmac_get_block_size +isc_hp_new +isc_hp_destroy +isc_hp_clear +isc_hp_protect +isc_hp_protect_ptr +isc_hp_protect_release +isc_hp_retire isc_ht_add isc_ht_count isc_ht_delete diff --git a/lib/isc/win32/libisc.vcxproj.filters.in b/lib/isc/win32/libisc.vcxproj.filters.in index 22591f44db2..98993ee9b28 100644 --- a/lib/isc/win32/libisc.vcxproj.filters.in +++ b/lib/isc/win32/libisc.vcxproj.filters.in @@ -104,6 +104,9 @@ Library Header Files + + Library Header Files + Library Header Files @@ -500,6 +503,9 @@ Library Source Files + + Library Source Files + Library Source Files diff --git a/lib/isc/win32/libisc.vcxproj.in b/lib/isc/win32/libisc.vcxproj.in index 885d83c64e8..332aa2aaf15 100644 --- a/lib/isc/win32/libisc.vcxproj.in +++ b/lib/isc/win32/libisc.vcxproj.in @@ -311,6 +311,7 @@ copy InstallFiles ..\Build\Release\ + @@ -421,6 +422,7 @@ copy InstallFiles ..\Build\Release\ + diff --git a/util/copyrights b/util/copyrights index 9d5aa28c465..e07e9972b38 100644 --- a/util/copyrights +++ b/util/copyrights @@ -2148,6 +2148,7 @@ ./lib/isc/heap.c C 1997,1998,1999,2000,2001,2004,2005,2006,2007,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019 ./lib/isc/hex.c C 2000,2001,2002,2003,2004,2005,2007,2008,2013,2014,2015,2016,2018,2019 ./lib/isc/hmac.c C 2000,2001,2004,2005,2006,2007,2009,2011,2012,2013,2014,2015,2016,2017,2018,2019 +./lib/isc/hp.c C 2019 ./lib/isc/ht.c C 2016,2017,2018,2019 ./lib/isc/httpd.c C 2006,2007,2008,2010,2011,2012,2013,2014,2015,2016,2017,2018,2019 ./lib/isc/include/Makefile.in MAKE 1998,1999,2000,2001,2004,2007,2012,2014,2016,2018,2019 @@ -2179,6 +2180,7 @@ ./lib/isc/include/isc/heap.h C 1997,1998,1999,2000,2001,2004,2005,2006,2007,2009,2012,2016,2018,2019 ./lib/isc/include/isc/hex.h C 2000,2001,2004,2005,2006,2007,2008,2016,2018,2019 ./lib/isc/include/isc/hmac.h C 2000,2001,2004,2005,2006,2007,2009,2011,2012,2013,2014,2015,2016,2017,2018,2019 +./lib/isc/include/isc/hp.h C 2019 ./lib/isc/include/isc/ht.h C 2016,2017,2018,2019 ./lib/isc/include/isc/httpd.h C 2006,2007,2008,2014,2016,2018,2019 ./lib/isc/include/isc/interfaceiter.h C 1999,2000,2001,2004,2005,2006,2007,2016,2018,2019