Remove unused code.
This commit is contained in:
parent
30b1c6a4db
commit
a0113640cb
757
src/klist.c
757
src/klist.c
@ -1,757 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 Andrew Smith - BlackArrow Ltd
|
||||
* Copyright 2015-2016 Andrew Smith
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at your option)
|
||||
* any later version. See COPYING for more details.
|
||||
*/
|
||||
|
||||
#include "klist.h"
|
||||
|
||||
const char *tree_node_list_name = "TreeNodes";
|
||||
|
||||
#if LOCK_CHECK
|
||||
bool disable_checks = false;
|
||||
bool check_locks = true;
|
||||
const char *thread_noname = "UNSET";
|
||||
int next_thread_id = 0;
|
||||
__thread int my_thread_id = -1;
|
||||
__thread char *my_thread_name = NULL;
|
||||
__thread bool my_check_locks = true;
|
||||
|
||||
bool auto_check_deadlocks = true;
|
||||
// Must be false to start with
|
||||
bool check_deadlocks = false;
|
||||
__thread int my_locks[MAX_LOCKDEPTH];
|
||||
__thread const char *my_locks_n[MAX_LOCKDEPTH];
|
||||
__thread const char *my_locks_fl[MAX_LOCKDEPTH];
|
||||
__thread const char *my_locks_f[MAX_LOCKDEPTH];
|
||||
__thread int my_locks_l[MAX_LOCKDEPTH];
|
||||
__thread int my_lock_level = 0;
|
||||
__thread bool my_check_deadlocks = true;
|
||||
#endif
|
||||
// Required for cmd_stats
|
||||
bool lock_check_init = false;
|
||||
cklock_t lock_check_lock;
|
||||
K_LISTS *all_klists;
|
||||
|
||||
#define _CHKLIST(_list, _name) do {\
|
||||
if (!_list) { \
|
||||
quithere(1, "%s() can't process a NULL " _name \
|
||||
KLIST_FFL, \
|
||||
__func__, KLIST_FFL_PASS); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define CHKLIST(__list) _CHKLIST(__list, "list")
|
||||
|
||||
#define CHKLS(__list) _CHKLIST(__list, "list/store")
|
||||
|
||||
#define _CHKITEM(_item, _list, _name) do {\
|
||||
if (!_item) { \
|
||||
quithere(1, "%s() can't process a NULL %s " _name \
|
||||
KLIST_FFL, \
|
||||
__func__, _list->name, \
|
||||
KLIST_FFL_PASS); \
|
||||
} \
|
||||
} while (0);
|
||||
|
||||
#define CHKITEM(__item, __list) _CHKITEM(__item, __list, "item")
|
||||
|
||||
void _dsp_kstore(K_STORE *store, char *filename, char *msg, KLIST_FFL_ARGS)
|
||||
{
|
||||
K_ITEM *item;
|
||||
FILE *stream;
|
||||
struct tm tm;
|
||||
time_t now_t;
|
||||
char stamp[128];
|
||||
|
||||
if (!(store->master->dsp_func)) {
|
||||
quithere(1, "List %s has no dsp_func" KLIST_FFL,
|
||||
store->master->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
now_t = time(NULL);
|
||||
localtime_r(&now_t, &tm);
|
||||
snprintf(stamp, sizeof(stamp),
|
||||
"[%d-%02d-%02d %02d:%02d:%02d]",
|
||||
tm.tm_year + 1900,
|
||||
tm.tm_mon + 1,
|
||||
tm.tm_mday,
|
||||
tm.tm_hour,
|
||||
tm.tm_min,
|
||||
tm.tm_sec);
|
||||
|
||||
stream = fopen(filename, "ae");
|
||||
if (!stream)
|
||||
{
|
||||
fprintf(stderr, "%s %s() failed to open '%s' (%d) %s",
|
||||
stamp, __func__, filename, errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
|
||||
if (msg)
|
||||
fprintf(stream, "%s %s\n", stamp, msg);
|
||||
else
|
||||
fprintf(stream, "%s Dump of store '%s':\n", stamp, store->master->name);
|
||||
|
||||
if (store->count > 0)
|
||||
{
|
||||
K_RLOCK(store->master);
|
||||
|
||||
item = store->head;
|
||||
while (item)
|
||||
{
|
||||
store->master->dsp_func(item, stream);
|
||||
item = item->next;
|
||||
}
|
||||
K_RUNLOCK(store->master);
|
||||
|
||||
fprintf(stream, "End\n\n");
|
||||
}
|
||||
else
|
||||
fprintf(stream, "Empty kstore\n\n");
|
||||
|
||||
fclose(stream);
|
||||
}
|
||||
|
||||
static void k_alloc_items(K_LIST *list, KLIST_FFL_ARGS)
|
||||
{
|
||||
K_ITEM *item;
|
||||
void *data;
|
||||
int allocate, i;
|
||||
|
||||
CHKLIST(list);
|
||||
|
||||
if (list->is_store) {
|
||||
quithere(1, "List %s store can't %s()" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (list->limit > 0 && list->total >= list->limit)
|
||||
return;
|
||||
|
||||
allocate = list->allocate;
|
||||
if (list->limit > 0 && (list->total + allocate) > list->limit)
|
||||
allocate = list->limit - list->total;
|
||||
|
||||
list->item_mem_count++;
|
||||
if (!(list->item_memory = realloc(list->item_memory,
|
||||
list->item_mem_count * sizeof(*(list->item_memory))))) {
|
||||
quithere(1, "List %s item_memory failed to realloc count=%d",
|
||||
list->name, list->item_mem_count);
|
||||
}
|
||||
item = calloc(allocate, sizeof(*item));
|
||||
if (!item) {
|
||||
quithere(1, "List %s failed to calloc %d new items - total was %d, limit was %d",
|
||||
list->name, allocate, list->total, list->limit);
|
||||
}
|
||||
list->item_memory[list->item_mem_count - 1] = (void *)item;
|
||||
|
||||
item[0].name = list->name;
|
||||
item[0].prev = NULL;
|
||||
item[0].next = &(item[1]);
|
||||
for (i = 1; i < allocate-1; i++) {
|
||||
item[i].name = list->name;
|
||||
item[i].prev = &item[i-1];
|
||||
item[i].next = &item[i+1];
|
||||
}
|
||||
item[allocate-1].name = list->name;
|
||||
item[allocate-1].prev = &(item[allocate-2]);
|
||||
item[allocate-1].next = NULL;
|
||||
|
||||
list->head = item;
|
||||
if (list->do_tail)
|
||||
list->tail = &(item[allocate-1]);
|
||||
|
||||
list->data_mem_count++;
|
||||
if (!(list->data_memory = realloc(list->data_memory,
|
||||
list->data_mem_count * sizeof(*(list->data_memory))))) {
|
||||
quithere(1, "List %s data_memory failed to realloc count=%d",
|
||||
list->name, list->data_mem_count);
|
||||
}
|
||||
data = calloc(allocate, list->siz);
|
||||
if (!data) {
|
||||
quithere(1, "List %s failed to calloc %d new data - total was %d, limit was %d",
|
||||
list->name, allocate, list->total, list->limit);
|
||||
}
|
||||
list->data_memory[list->data_mem_count - 1] = data;
|
||||
|
||||
item = list->head;
|
||||
while (item) {
|
||||
item->data = data;
|
||||
data += list->siz;
|
||||
item = item->next;
|
||||
}
|
||||
|
||||
list->total += allocate;
|
||||
list->count = allocate;
|
||||
list->count_up = allocate;
|
||||
}
|
||||
|
||||
K_STORE *_k_new_store(K_LIST *list, bool gotlock, KLIST_FFL_ARGS)
|
||||
{
|
||||
K_STORE *store;
|
||||
|
||||
CHKLIST(list);
|
||||
|
||||
store = calloc(1, sizeof(*store));
|
||||
if (!store)
|
||||
quithere(1, "Failed to calloc store for %s", list->name);
|
||||
|
||||
store->master = list;
|
||||
store->is_store = true;
|
||||
store->lock = NULL;
|
||||
store->name = list->name;
|
||||
store->do_tail = list->do_tail;
|
||||
store->prev_store = NULL;
|
||||
// Only tracked for lists with a lock
|
||||
if (store->master->lock == NULL) {
|
||||
store->next_store = NULL;
|
||||
store->master->stores++;
|
||||
} else {
|
||||
if (!gotlock)
|
||||
K_WLOCK(list);
|
||||
// In the master list, next is the head
|
||||
if (list->next_store)
|
||||
list->next_store->prev_store = store;
|
||||
store->next_store = list->next_store;
|
||||
list->next_store = store;
|
||||
list->stores++;
|
||||
if (!gotlock)
|
||||
K_WUNLOCK(list);
|
||||
}
|
||||
|
||||
return store;
|
||||
}
|
||||
|
||||
K_LIST *_k_new_list(const char *name, size_t siz, int allocate, int limit,
|
||||
bool do_tail, bool lock_only, bool without_lock,
|
||||
bool local_list, const char *name2, int cull_limit,
|
||||
KLIST_FFL_ARGS)
|
||||
{
|
||||
K_LIST *list;
|
||||
|
||||
if (allocate < 1)
|
||||
quithere(1, "Invalid new list %s with allocate %d must be > 0", name, allocate);
|
||||
|
||||
if (limit < 0)
|
||||
quithere(1, "Invalid new list %s with limit %d must be >= 0", name, limit);
|
||||
|
||||
/* after culling, the first block of items are again allocated,
|
||||
* so there's no point culling a single block of items */
|
||||
if (cull_limit > 0 && cull_limit <= allocate)
|
||||
quithere(1, "Invalid new list %s with cull_limit %d must be > allocate (%d)", name, cull_limit, allocate);
|
||||
|
||||
list = calloc(1, sizeof(*list));
|
||||
if (!list)
|
||||
quithere(1, "Failed to calloc list %s", name);
|
||||
|
||||
list->master = list;
|
||||
list->is_store = false;
|
||||
list->is_lock_only = lock_only;
|
||||
list->local_list = local_list;
|
||||
|
||||
if (without_lock)
|
||||
list->lock = NULL;
|
||||
else {
|
||||
list->lock = calloc(1, sizeof(*(list->lock)));
|
||||
if (!(list->lock))
|
||||
quithere(1, "Failed to calloc lock for list %s", name);
|
||||
|
||||
cklock_init(list->lock);
|
||||
}
|
||||
|
||||
list->name = name;
|
||||
list->name2 = name2;
|
||||
list->siz = siz;
|
||||
list->allocate = allocate;
|
||||
list->limit = limit;
|
||||
list->do_tail = do_tail;
|
||||
list->cull_limit = cull_limit;
|
||||
list->next_store = list->prev_store = NULL;
|
||||
|
||||
if (!(list->is_lock_only))
|
||||
k_alloc_items(list, KLIST_FFL_PASS);
|
||||
|
||||
/* Don't want to keep track of short lived (tree) lists
|
||||
* since they wont use locking anyway */
|
||||
if (!list->local_list) {
|
||||
K_LISTS *klists;
|
||||
|
||||
// not locked :P
|
||||
if (!lock_check_init) {
|
||||
quitfrom(1, file, func, line,
|
||||
"in %s(), lock_check_lock has not been initialised!",
|
||||
__func__);
|
||||
}
|
||||
|
||||
klists = calloc(1, sizeof(*klists));
|
||||
if (!klists)
|
||||
quithere(1, "Failed to calloc klists %s", name);
|
||||
|
||||
klists->klist = list;
|
||||
ck_wlock(&lock_check_lock);
|
||||
klists->next = all_klists;
|
||||
all_klists = klists;
|
||||
ck_wunlock(&lock_check_lock);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
/*
|
||||
* Unlink and return the head of the list
|
||||
* If the list is empty:
|
||||
* 1) If it's a store - return NULL
|
||||
* 2) alloc a new list and return the head -
|
||||
* which is NULL if the list limit has been reached
|
||||
*/
|
||||
K_ITEM *_k_unlink_head(K_LIST *list, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
K_ITEM *item;
|
||||
|
||||
CHKLS(list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
if (!(list->head) && !(list->is_store))
|
||||
k_alloc_items(list, KLIST_FFL_PASS);
|
||||
|
||||
if (!(list->head))
|
||||
return NULL;
|
||||
|
||||
item = list->head;
|
||||
list->head = item->next;
|
||||
if (list->head)
|
||||
list->head->prev = NULL;
|
||||
else {
|
||||
if (list->do_tail)
|
||||
list->tail = NULL;
|
||||
}
|
||||
|
||||
item->prev = item->next = NULL;
|
||||
|
||||
list->count--;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// Zeros the head returned
|
||||
K_ITEM *_k_unlink_head_zero(K_LIST *list, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
K_ITEM *item;
|
||||
|
||||
CHKLS(list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
item = _k_unlink_head(list, false, KLIST_FFL_PASS);
|
||||
|
||||
if (item)
|
||||
memset(item->data, 0, list->siz);
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
// Returns NULL if empty
|
||||
K_ITEM *_k_unlink_tail(K_LIST *list, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
K_ITEM *item;
|
||||
|
||||
CHKLS(list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
if (!(list->do_tail)) {
|
||||
quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (!(list->tail))
|
||||
return NULL;
|
||||
|
||||
item = list->tail;
|
||||
list->tail = item->prev;
|
||||
if (list->tail)
|
||||
list->tail->next = NULL;
|
||||
else
|
||||
list->head = NULL;
|
||||
|
||||
item->prev = item->next = NULL;
|
||||
|
||||
list->count--;
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
#define CHKCULL(_list) \
|
||||
do { \
|
||||
if (!((_list)->is_store) && !((_list)->is_lock_only) && \
|
||||
(_list)->cull_limit > 0 && \
|
||||
(_list)->count == (_list)->total && \
|
||||
(_list)->total >= (_list)->cull_limit) { \
|
||||
k_cull_list(_list, file, func, line); \
|
||||
} \
|
||||
} while(0);
|
||||
|
||||
static void k_cull_list(K_LIST *list, KLIST_FFL_ARGS)
|
||||
{
|
||||
int i;
|
||||
|
||||
CHKLIST(list);
|
||||
_LIST_WRITE(list, true, file, func, line);
|
||||
|
||||
if (list->is_store) {
|
||||
quithere(1, "List %s can't %s() a store" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (list->is_lock_only) {
|
||||
quithere(1, "List %s can't %s() a lock_only" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (list->count != list->total) {
|
||||
quithere(1, "List %s can't %s() a list in use" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
for (i = 0; i < list->item_mem_count; i++)
|
||||
free(list->item_memory[i]);
|
||||
free(list->item_memory);
|
||||
list->item_memory = NULL;
|
||||
list->item_mem_count = 0;
|
||||
|
||||
for (i = 0; i < list->data_mem_count; i++)
|
||||
free(list->data_memory[i]);
|
||||
free(list->data_memory);
|
||||
list->data_memory = NULL;
|
||||
list->data_mem_count = 0;
|
||||
|
||||
list->total = list->count = list->count_up = 0;
|
||||
list->head = list->tail = NULL;
|
||||
|
||||
list->cull_count++;
|
||||
|
||||
k_alloc_items(list, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
void _k_add_head(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
CHKLS(list);
|
||||
CHKITEM(item, list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
if (item->name != list->name) {
|
||||
quithere(1, "List %s can't %s() a %s item" KLIST_FFL,
|
||||
list->name, __func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (item->prev || item->next) {
|
||||
quithere(1, "%s() added item %s still linked" KLIST_FFL,
|
||||
__func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
item->prev = NULL;
|
||||
item->next = list->head;
|
||||
if (list->head)
|
||||
list->head->prev = item;
|
||||
|
||||
list->head = item;
|
||||
|
||||
if (list->do_tail) {
|
||||
if (!(list->tail))
|
||||
list->tail = item;
|
||||
}
|
||||
|
||||
list->count++;
|
||||
list->count_up++;
|
||||
|
||||
CHKCULL(list);
|
||||
}
|
||||
|
||||
/* slows it down (of course) - only for debugging
|
||||
void _k_free_head(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
CHKLS(list);
|
||||
CHKITEM(item, list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
memset(item->data, 0xff, list->siz);
|
||||
_k_add_head(list, item, KLIST_FFL_PASS);
|
||||
}
|
||||
*/
|
||||
|
||||
void _k_add_tail(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
CHKLS(list);
|
||||
CHKITEM(item, list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
if (item->name != list->name) {
|
||||
quithere(1, "List %s can't %s() a %s item" KLIST_FFL,
|
||||
list->name, __func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (!(list->do_tail)) {
|
||||
quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (item->prev || item->next) {
|
||||
quithere(1, "%s() added item %s still linked" KLIST_FFL,
|
||||
__func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
item->prev = list->tail;
|
||||
item->next = NULL;
|
||||
if (list->tail)
|
||||
list->tail->next = item;
|
||||
|
||||
list->tail = item;
|
||||
|
||||
if (!(list->head))
|
||||
list->head = item;
|
||||
|
||||
list->count++;
|
||||
list->count_up++;
|
||||
|
||||
CHKCULL(list);
|
||||
}
|
||||
|
||||
// Insert item into the list next after 'after'
|
||||
void _k_insert_after(K_LIST *list, K_ITEM *item, K_ITEM *after, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
CHKLS(list);
|
||||
CHKITEM(item, list);
|
||||
_CHKITEM(item, after, "after");
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
if (item->name != list->name) {
|
||||
quithere(1, "List %s can't %s() a %s item" KLIST_FFL,
|
||||
list->name, __func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (after->name != list->name) {
|
||||
quithere(1, "List %s can't %s() a %s after" KLIST_FFL,
|
||||
list->name, __func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (item->prev || item->next) {
|
||||
quithere(1, "%s() added item %s still linked" KLIST_FFL,
|
||||
__func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
item->prev = after;
|
||||
item->next = after->next;
|
||||
if (item->next)
|
||||
item->next->prev = item;
|
||||
after->next = item;
|
||||
|
||||
if (list->do_tail) {
|
||||
if (list->tail == after)
|
||||
list->tail = item;
|
||||
}
|
||||
|
||||
list->count++;
|
||||
list->count_up++;
|
||||
|
||||
// no point checking cull since this wouldn't be an _free list
|
||||
}
|
||||
|
||||
void _k_unlink_item(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
CHKLS(list);
|
||||
CHKITEM(item, list);
|
||||
_LIST_WRITE(list, chklock, file, func, line);
|
||||
|
||||
if (item->name != list->name) {
|
||||
quithere(1, "List %s can't %s() a %s item" KLIST_FFL,
|
||||
list->name, __func__, item->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (item->prev)
|
||||
item->prev->next = item->next;
|
||||
|
||||
if (item->next)
|
||||
item->next->prev = item->prev;
|
||||
|
||||
if (list->head == item)
|
||||
list->head = item->next;
|
||||
|
||||
if (list->do_tail) {
|
||||
if (list->tail == item)
|
||||
list->tail = item->prev;
|
||||
}
|
||||
|
||||
item->prev = item->next = NULL;
|
||||
|
||||
list->count--;
|
||||
}
|
||||
|
||||
void _k_list_transfer_to_head(K_LIST *from, K_LIST *to, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
_CHKLIST(from, "from list/store");
|
||||
_CHKLIST(to, "to list/store");
|
||||
|
||||
if (from->name != to->name) {
|
||||
quithere(1, "List %s can't %s() to a %s list" KLIST_FFL,
|
||||
from->name, __func__, to->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
// from and to are the same lock
|
||||
_LIST_WRITE(to, chklock, file, func, line);
|
||||
|
||||
if (!(from->do_tail)) {
|
||||
quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL,
|
||||
from->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (!(from->head))
|
||||
return;
|
||||
|
||||
if (to->head)
|
||||
to->head->prev = from->tail;
|
||||
else
|
||||
to->tail = from->tail;
|
||||
|
||||
from->tail->next = to->head;
|
||||
to->head = from->head;
|
||||
|
||||
from->head = from->tail = NULL;
|
||||
to->count += from->count;
|
||||
from->count = 0;
|
||||
to->count_up += from->count_up;
|
||||
from->count_up = 0;
|
||||
|
||||
CHKCULL(to);
|
||||
}
|
||||
|
||||
void _k_list_transfer_to_tail(K_LIST *from, K_LIST *to, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS)
|
||||
{
|
||||
_CHKLIST(from, "from list/store");
|
||||
_CHKLIST(to, "to list/store");
|
||||
|
||||
if (from->name != to->name) {
|
||||
quithere(1, "List %s can't %s() to a %s list" KLIST_FFL,
|
||||
from->name, __func__, to->name, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
// from and to are the same lock
|
||||
_LIST_WRITE(to, chklock, file, func, line);
|
||||
|
||||
if (!(from->do_tail)) {
|
||||
quithere(1, "List %s can't %s() - do_tail is false" KLIST_FFL,
|
||||
from->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (!(from->head))
|
||||
return;
|
||||
|
||||
if (to->tail)
|
||||
to->tail->next = from->head;
|
||||
else
|
||||
to->head = from->head;
|
||||
|
||||
from->head->prev = to->tail;
|
||||
to->tail = from->tail;
|
||||
|
||||
from->head = from->tail = NULL;
|
||||
to->count += from->count;
|
||||
from->count = 0;
|
||||
to->count_up += from->count_up;
|
||||
from->count_up = 0;
|
||||
|
||||
CHKCULL(to);
|
||||
}
|
||||
|
||||
K_LIST *_k_free_list(K_LIST *list, KLIST_FFL_ARGS)
|
||||
{
|
||||
int i;
|
||||
|
||||
CHKLIST(list);
|
||||
|
||||
if (list->is_store) {
|
||||
quithere(1, "List %s can't %s() a store" KLIST_FFL,
|
||||
list->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
for (i = 0; i < list->item_mem_count; i++)
|
||||
free(list->item_memory[i]);
|
||||
free(list->item_memory);
|
||||
|
||||
for (i = 0; i < list->data_mem_count; i++)
|
||||
free(list->data_memory[i]);
|
||||
free(list->data_memory);
|
||||
|
||||
if (list->lock) {
|
||||
cklock_destroy(list->lock);
|
||||
|
||||
free(list->lock);
|
||||
}
|
||||
|
||||
// local_list lists are not stored in all_klists
|
||||
if (!list->local_list) {
|
||||
K_LISTS *klists, *klists_prev = NULL;
|
||||
|
||||
// not locked :P
|
||||
if (!lock_check_init) {
|
||||
quitfrom(1, file, func, line,
|
||||
"in %s(), lock_check_lock has not been initialised!",
|
||||
__func__);
|
||||
}
|
||||
|
||||
ck_wlock(&lock_check_lock);
|
||||
klists = all_klists;
|
||||
while (klists && klists->klist != list) {
|
||||
klists_prev = klists;
|
||||
klists = klists->next;
|
||||
}
|
||||
if (!klists) {
|
||||
quitfrom(1, file, func, line,
|
||||
"in %s(), list %s not in klists",
|
||||
__func__, list->name);
|
||||
} else {
|
||||
if (klists_prev)
|
||||
klists_prev->next = klists->next;
|
||||
else
|
||||
all_klists = klists->next;
|
||||
free(klists);
|
||||
}
|
||||
ck_wunlock(&lock_check_lock);
|
||||
}
|
||||
|
||||
free(list);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
K_STORE *_k_free_store(K_STORE *store, KLIST_FFL_ARGS)
|
||||
{
|
||||
_CHKLIST(store, "store");
|
||||
|
||||
if (!(store->is_store)) {
|
||||
quithere(1, "Store %s can't %s() the list" KLIST_FFL,
|
||||
store->name, __func__, KLIST_FFL_PASS);
|
||||
}
|
||||
|
||||
if (store->master->lock == NULL)
|
||||
store->master->stores--;
|
||||
else {
|
||||
K_WLOCK(store->master);
|
||||
// unlink store from the list
|
||||
if (store->prev_store)
|
||||
store->prev_store->next_store = store->next_store;
|
||||
if (store->next_store)
|
||||
store->next_store->prev_store = store->prev_store;
|
||||
// correct the head if we are the head
|
||||
if (store->master->next_store == store)
|
||||
store->master->next_store = store->next_store;
|
||||
store->master->stores--;
|
||||
K_WUNLOCK(store->master);
|
||||
}
|
||||
|
||||
free(store);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
721
src/klist.h
721
src/klist.h
@ -1,721 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2014 Andrew Smith - BlackArrow Ltd
|
||||
* Copyright 2015-2016 Andrew Smith
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at your option)
|
||||
* any later version. See COPYING for more details.
|
||||
*/
|
||||
|
||||
#ifndef KLIST_H
|
||||
#define KLIST_H
|
||||
|
||||
#include "libckpool.h"
|
||||
|
||||
#define quithere(status, fmt, ...) \
|
||||
quitfrom(status, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define KLIST_FFL " - from %s %s() line %d"
|
||||
#define KLIST_SFFL " - from %s %s():%d"
|
||||
#define KLIST_AFFL "at %s %s():%d"
|
||||
#define KLIST_FFL_HERE __FILE__, __func__, __LINE__
|
||||
#define KLIST_FFL_PASS file, func, line
|
||||
#define KLIST_FFL_ARGS __maybe_unused const char *file, \
|
||||
__maybe_unused const char *func, \
|
||||
__maybe_unused const int line
|
||||
|
||||
extern const char *tree_node_list_name;
|
||||
|
||||
/* Code to check the state of locks being requested and also check
|
||||
* the state of locks when accessing the klist or ktree
|
||||
* You can disable it with ckpmsg 'locks.ID.locks' so you can compare
|
||||
* CPU usage with and later without it
|
||||
* (or just completely disable it by defining LOCK_CHECK 0 below)
|
||||
*
|
||||
* If we already hold any lock we ask for, it will report the duplication.
|
||||
* Duplication of read locks wont fail the code, but they do represent
|
||||
* code that should be 'fixed'
|
||||
*
|
||||
* If klist/ktree access expects to have a lock, but doesn't have the
|
||||
* required lock, it will report this bug
|
||||
*
|
||||
* Any errors found will set my_check_locks false before reporting the error,
|
||||
* to avoid floods of error messages or crashes due to unexpected states
|
||||
* of the thread's lock info
|
||||
* i.e. you can only find one bug per thread each time you run ckdb
|
||||
* ... they should be rare if ever ... since I used this to attempt to
|
||||
* find them :)
|
||||
*/
|
||||
#define LOCK_CHECK 1
|
||||
|
||||
/* Deadlock prediction is quite simple:
|
||||
* alloc_storage gives each K_LIST a unique 'lock priority' order greater
|
||||
* than 1 with the lowest being PRIO_TERMINAL=1
|
||||
* (thus until alloc_storage is run, no deadlock prediction is enabled)
|
||||
* If any code locks a K_LIST with a 'lock priority' higher than one it
|
||||
* already holds, then that means it could result in a deadlock,
|
||||
* since the reverse priority order is expected
|
||||
*
|
||||
* This is implemented by checking the 'lock priority'
|
||||
* each time we attempt to take out a lock within a lock
|
||||
* It simply checks the previous lock held to see if it's 'lock priority'
|
||||
* is lower than the new lock thus marginal CPU increase is only noticeable
|
||||
* on multi-level locks
|
||||
*
|
||||
* Any deadlocks predicted will set my_check_deadlocks false before reporting
|
||||
* the error, to avoid possible floods of repeated error messages
|
||||
* i.e. you can only find one deadlock per thread each time you run ckdb
|
||||
* ... they should be rare if ever ... since I used this to attempt to
|
||||
* find them :)
|
||||
*/
|
||||
|
||||
/* Deadlock prediction is part of LOCK_CHECK coz it uses the CHECK_LOCK() macro
|
||||
* If you want only deadlock checking, edit klist.c and set check_locks
|
||||
* default to false,
|
||||
* or turn off check_locks during ckdb startup with a ckpmsg 'locks.ID.locks'
|
||||
* If you turn deadlock prediction on with ckpmsg 'locks.1.deadlocks=y'
|
||||
* it will not re-enable it for any thread that has already predicted
|
||||
* a deadlock */
|
||||
|
||||
#if LOCK_CHECK
|
||||
// Disable all lock checks permanently if thread limits are exceeded
|
||||
extern bool disable_checks;
|
||||
|
||||
// We disable lock checking if an error is encountered
|
||||
extern bool check_locks;
|
||||
/* Maximum number of threads preallocated
|
||||
* This allows access to the lock tables without
|
||||
* using any locks */
|
||||
#define MAX_THREADS 128
|
||||
extern const char *thread_noname;
|
||||
extern int next_thread_id;
|
||||
extern __thread int my_thread_id;
|
||||
extern __thread char *my_thread_name;
|
||||
extern __thread bool my_check_locks;
|
||||
|
||||
// This decides if alloc_storage will set 'check_deadlocks' after it's setup
|
||||
extern bool auto_check_deadlocks;
|
||||
// This decides if deadlock prediction is happening
|
||||
extern bool check_deadlocks;
|
||||
// It should never get to 16 unless there's a bug
|
||||
#define MAX_LOCKDEPTH 16
|
||||
extern __thread int my_locks[MAX_LOCKDEPTH];
|
||||
extern __thread const char *my_locks_n[MAX_LOCKDEPTH];
|
||||
extern __thread const char *my_locks_fl[MAX_LOCKDEPTH];
|
||||
extern __thread const char *my_locks_f[MAX_LOCKDEPTH];
|
||||
extern __thread int my_locks_l[MAX_LOCKDEPTH];
|
||||
extern __thread int my_lock_level;
|
||||
extern __thread bool my_check_deadlocks;
|
||||
|
||||
extern const char *nullstr;
|
||||
#endif
|
||||
|
||||
typedef struct k_item {
|
||||
const char *name;
|
||||
struct k_item *prev;
|
||||
struct k_item *next;
|
||||
void *data;
|
||||
} K_ITEM;
|
||||
|
||||
#if LOCK_CHECK
|
||||
typedef struct k_lock {
|
||||
int r_count;
|
||||
int w_count;
|
||||
const char *first_held;
|
||||
const char *file;
|
||||
const char *func;
|
||||
int line;
|
||||
} K_LOCK;
|
||||
#endif
|
||||
|
||||
typedef struct k_list {
|
||||
const char *name;
|
||||
const char *name2; // name of the tree if it's a tree node list
|
||||
struct k_list *master;
|
||||
bool is_store;
|
||||
bool is_lock_only; // a lock emulating a list for lock checking
|
||||
bool local_list; // local (tree) lists doesn't need lock checking at all
|
||||
cklock_t *lock; // NULL for tree lists
|
||||
struct k_item *head;
|
||||
struct k_item *tail;
|
||||
size_t siz; // item data size
|
||||
int total; // total allocated
|
||||
int count; // in this list
|
||||
int count_up; // incremented every time one is added
|
||||
int allocate; // number to intially allocate and each time we run out
|
||||
int limit; // total limit - 0 means unlimited
|
||||
bool do_tail; // track the tail?
|
||||
int item_mem_count; // how many item memory buffers have been allocated
|
||||
void **item_memory; // allocated item memory buffers
|
||||
int data_mem_count; // how many item data memory buffers have been allocated
|
||||
void **data_memory; // allocated item data memory buffers
|
||||
void (*dsp_func)(K_ITEM *, FILE *); // optional data display to a file
|
||||
int cull_limit; // <1 means don't cull, otherwise total to cull at
|
||||
int cull_count; // number of times culled
|
||||
uint64_t ram; // ram allocated for data pointers - code must manage it
|
||||
struct k_list *next_store; // list of all stores - the head is next_store in the list master
|
||||
struct k_list *prev_store; // the stores themselves have their prev and next
|
||||
int stores; // how many stores it currently has
|
||||
#if LOCK_CHECK
|
||||
// Since each thread has it's own k_lock no locking is required on this
|
||||
K_LOCK k_lock[MAX_THREADS];
|
||||
// 0=unset=an error, >=1 is the priority - bigger=higher priority
|
||||
int deadlock_priority;
|
||||
#endif
|
||||
} K_LIST;
|
||||
|
||||
// Required for cmd_stats
|
||||
extern bool lock_check_init;
|
||||
extern cklock_t lock_check_lock;
|
||||
typedef struct k_lists {
|
||||
K_LIST *klist;
|
||||
struct k_lists *next;
|
||||
} K_LISTS;
|
||||
extern K_LISTS *all_klists;
|
||||
|
||||
/*
|
||||
* K_STORE is for a list of items taken from a K_LIST
|
||||
* The restriction is, a K_STORE must not allocate new items,
|
||||
* only the K_LIST should do that
|
||||
* i.e. all K_STORE items came from a K_LIST
|
||||
*/
|
||||
#define K_STORE K_LIST
|
||||
|
||||
// Extended ck wlock to allow >1 minute
|
||||
#define SINGLE_TIMEOUT_S 10
|
||||
/* 5mins - should never happen, but the longest lock: shift summarisation,
|
||||
* will get slower over time as the share rate rises
|
||||
* Currently, only the code that uses the process_pplns_free lock,
|
||||
* uses the k_longwlock function to acquire the lock, so that CKDB doesn't
|
||||
* exit if a shift summarisation takes longer than the normal timeout limit
|
||||
* Nothing else should need to */
|
||||
#define TIMEOUT_RETRIES 30
|
||||
static inline int wr_timedlock(pthread_rwlock_t *lock, int timeout)
|
||||
{
|
||||
tv_t now;
|
||||
ts_t abs;
|
||||
int ret;
|
||||
|
||||
tv_time(&now);
|
||||
tv_to_ts(&abs, &now);
|
||||
abs.tv_sec += timeout;
|
||||
|
||||
ret = pthread_rwlock_timedwrlock(lock, &abs);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static inline void k_longwlock(cklock_t *lock, KLIST_FFL_ARGS)
|
||||
{
|
||||
int ret, retries = 0;
|
||||
|
||||
retrym:
|
||||
ret = _mutex_timedlock(&(lock->mutex), SINGLE_TIMEOUT_S, file, func, line);
|
||||
if (unlikely(ret)) {
|
||||
if (likely(ret == ETIMEDOUT)) {
|
||||
LOGERR("WARNING: Prolonged mutex longlock contention from %s %s:%d, held by %s %s:%d",
|
||||
file, func, line, lock->mutex.file, lock->mutex.func, lock->mutex.line);
|
||||
if (++retries < TIMEOUT_RETRIES)
|
||||
goto retrym;
|
||||
quitfrom(1, file, func, line, "FAILED TO GRAB LONGMUTEX!");
|
||||
}
|
||||
quitfrom(1, file, func, line, "WTF MUTEX ERROR ON LONGLOCK!");
|
||||
}
|
||||
|
||||
retries = 0;
|
||||
retry:
|
||||
ret = wr_timedlock(&(lock->rwlock.rwlock), SINGLE_TIMEOUT_S);
|
||||
if (unlikely(ret)) {
|
||||
if (likely(ret == ETIMEDOUT)) {
|
||||
LOGERR("WARNING: Prolonged longwrite lock contention from %s %s:%d, held by %s %s:%d",
|
||||
file, func, line, lock->rwlock.file, lock->rwlock.func, lock->rwlock.line);
|
||||
if (++retries < TIMEOUT_RETRIES)
|
||||
goto retry;
|
||||
quitfrom(1, file, func, line, "FAILED TO GRAB LONGWRITE LOCK!");
|
||||
}
|
||||
quitfrom(1, file, func, line, "WTF ERROR ON LONGWRITE LOCK!");
|
||||
}
|
||||
lock->rwlock.file = file;
|
||||
lock->rwlock.func = func;
|
||||
lock->rwlock.line = line;
|
||||
}
|
||||
#define ck_KLONGW(_lock) k_longwlock(_lock, __FILE__, __func__, __LINE__)
|
||||
|
||||
#if LOCK_CHECK
|
||||
#define LOCK_MAYBE
|
||||
/* The simple lock_check_init check is in case someone incorrectly changes ckdb.c ...
|
||||
* It's not fool proof :P
|
||||
* If LOCK_INIT() is called too many times, i.e. too many threads,
|
||||
* it will report and disable lock checking */
|
||||
#define LOCK_INIT(_name) do { \
|
||||
if (!lock_check_init) { \
|
||||
quithere(1, "In thread %s, lock_check_lock has not been " \
|
||||
"initialised!", _name); \
|
||||
} \
|
||||
ck_wlock(&lock_check_lock); \
|
||||
my_thread_id = next_thread_id++; \
|
||||
ck_wunlock(&lock_check_lock); \
|
||||
if (my_thread_id >= MAX_THREADS) { \
|
||||
disable_checks = true; \
|
||||
LOGERR("WARNING: all lock checking disabled due to " \
|
||||
"initialising too many threads - limit %d", \
|
||||
MAX_THREADS); \
|
||||
} \
|
||||
my_thread_name = strdup(_name); \
|
||||
} while (0)
|
||||
#define FIRST_LOCK_INIT(_name) do { \
|
||||
if (lock_check_init) { \
|
||||
quithere(1, "In thread %s, lock_check_lock has already been " \
|
||||
"initialised!", (_name)); \
|
||||
} \
|
||||
cklock_init(&lock_check_lock); \
|
||||
lock_check_init = true; \
|
||||
LOCK_INIT(_name); \
|
||||
} while (0)
|
||||
|
||||
#define LOCK_MODE_LOCK 0
|
||||
#define LOCK_MODE_UNLOCK 1
|
||||
#define LOCK_TYPE_READ 0
|
||||
#define LOCK_TYPE_WRITE 1
|
||||
|
||||
// Lists with this priority cannot nest any lock inside their lock
|
||||
#define PRIO_TERMINAL 1
|
||||
|
||||
#define LOCKERR(fmt, ...) LOGEMERG("***CHKLOCK %s:%d(now off) " fmt, \
|
||||
my_thread_name ? : thread_noname, \
|
||||
my_thread_id, ##__VA_ARGS__)
|
||||
#define DLOCKERR(fmt, ...) LOGEMERG("***PREDLOCK %s(now off) " fmt, \
|
||||
my_thread_name ? : thread_noname, \
|
||||
##__VA_ARGS__)
|
||||
#define DLOCKOK(fmt, ...) LOGWARNING("***PREDLOCK %s " fmt, \
|
||||
my_thread_name ? : thread_noname, \
|
||||
##__VA_ARGS__)
|
||||
|
||||
// Neither test 'should' ever fail
|
||||
#define DLPRIO(_list, _p) do { \
|
||||
if ((_list ## _free)->is_store) \
|
||||
quithere(1, "Can't deadlock prioritise a K_STORE"); \
|
||||
if ((_list ## _free)->master != (_list ## _free)) \
|
||||
quithere(1, "K_LIST master is not itself"); \
|
||||
(_list ## _free)->deadlock_priority = (_p); \
|
||||
} while (0)
|
||||
|
||||
#define DLPCHECK() do { \
|
||||
K_LISTS *_klists; \
|
||||
if (!lock_check_init) { \
|
||||
quithere(1, "lock_check_lock has not been initialised!"); \
|
||||
} \
|
||||
ck_wlock(&lock_check_lock); \
|
||||
_klists = all_klists; \
|
||||
while (_klists) { \
|
||||
if (_klists->klist->deadlock_priority < PRIO_TERMINAL) { \
|
||||
DLOCKOK("%s priority not set (%d)", \
|
||||
_klists->klist->name, \
|
||||
_klists->klist->deadlock_priority); \
|
||||
} \
|
||||
_klists = _klists->next; \
|
||||
} \
|
||||
ck_wunlock(&lock_check_lock); \
|
||||
} while (0)
|
||||
|
||||
/* Optimisation should remove the code for all but the required _mode/_type
|
||||
* since all the related ifs are constants */
|
||||
#define THRLCK(_list) (((_list)->master)->k_lock[my_thread_id])
|
||||
#define CHECK_LOCK(_list, _func, _mode, _type) do { \
|
||||
static const char *_n = #_list " " #_func; \
|
||||
static const char *_fl = __FILE__; \
|
||||
static const char *_f = __func__; \
|
||||
static const int _l = __LINE__; \
|
||||
if (!disable_checks && my_check_locks && check_locks) { \
|
||||
if (_mode == LOCK_MODE_LOCK) { \
|
||||
if (THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count != 0) || \
|
||||
(THRLCK(_list).w_count != 0)) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid (r%d:w%d) " \
|
||||
"first: %s " KLIST_AFFL, \
|
||||
_n, _fl, _f, _l, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line); \
|
||||
} else { \
|
||||
THRLCK(_list).first_held = _n; \
|
||||
THRLCK(_list).file = _fl; \
|
||||
THRLCK(_list).func = _f; \
|
||||
THRLCK(_list).line = _l; \
|
||||
if (_type == LOCK_TYPE_READ) \
|
||||
THRLCK(_list).r_count++; \
|
||||
if (_type == LOCK_TYPE_WRITE) \
|
||||
THRLCK(_list).w_count++; \
|
||||
} \
|
||||
} \
|
||||
if (_mode == LOCK_MODE_UNLOCK && _type == LOCK_TYPE_READ) { \
|
||||
if (!THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count != 1) || \
|
||||
(THRLCK(_list).w_count != 0)) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid (r%d:w%d) " \
|
||||
"first: %s " KLIST_AFFL, \
|
||||
_n, _fl, _f, _l, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line); \
|
||||
} else { \
|
||||
THRLCK(_list).first_held = NULL; \
|
||||
THRLCK(_list).file = NULL; \
|
||||
THRLCK(_list).func = NULL; \
|
||||
THRLCK(_list).line = 0; \
|
||||
THRLCK(_list).r_count--; \
|
||||
} \
|
||||
} \
|
||||
if (_mode == LOCK_MODE_UNLOCK && _type == LOCK_TYPE_WRITE) { \
|
||||
if (!THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count != 0) || \
|
||||
(THRLCK(_list).w_count != 1)) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid (r%d:w%d) " \
|
||||
"first: %s " KLIST_AFFL, \
|
||||
_n, _fl, _f, _l, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line); \
|
||||
} else { \
|
||||
THRLCK(_list).first_held = NULL; \
|
||||
THRLCK(_list).file = NULL; \
|
||||
THRLCK(_list).func = NULL; \
|
||||
THRLCK(_list).line = 0; \
|
||||
THRLCK(_list).w_count--; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
if (!disable_checks && check_deadlocks && my_check_deadlocks) { \
|
||||
int _dp = (_list)->deadlock_priority; \
|
||||
if (my_lock_level == 0) { \
|
||||
if (_mode == LOCK_MODE_LOCK) { \
|
||||
if (_dp < PRIO_TERMINAL) { \
|
||||
my_check_deadlocks = false; \
|
||||
DLOCKERR("%s " KLIST_AFFL \
|
||||
" bad lock prio %d", \
|
||||
_n, _fl, _f, _l, \
|
||||
_dp); \
|
||||
} else { \
|
||||
my_locks[0] = _dp; \
|
||||
my_locks_n[0] = _n; \
|
||||
my_locks_fl[0] = _fl; \
|
||||
my_locks_f[0] = _f; \
|
||||
my_locks_l[0] = _l; \
|
||||
my_lock_level = 1; \
|
||||
} \
|
||||
} \
|
||||
if (_mode == LOCK_MODE_UNLOCK) { \
|
||||
DLOCKOK("%s " KLIST_AFFL \
|
||||
" lock level was 0 - unlock" \
|
||||
" prio %d ignored", \
|
||||
_n, _fl, _f, _l, \
|
||||
_dp); \
|
||||
} \
|
||||
} else { \
|
||||
if (_mode == LOCK_MODE_UNLOCK) { \
|
||||
if (my_locks[--my_lock_level] != _dp) { \
|
||||
my_check_deadlocks = false; \
|
||||
DLOCKERR("%s " KLIST_AFFL \
|
||||
" unlock prio %d" \
|
||||
" doesn't match prev" \
|
||||
" locked prio %d", \
|
||||
_n, _fl, _f, _l, \
|
||||
_dp, \
|
||||
my_locks[my_lock_level]); \
|
||||
} \
|
||||
} \
|
||||
if (_mode == LOCK_MODE_LOCK) { \
|
||||
int _i = my_lock_level - 1; \
|
||||
if (my_locks[_i] == PRIO_TERMINAL) { \
|
||||
my_check_deadlocks = false; \
|
||||
DLOCKERR("%s " KLIST_AFFL \
|
||||
" prev lock" \
|
||||
" prio[%d]=TERMINAL" \
|
||||
" ... lock prio[%d]" \
|
||||
"=%d %s " KLIST_AFFL, \
|
||||
_n, _fl, _f, _l, _i, \
|
||||
my_lock_level, _dp, \
|
||||
my_locks_n[_i], \
|
||||
my_locks_fl[_i], \
|
||||
my_locks_f[_i], \
|
||||
my_locks_l[_i]); \
|
||||
} else if (my_locks[_i] <= _dp) { \
|
||||
my_check_deadlocks = false; \
|
||||
DLOCKERR("%s " KLIST_AFFL \
|
||||
" lock prio[%d]=%d" \
|
||||
" >= prev lock" \
|
||||
" prio[%d]=%d" \
|
||||
" %s " KLIST_AFFL, \
|
||||
_n, _fl, _f, _l, \
|
||||
my_lock_level, _dp, \
|
||||
_i, my_locks[_i], \
|
||||
my_locks_n[_i], \
|
||||
my_locks_fl[_i], \
|
||||
my_locks_f[_i], \
|
||||
my_locks_l[_i]); \
|
||||
} else { \
|
||||
my_locks[my_lock_level] = _dp; \
|
||||
my_locks_n[my_lock_level] = _n; \
|
||||
my_locks_fl[my_lock_level] = _fl; \
|
||||
my_locks_f[my_lock_level] = _f; \
|
||||
my_locks_l[my_lock_level++] = _l; \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
ck_##_func(_list->lock); \
|
||||
} while (0)
|
||||
|
||||
#define CHECK_WLOCK(_list) CHECK_LOCK(_list, wlock, \
|
||||
LOCK_MODE_LOCK, LOCK_TYPE_WRITE)
|
||||
#define CHECK_KLONGWLOCK(_list) CHECK_LOCK(_list, KLONGW, \
|
||||
LOCK_MODE_LOCK, LOCK_TYPE_WRITE)
|
||||
#define CHECK_WUNLOCK(_list) CHECK_LOCK(_list, wunlock, \
|
||||
LOCK_MODE_UNLOCK, LOCK_TYPE_WRITE)
|
||||
#define CHECK_RLOCK(_list) CHECK_LOCK(_list, rlock, \
|
||||
LOCK_MODE_LOCK, LOCK_TYPE_READ)
|
||||
#define CHECK_RUNLOCK(_list) CHECK_LOCK(_list, runlock, \
|
||||
LOCK_MODE_UNLOCK, LOCK_TYPE_READ)
|
||||
|
||||
#define _LIST_WRITE(_list, _chklock, _file, _func, _line) do { \
|
||||
if (!disable_checks && my_check_locks && check_locks && _chklock) { \
|
||||
if (!THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count != 0) || \
|
||||
(THRLCK(_list).w_count != 1)) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid write " \
|
||||
"access (r%d:w%d) first: %s " \
|
||||
KLIST_AFFL KLIST_SFFL, \
|
||||
(_list)->master->name ? : nullstr, \
|
||||
KLIST_FFL_HERE, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line, \
|
||||
_file, _func, _line); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#define _LIST_WRITE2(_list, _chklock) do { \
|
||||
if (!disable_checks && my_check_locks && check_locks && _chklock) { \
|
||||
if (!THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count != 0) || \
|
||||
(THRLCK(_list).w_count != 1)) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid write " \
|
||||
"access (r%d:w%d) first: %s " \
|
||||
KLIST_AFFL, \
|
||||
(_list)->master->name ? : nullstr, \
|
||||
KLIST_FFL_HERE, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
// read is ok under read or write
|
||||
#define _LIST_READ(_list, _chklock, _file, _func, _line) do { \
|
||||
if (!disable_checks && my_check_locks && check_locks && _chklock) { \
|
||||
if (!THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count + \
|
||||
THRLCK(_list).w_count) != 1) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid read " \
|
||||
"access (r%d:w%d) first: %s " \
|
||||
KLIST_AFFL KLIST_SFFL, \
|
||||
(_list)->master->name ? : nullstr, \
|
||||
KLIST_FFL_HERE, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line, \
|
||||
_file, _func, _line); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#define _LIST_READ2(_list, _chklock) do { \
|
||||
if (!disable_checks && my_check_locks && check_locks && _chklock) { \
|
||||
if (!THRLCK(_list).first_held || \
|
||||
(THRLCK(_list).r_count + \
|
||||
THRLCK(_list).w_count) != 1) { \
|
||||
my_check_locks = false; \
|
||||
LOCKERR("%s " KLIST_AFFL " invalid read " \
|
||||
"access (r%d:w%d) first: %s " \
|
||||
KLIST_AFFL, \
|
||||
(_list)->master->name ? : nullstr, \
|
||||
KLIST_FFL_HERE, \
|
||||
THRLCK(_list).r_count, \
|
||||
THRLCK(_list).w_count, \
|
||||
THRLCK(_list).first_held ? : thread_noname, \
|
||||
THRLCK(_list).file ? : nullstr, \
|
||||
THRLCK(_list).func ? : nullstr, \
|
||||
THRLCK(_list).line); \
|
||||
} \
|
||||
} \
|
||||
} while (0)
|
||||
#define LIST_WRITE(_list) _LIST_WRITE2(_list, true)
|
||||
#define LIST_READ(_list) _LIST_READ2(_list, true)
|
||||
static inline K_ITEM *list_whead(K_LIST *list)
|
||||
{
|
||||
LIST_WRITE(list);
|
||||
return list->head;
|
||||
}
|
||||
static inline K_ITEM *list_rhead(K_LIST *list)
|
||||
{
|
||||
LIST_READ(list);
|
||||
return list->head;
|
||||
}
|
||||
static inline K_ITEM *list_wtail(K_LIST *list)
|
||||
{
|
||||
LIST_READ(list);
|
||||
return list->head;
|
||||
}
|
||||
static inline K_ITEM *list_rtail(K_LIST *list)
|
||||
{
|
||||
LIST_READ(list);
|
||||
return list->head;
|
||||
}
|
||||
#define LIST_WHEAD(_list) list_whead(_list)
|
||||
#define LIST_RHEAD(_list) list_rhead(_list)
|
||||
#define LIST_WTAIL(_list) list_wtail(_list)
|
||||
#define LIST_RTAIL(_list) list_rtail(_list)
|
||||
#else
|
||||
#define LOCK_MAYBE __maybe_unused
|
||||
#define LOCK_INIT(_name)
|
||||
#define FIRST_LOCK_INIT(_ignore) do { \
|
||||
if (lock_check_init) { \
|
||||
quithere(1, "lock_check_lock has already been " \
|
||||
"initialised!"); \
|
||||
} \
|
||||
cklock_init(&lock_check_lock); \
|
||||
lock_check_init = true; \
|
||||
} while (0)
|
||||
#define CHECK_WLOCK(_list) ck_wlock((_list)->lock)
|
||||
#define CHECK_KLONGWLOCK(_list) ck_KLONGW((_list)->lock)
|
||||
#define CHECK_WUNLOCK(_list) ck_wunlock((_list)->lock)
|
||||
#define CHECK_RLOCK(_list) ck_rlock((_list)->lock)
|
||||
#define CHECK_RUNLOCK(_list) ck_runlock((_list)->lock)
|
||||
#define _LIST_WRITE(_list, _chklock, _file, _func, _line)
|
||||
#define _LIST_READ(_list, _chklock, _file, _func, _line)
|
||||
#define LIST_WRITE(_list)
|
||||
#define LIST_READ(_list)
|
||||
#define LIST_WHEAD(_list) (_list)->head
|
||||
#define LIST_RHEAD(_list) (_list)->head
|
||||
#define LIST_WTAIL(_list) (_list)->tail
|
||||
#define LIST_RTAIL(_list) (_list)->tail
|
||||
#endif
|
||||
|
||||
#define LIST_HEAD_NOLOCK(_list) (_list)->head
|
||||
#define LIST_TAIL_NOLOCK(_list) (_list)->tail
|
||||
|
||||
#define CHECK_lock(_list) do { \
|
||||
if ((_list)->lock == NULL) { \
|
||||
quithere(1, "Attempt to lock list '%s' master '%s' " \
|
||||
" that has no lock", \
|
||||
(_list)->name, (_list)->master->name); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define K_WLOCK(_list) do { \
|
||||
CHECK_lock(_list); \
|
||||
CHECK_WLOCK(_list); \
|
||||
} while (0)
|
||||
#define K_KLONGWLOCK(_list) do { \
|
||||
CHECK_lock(_list); \
|
||||
CHECK_KLONGWLOCK(_list); \
|
||||
} while (0)
|
||||
#define K_WUNLOCK(_list) do { \
|
||||
CHECK_lock(_list); \
|
||||
CHECK_WUNLOCK(_list); \
|
||||
} while (0)
|
||||
#define K_RLOCK(_list) do { \
|
||||
CHECK_lock(_list); \
|
||||
CHECK_RLOCK(_list); \
|
||||
} while (0)
|
||||
#define K_RUNLOCK(_list) do { \
|
||||
CHECK_lock(_list); \
|
||||
CHECK_RUNLOCK(_list); \
|
||||
} while (0)
|
||||
|
||||
#define STORE_WHEAD(_s) LIST_WHEAD(_s)
|
||||
#define STORE_RHEAD(_s) LIST_RHEAD(_s)
|
||||
#define STORE_WTAIL(_s) LIST_WTAIL(_s)
|
||||
#define STORE_RTAIL(_s) LIST_RTAIL(_s)
|
||||
|
||||
// No need to lock temporary/private stores
|
||||
#define STORE_HEAD_NOLOCK(_s) LIST_HEAD_NOLOCK(_s)
|
||||
#define STORE_TAIL_NOLOCK(_s) LIST_TAIL_NOLOCK(_s)
|
||||
|
||||
extern void _dsp_kstore(K_STORE *store, char *filename, char *msg, KLIST_FFL_ARGS);
|
||||
#define dsp_kstore(_store, _file, _msg) _dsp_kstore(_store, _file, _msg, KLIST_FFL_HERE)
|
||||
extern K_STORE *_k_new_store(K_LIST *list, bool gotlock, KLIST_FFL_ARGS);
|
||||
#define k_new_store(_list) _k_new_store(_list, false, KLIST_FFL_HERE)
|
||||
#define k_new_store_locked(_list) _k_new_store(_list, true, KLIST_FFL_HERE)
|
||||
extern K_LIST *_k_new_list(const char *name, size_t siz, int allocate,
|
||||
int limit, bool do_tail, bool lock_only,
|
||||
bool without_lock, bool local_list,
|
||||
const char *name2, int cull_limit, KLIST_FFL_ARGS);
|
||||
#define k_new_list(_name, _siz, _allocate, _limit, _do_tail) \
|
||||
_k_new_list(_name, _siz, _allocate, _limit, _do_tail, false, false, false, NULL, 0, KLIST_FFL_HERE)
|
||||
#define k_lock_only_list(_name) \
|
||||
_k_new_list(_name, 1, 1, 1, true, true, false, false, NULL, 0, KLIST_FFL_HERE)
|
||||
#define k_new_tree_list(_name, _siz, _allocate, _limit, _do_tail, _local_tree, _name2) \
|
||||
_k_new_list(_name, _siz, _allocate, _limit, _do_tail, false, true, _local_tree, _name2, 0, KLIST_FFL_HERE)
|
||||
#define k_new_list_cull(_name, _siz, _allocate, _limit, _do_tail, _cull) \
|
||||
_k_new_list(_name, _siz, _allocate, _limit, _do_tail, false, false, false, NULL, _cull, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_k_unlink_head(K_LIST *list, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_unlink_head(_list) _k_unlink_head(_list, true, KLIST_FFL_HERE)
|
||||
#define k_unlink_head_nolock(_list) _k_unlink_head(_list, false, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_k_unlink_head_zero(K_LIST *list, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_unlink_head_zero(_list) _k_unlink_head_zero(_list, true, KLIST_FFL_HERE)
|
||||
//#define k_unlink_head_zero_nolock(_list) _k_unlink_head_zero(_list, false, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_k_unlink_tail(K_LIST *list, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_unlink_tail(_list) _k_unlink_tail(_list, true, KLIST_FFL_HERE)
|
||||
//#define k_unlink_tail_nolock(_list) _k_unlink_tail(_list, false, KLIST_FFL_HERE)
|
||||
extern void _k_add_head(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_add_head(_list, _item) _k_add_head(_list, _item, true, KLIST_FFL_HERE)
|
||||
#define k_add_head_nolock(_list, _item) _k_add_head(_list, _item, false, KLIST_FFL_HERE)
|
||||
// extern void k_free_head(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_free_head(__list, __item) _k_add_head(__list, __item, true, KLIST_FFL_HERE)
|
||||
//#define k_free_head_nolock(__list, __item) _k_add_head(__list, __item, false, KLIST_FFL_HERE)
|
||||
extern void _k_add_tail(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_add_tail(_list, _item) _k_add_tail(_list, _item, true, KLIST_FFL_HERE)
|
||||
#define k_add_tail_nolock(_list, _item) _k_add_tail(_list, _item, false, KLIST_FFL_HERE)
|
||||
extern void _k_insert_after(K_LIST *list, K_ITEM *item, K_ITEM *after, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_insert_after(_list, _item, _after) _k_insert_after(_list, _item, _after, true, KLIST_FFL_HERE)
|
||||
//#define k_insert_after_nolock(_list, _item, _after) _k_insert_after(_list, _item, _after, false, KLIST_FFL_HERE)
|
||||
extern void _k_unlink_item(K_LIST *list, K_ITEM *item, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_unlink_item(_list, _item) _k_unlink_item(_list, _item, true, KLIST_FFL_HERE)
|
||||
#define k_unlink_item_nolock(_list, _item) _k_unlink_item(_list, _item, false, KLIST_FFL_HERE)
|
||||
extern void _k_list_transfer_to_head(K_LIST *from, K_LIST *to, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_list_transfer_to_head(_from, _to) _k_list_transfer_to_head(_from, _to, true, KLIST_FFL_HERE)
|
||||
//#define k_list_transfer_to_head_nolock(_from, _to) _k_list_transfer_to_head(_from, _to, false, KLIST_FFL_HERE)
|
||||
extern void _k_list_transfer_to_tail(K_LIST *from, K_LIST *to, LOCK_MAYBE bool chklock, KLIST_FFL_ARGS);
|
||||
#define k_list_transfer_to_tail(_from, _to) _k_list_transfer_to_tail(_from, _to, true, KLIST_FFL_HERE)
|
||||
#define k_list_transfer_to_tail_nolock(_from, _to) _k_list_transfer_to_tail(_from, _to, false, KLIST_FFL_HERE)
|
||||
extern K_LIST *_k_free_list(K_LIST *list, KLIST_FFL_ARGS);
|
||||
#define k_free_list(_list) _k_free_list(_list, KLIST_FFL_HERE)
|
||||
extern K_STORE *_k_free_store(K_STORE *store, KLIST_FFL_ARGS);
|
||||
#define k_free_store(_store) _k_free_store(_store, KLIST_FFL_HERE)
|
||||
|
||||
#endif
|
||||
1104
src/ktree.c
1104
src/ktree.c
File diff suppressed because it is too large
Load Diff
117
src/ktree.h
117
src/ktree.h
@ -1,117 +0,0 @@
|
||||
/*
|
||||
* Copyright 1995-2016 Andrew Smith
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the Free
|
||||
* Software Foundation; either version 3 of the License, or (at your option)
|
||||
* any later version. See COPYING for more details.
|
||||
*/
|
||||
|
||||
#ifndef ___KTREE_H
|
||||
#define ___KTREE_H
|
||||
|
||||
#include "klist.h"
|
||||
#include "libckpool.h"
|
||||
|
||||
#define quithere(status, fmt, ...) \
|
||||
quitfrom(status, __FILE__, __func__, __LINE__, fmt, ##__VA_ARGS__)
|
||||
|
||||
#define KTREE_FFL " - from %s %s() line %d"
|
||||
#define KTREE_FFL_HERE __FILE__, __func__, __LINE__
|
||||
#define KTREE_FFL_PASS file, func, line
|
||||
#define KTREE_FFL_ARGS __maybe_unused const char *file, \
|
||||
__maybe_unused const char *func, \
|
||||
__maybe_unused const int line
|
||||
|
||||
#define cmp_t int32_t
|
||||
|
||||
#define CMP_STR(a,b) strcmp((a),(b))
|
||||
#define CMP_INT(a,b) ((a)-(b))
|
||||
#define CMP_BIG_Z(a,b) (((a) < (b)) ? -1 : 1)
|
||||
#define CMP_BIG(a,b) (((a) == (b)) ? 0 : CMP_BIG_Z(a,b))
|
||||
#define CMP_TV(a,b) (((a).tv_sec == (b).tv_sec) ? CMP_BIG((a).tv_usec,(b).tv_usec) : \
|
||||
CMP_BIG_Z((a).tv_sec,(b).tv_sec))
|
||||
#define CMP_BIGINT CMP_BIG
|
||||
#define CMP_DOUBLE CMP_BIG
|
||||
|
||||
#define _TREE_WRITE(_t, _c, _fl, _f, _l) _LIST_WRITE(_t, _c, _fl, _f, _l)
|
||||
#define _TREE_READ(_t, _c, _fl, _f, _l) _LIST_READ(_t, _c, _fl, _f, _l)
|
||||
|
||||
typedef struct knode
|
||||
{
|
||||
K_ITEM *kitem;
|
||||
bool isNil;
|
||||
bool red;
|
||||
struct knode *parent;
|
||||
struct knode *left;
|
||||
struct knode *right;
|
||||
K_ITEM *data;
|
||||
long test;
|
||||
} K_NODE;
|
||||
|
||||
typedef struct ktree
|
||||
{
|
||||
const char *name;
|
||||
K_NODE *root;
|
||||
cmp_t (*cmp_funct)(K_ITEM *, K_ITEM *);
|
||||
K_LIST *master;
|
||||
K_LIST *node_free;
|
||||
K_STORE *node_store;
|
||||
} K_TREE;
|
||||
|
||||
typedef void *K_TREE_CTX;
|
||||
|
||||
// Avoid allocating too much ram up front for temporary trees
|
||||
#define NODE_ALLOC 64
|
||||
#define NODE_LIMIT 0
|
||||
|
||||
extern K_TREE *_new_ktree(const char *name, cmp_t (*cmp_funct)(K_ITEM *, K_ITEM *),
|
||||
K_LIST *master, int alloc, int limit, bool local_tree,
|
||||
KTREE_FFL_ARGS);
|
||||
#define new_ktree(_name, _cmp_funct, _master) \
|
||||
_new_ktree(_name, _cmp_funct, _master, _master->allocate, _master->limit, false, KLIST_FFL_HERE)
|
||||
#define new_ktree_local(_name, _cmp_funct, _master) \
|
||||
_new_ktree(_name, _cmp_funct, _master, _master->allocate, _master->limit, true, KLIST_FFL_HERE)
|
||||
#define new_ktree_auto(_name, _cmp_funct, _master) \
|
||||
_new_ktree(_name, _cmp_funct, _master, NODE_ALLOC, NODE_LIMIT, true, KLIST_FFL_HERE)
|
||||
#define new_ktree_size(_name, _cmp_funct, _master, _alloc, _limit) \
|
||||
_new_ktree(_name, _cmp_funct, _master, _alloc, _limit, false, KLIST_FFL_HERE)
|
||||
extern void _dump_ktree(K_TREE *tree, char *(*dsp_funct)(K_ITEM *), KTREE_FFL_ARGS);
|
||||
#define dump_ktree(_tree, _dsp_funct) _dump_ktree(_tree, _dsp_funct, KLIST_FFL_HERE)
|
||||
extern void _dsp_ktree(K_TREE *tree, char *filename, char *msg, KTREE_FFL_ARGS);
|
||||
#define dsp_ktree(_tree, _filename, _msg) _dsp_ktree(_tree, _filename, _msg, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_first_in_ktree(K_TREE *tree, K_TREE_CTX *ctx, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS);
|
||||
#define first_in_ktree(_tree, _ctx) _first_in_ktree(_tree, _ctx, true, KLIST_FFL_HERE)
|
||||
#define first_in_ktree_nolock(_ktree, _ctx) _first_in_ktree(_ktree, _ctx, false, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_last_in_ktree(K_TREE *tree, K_TREE_CTX *ctx, KTREE_FFL_ARGS);
|
||||
#define last_in_ktree(_tree, _ctx) _last_in_ktree(_tree, _ctx, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_next_in_ktree(K_TREE_CTX *ctx, KTREE_FFL_ARGS);
|
||||
#define next_in_ktree(_ctx) _next_in_ktree(_ctx, KLIST_FFL_HERE)
|
||||
// No difference for now
|
||||
#define next_in_ktree_nolock(_ctx) _next_in_ktree(_ctx, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_prev_in_ktree(K_TREE_CTX *ctx, KTREE_FFL_ARGS);
|
||||
#define prev_in_ktree(_ctx) _prev_in_ktree(_ctx, KLIST_FFL_HERE)
|
||||
// No difference for now
|
||||
#define prev_in_ktree_nolock(_ctx) _prev_in_ktree(_ctx, KLIST_FFL_HERE)
|
||||
extern void _add_to_ktree(K_TREE *tree, K_ITEM *data, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS);
|
||||
#define add_to_ktree(_tree, _data) _add_to_ktree(_tree, _data, true, KLIST_FFL_HERE)
|
||||
#define add_to_ktree_nolock(_tree, _data) _add_to_ktree(_tree, _data, false, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_find_in_ktree(K_TREE *tree, K_ITEM *data, K_TREE_CTX *ctx, bool chklock, KTREE_FFL_ARGS);
|
||||
#define find_in_ktree(_tree, _data, _ctx) _find_in_ktree(_tree, _data, _ctx, true, KLIST_FFL_HERE)
|
||||
#define find_in_ktree_nolock(_tree, _data, _ctx) _find_in_ktree(_tree, _data, _ctx, false, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_find_after_in_ktree(K_TREE *ktree, K_ITEM *data, K_TREE_CTX *ctx, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS);
|
||||
#define find_after_in_ktree(_ktree, _data, _ctx) _find_after_in_ktree(_ktree, _data, _ctx, true, KLIST_FFL_HERE)
|
||||
//#define find_after_in_ktree_nolock(_ktree, _data, _ctx) _find_after_in_ktree(_ktree, _data, _ctx, false, KLIST_FFL_HERE)
|
||||
extern K_ITEM *_find_before_in_ktree(K_TREE *ktree, K_ITEM *data, K_TREE_CTX *ctx, KTREE_FFL_ARGS);
|
||||
#define find_before_in_ktree(_ktree, _data, _ctx) _find_before_in_ktree(_ktree, _data, _ctx, KLIST_FFL_HERE)
|
||||
extern void _remove_from_ktree(K_TREE *tree, K_ITEM *data, K_TREE_CTX *ctx, LOCK_MAYBE bool chklock, KTREE_FFL_ARGS);
|
||||
extern void _remove_from_ktree_free(K_TREE *tree, K_ITEM *data, bool chklock, KTREE_FFL_ARGS);
|
||||
#define remove_from_ktree(_tree, _data) _remove_from_ktree_free(_tree, _data, true, KLIST_FFL_HERE)
|
||||
//#define remove_from_ktree_nolock(_tree, _data) _remove_from_ktree_free(_tree, _data, false, KLIST_FFL_HERE)
|
||||
extern void _free_ktree(K_TREE *tree, void (*free_funct)(void *), KTREE_FFL_ARGS);
|
||||
#define free_ktree(_tree, _free_funct) do { \
|
||||
_free_ktree(_tree, _free_funct, KLIST_FFL_HERE); \
|
||||
_tree = NULL; \
|
||||
} while (0)
|
||||
|
||||
#endif
|
||||
Loading…
Reference in New Issue
Block a user