Files
bind9/lib/isc/hp.c
T

188 lines
5.9 KiB
C

/*
* 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.
*/
/*
* 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 <COPYRIGHT HOLDER> 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 <isc/atomic.h>
#include <isc/hp.h>
#include <isc/string.h>
#include <isc/mem.h>
#include <isc/util.h>
#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 */
#define MAX_RETIRED (HP_MAX_THREADS * HP_MAX_HPS) /* Maximum number of retired objects per thread */
struct isc__hp_rl {
int size;
uintptr_t list[MAX_RETIRED];
};
struct isc_hp {
int max_hps;
int max_threads;
isc_mem_t *mctx;
atomic_uintptr_t *hp[HP_MAX_THREADS];
struct isc__hp_rl *rl[HP_MAX_THREADS*CLPAD];
isc_hp_deletefunc_t *deletefunc;
};
isc_hp_t *
isc_hp_new(isc_mem_t *mctx, size_t max_hps, size_t max_threads, isc_hp_deletefunc_t *deletefunc) {
isc_hp_t *hp = isc_mem_get(mctx, sizeof(*hp));
hp->max_hps = HP_MAX_HPS;
hp->max_threads = HP_MAX_THREADS;
isc_mem_attach(mctx, &hp->mctx);
*hp = (isc_hp_t){ .max_hps = max_hps,
.max_threads = max_threads,
.deletefunc = deletefunc };
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*CLPAD] = isc_mem_get(mctx, sizeof(hp->rl[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*CLPAD]->size; j++) {
void *data = (void *)hp->rl[i*CLPAD]->list[j];
hp->deletefunc(data);
}
}
isc_mem_putanddetach(&hp->mctx, hp, sizeof(*hp));
}
void
isc_hp_clear(isc_hp_t *hp, const int tid) {
REQUIRE(tid < hp->max_threads);
for (int i = 0; i < hp->max_hps; i++) {
atomic_store_release(&hp->hp[tid][i], 0);
}
}
/**
* Progress Condition: wait-free population oblivious
*/
void isc_hp_clear_one(isc_hp_t *hp, int ihp, const int tid) {
atomic_store_release(&hp->hp[tid][ihp], 0);
}
/**
* Progress Condition: lock-free
*/
uintptr_t
isc_hp_protect(isc_hp_t *hp, int ihp, const atomic_uintptr_t atom, const int tid) {
uintptr_t n = 0;
uintptr_t ret;
while ((ret = atomic_load(&atom)) != n) {
atomic_store(&hp->hp[tid][ihp], ret);
n = ret;
}
return (ret);
}
/**
* 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_ptr(isc_hp_t *hp, int ihp, const atomic_uintptr_t ptr, const int tid) {
atomic_store(&hp->hp[tid][ihp], ptr);
return (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, const atomic_uintptr_t ptr, const int tid) {
atomic_store_release(&hp->hp[tid][ihp], ptr);
return (ptr);
}
/**
* Progress Condition: wait-free bounded (by the number of threads squared)
*/
void
isc_hp_retire(isc_hp_t *hp, const atomic_uintptr_t ptr, const int tid) {
hp->rl[tid*CLPAD]->list[hp->rl[tid*CLPAD]->size++] = ptr;
INSIST(hp->rl[tid*CLPAD]->size < MAX_RETIRED);
if (hp->rl[tid*CLPAD]->size < HP_THRESHOLD_R) {
return;
}
for (int iret = 0; iret < hp->rl[tid*CLPAD]->size;) {
uintptr_t obj = hp->rl[tid*CLPAD]->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*CLPAD]->size - iret) *
sizeof(hp->rl[tid*CLPAD]->list[0]);
memmove(&hp->rl[tid*CLPAD]->list[iret],
&hp->rl[tid*CLPAD]->list[iret + 1],
bytes);
hp->rl[tid*CLPAD]->size--;
hp->deletefunc((void *)obj);
}
iret++;
}
}