blob: 1791f2a5b7f7c3732578412b76e85f7aab501cbe [file] [log] [blame]
/*****************************************************************************
* list.c
*****************************************************************************
* Copyright (C) 2001-2002 The Regents of the University of California.
* Copyright (C) 2021 NVIDIA Corporation.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Chris Dunlap <cdunlap@llnl.gov>.
*
* This file is from LSD-Tools, the LLNL Software Development Toolbox.
*
* LSD-Tools 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 2 of the License, or (at your option)
* any later version.
*
* In addition, as a special exception, the copyright holders give permission
* to link the code of portions of this program with the OpenSSL library under
* certain conditions as described in each individual source file, and
* distribute linked combinations including the two. You must obey the GNU
* General Public License in all respects for all of the code used other than
* OpenSSL. If you modify file(s) with this exception, you may extend this
* exception to your version of the file(s), but you are not obligated to do
* so. If you do not wish to do so, delete this exception statement from your
* version. If you delete this exception statement from all source files in
* the program, then also delete it here.
*
* LSD-Tools is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with LSD-Tools; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*****************************************************************************
* Refer to "list.h" for documentation on public functions.
*****************************************************************************/
#include "config.h"
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "src/common/list.h"
#include "src/common/log.h"
#include "src/common/macros.h"
#include "src/common/xassert.h"
#include "src/common/xmalloc.h"
/*
** Define slurm-specific aliases for use by plugins, see slurm_xlator.h
** for details.
*/
strong_alias(list_create, slurm_list_create);
strong_alias(list_destroy, slurm_list_destroy);
strong_alias(list_is_empty, slurm_list_is_empty);
strong_alias(list_count, slurm_list_count);
strong_alias(list_shallow_copy, slurm_list_shallow_copy);
strong_alias(list_append, slurm_list_append);
strong_alias(list_append_list, slurm_list_append_list);
strong_alias(list_transfer, slurm_list_transfer);
strong_alias(list_transfer_max, slurm_list_transfer_max);
strong_alias(list_transfer_unique, slurm_list_transfer_unique);
strong_alias(list_push, list_prepend);
strong_alias(list_push, slurm_list_prepend);
strong_alias(list_find_first, slurm_list_find_first);
strong_alias(list_find_first_ro, slurm_list_find_first_ro);
strong_alias(list_delete_all, slurm_list_delete_all);
strong_alias(list_delete_first, slurm_list_delete_first);
strong_alias(list_delete_ptr, slurm_list_delete_ptr);
strong_alias(list_for_each, slurm_list_for_each);
strong_alias(list_for_each_ro, slurm_list_for_each_ro);
strong_alias(list_for_each_max, slurm_list_for_each_max);
strong_alias(list_flush, slurm_list_flush);
strong_alias(list_flush_max, slurm_list_flush_max);
strong_alias(list_sort, slurm_list_sort);
strong_alias(list_flip, slurm_list_flip);
strong_alias(list_push, slurm_list_push);
strong_alias(list_pop, slurm_list_pop);
strong_alias(list_peek, slurm_list_peek);
strong_alias(list_append, list_enqueue);
strong_alias(list_append, slurm_list_enqueue);
strong_alias(list_pop, list_dequeue);
strong_alias(list_pop, slurm_list_dequeue);
strong_alias(list_iterator_create, slurm_list_iterator_create);
strong_alias(list_iterator_reset, slurm_list_iterator_reset);
strong_alias(list_iterator_destroy, slurm_list_iterator_destroy);
strong_alias(list_next, slurm_list_next);
strong_alias(list_insert, slurm_list_insert);
strong_alias(list_find, slurm_list_find);
strong_alias(list_remove, slurm_list_remove);
strong_alias(list_remove_first, slurm_list_remove_first);
strong_alias(list_delete_item, slurm_list_delete_item);
/***************
* Constants *
***************/
#define LIST_MAGIC 0xDEADBEEF
#define LIST_ITR_MAGIC 0xDEADBEFF
/****************
* Data Types *
****************/
typedef struct listNode {
void *data; /* node's data */
struct listNode *next; /* next node in list */
} list_node_t;
struct listIterator {
unsigned int magic; /* sentinel for asserting validity */
list_t *list; /* the list being iterated */
list_node_t *pos; /* the next node to be iterated */
list_node_t **prev; /* addr of 'next' ptr to prv It node */
list_itr_t *iNext; /* iterator chain for list_destroy() */
};
struct xlist {
unsigned int magic; /* sentinel for asserting validity */
int count; /* number of nodes in list */
list_node_t *head; /* head of the list */
list_node_t **tail; /* addr of last node's 'next' ptr */
list_itr_t *iNext; /* iterator chain for list_destroy() */
ListDelF fDel; /* function to delete node data */
pthread_rwlock_t mutex; /* mutex to protect access to list */
pthread_t tid; /* id of thread holding write lock */
list_node_t *free_nodes; /* head of unused nodes */
list_node_t *node_allocations; /* memory for additional nodes */
list_node_t nodes[]; /* THIS MUST BE THE LAST PART OF THE STRUCT */
};
/*
* Fix as many list_node_t elements into a 4k page alongside the list as
* possible. Leave at least 32 bytes off to account for xmalloc() and malloc()
* overhead.
*/
static const int nodes_in_page = (4064 - sizeof(list_t)) / sizeof(list_node_t);
/****************
* Macros
****************/
#define LIST_THREAD_LOCK_DEF pthread_t tid = pthread_self(); bool locked = false;
#define LIST_THREAD_LOCK(l, write_lock) \
do { \
/* \
* If I am on the same thread and already locked, \
* don't lock again. \
*/ \
if (l->tid != tid) { \
if (write_lock) { \
slurm_rwlock_wrlock(&l->mutex); \
/* Only set tid under a write lock */ \
l->tid = tid; \
} else { \
slurm_rwlock_rdlock(&l->mutex); \
xassert(!l->tid); \
} \
locked = true; \
} else { \
debug3("%s: list lock already held by this thread", \
__func__); \
} \
} while (0)
#define LIST_THREAD_UNLOCK(l, write_lock) \
do { \
if (locked) { \
/* Only clear tid under a write lock */ \
if (write_lock) \
l->tid = 0; \
slurm_rwlock_unlock(&l->mutex); \
} \
} while (0)
/****************
* Prototypes *
****************/
static void _list_node_create(list_t *l, list_node_t **pp, void *x);
static void *_list_node_destroy(list_t *l, list_node_t **pp);
static void *_list_find_first_locked(list_t *l, ListFindF f, void *key);
#ifndef NDEBUG
static int _list_mutex_is_locked(pthread_rwlock_t *mutex);
#endif
/***************
* Functions *
***************/
extern list_t *list_create(ListDelF f)
{
list_t *l = xmalloc(sizeof(*l) + (nodes_in_page * sizeof(list_node_t)));
l->magic = LIST_MAGIC;
l->head = NULL;
l->tail = &l->head;
l->iNext = NULL;
l->fDel = f;
l->count = 0;
slurm_rwlock_init(&l->mutex);
l->node_allocations = NULL;
l->free_nodes = &l->nodes[0];
for (int i = 0; i < (nodes_in_page - 1); i++)
l->nodes[i].next = &l->nodes[i + 1];
return l;
}
extern void list_destroy(list_t *l)
{
list_itr_t *i, *iTmp;
list_node_t *p, *pTmp;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
i = l->iNext;
while (i) {
xassert(i->magic == LIST_ITR_MAGIC);
i->magic = ~LIST_ITR_MAGIC;
iTmp = i->iNext;
xfree(i);
i = iTmp;
}
p = l->head;
while (p) {
if (p->data && l->fDel)
l->fDel(p->data);
p = p->next;
}
p = l->node_allocations;
while (p) {
pTmp = p->next;
xfree(p);
p = pTmp;
}
l->magic = ~LIST_MAGIC;
slurm_rwlock_unlock(&l->mutex);
slurm_rwlock_destroy(&l->mutex);
xfree(l);
}
extern int list_is_empty(list_t *l)
{
int n;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_rdlock(&l->mutex);
n = l->count;
slurm_rwlock_unlock(&l->mutex);
return (n == 0);
}
/*
* Return the number of items in list [l].
* If [l] is NULL, return 0.
*/
extern int list_count(list_t *l)
{
int n;
LIST_THREAD_LOCK_DEF;
if (!l)
return 0;
xassert(l->magic == LIST_MAGIC);
LIST_THREAD_LOCK(l, false);
n = l->count;
LIST_THREAD_UNLOCK(l, false);
return n;
}
extern list_t *list_shallow_copy(list_t *l)
{
list_t *m = list_create(NULL);
(void) list_append_list(m, l);
return m;
}
extern void list_append(list_t *l, void *x)
{
LIST_THREAD_LOCK_DEF;
xassert(l != NULL);
xassert(x != NULL);
xassert(l->magic == LIST_MAGIC);
LIST_THREAD_LOCK(l, true);
_list_node_create(l, l->tail, x);
LIST_THREAD_UNLOCK(l, true);
}
extern int list_append_list(list_t *l, list_t *sub)
{
int n = 0;
list_node_t *p;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
xassert(l->fDel == NULL);
xassert(sub != NULL);
xassert(sub->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
slurm_rwlock_wrlock(&sub->mutex);
p = sub->head;
while (p) {
_list_node_create(l, l->tail, p->data);
n++;
p = p->next;
}
slurm_rwlock_unlock(&sub->mutex);
slurm_rwlock_unlock(&l->mutex);
return n;
}
/*
* Pops off list [sub] to [l] with maximum number of entries.
* Set max = 0 to transfer all entries.
* Note: list [l] must have the same destroy function as list [sub].
* Note: list [sub] may be returned empty, but not destroyed.
* Returns a count of the number of items added to list [l].
*/
extern int list_transfer_max(list_t *l, list_t *sub, int max)
{
void *v;
int n = 0;
xassert(l);
xassert(sub);
xassert(l->magic == LIST_MAGIC);
xassert(sub->magic == LIST_MAGIC);
xassert(l->fDel == sub->fDel);
slurm_rwlock_wrlock(&l->mutex);
slurm_rwlock_wrlock(&sub->mutex);
while ((!max || n <= max) && (v = _list_node_destroy(sub, &sub->head))) {
_list_node_create(l, l->tail, v);
n++;
}
slurm_rwlock_unlock(&sub->mutex);
slurm_rwlock_unlock(&l->mutex);
return n;
}
extern int list_transfer_match(list_t *l, list_t *sub, ListFindF f, void *key)
{
list_node_t **pp;
void *v;
int n = 0;
xassert(l);
xassert(sub);
xassert(l != sub);
xassert(l->magic == LIST_MAGIC);
xassert(sub->magic == LIST_MAGIC);
xassert(l->fDel == sub->fDel);
slurm_rwlock_wrlock(&l->mutex);
slurm_rwlock_wrlock(&sub->mutex);
pp = &l->head;
while (*pp) {
if (f((*pp)->data, key)) {
if ((v = _list_node_destroy(l, pp)))
n++;
_list_node_create(sub, sub->tail, v);
} else {
pp = &(*pp)->next;
}
}
slurm_rwlock_unlock(&sub->mutex);
slurm_rwlock_unlock(&l->mutex);
return n;
}
/*
* Pops off list [sub] to [l].
* Set max = 0 to transfer all entries.
* Note: list [l] must have the same destroy function as list [sub].
* Note: list [sub] will be returned empty, but not destroyed.
* Returns a count of the number of items added to list [l].
*/
extern int list_transfer(list_t *l, list_t *sub)
{
return list_transfer_max(l, sub, 0);
}
/*
* Pop off elements in list [sub] to [l], unless already in [l].
* Note: list [l] must have the same destroy function as list [sub].
* Note: list [l] could contain repeated elements, but those aren't removed.
* Note: list [sub] will be returned with repeated elements or empty,
* but never destroyed.
* Returns a count of the number of items added to list [l].
*/
extern int list_transfer_unique(list_t *l, ListFindF f, list_t *sub)
{
list_node_t **pp;
void *v;
int n = 0;
xassert(l);
xassert(f);
xassert(sub);
xassert(l->magic == LIST_MAGIC);
xassert(sub->magic == LIST_MAGIC);
xassert(l->fDel == sub->fDel);
slurm_rwlock_wrlock(&l->mutex);
slurm_rwlock_wrlock(&sub->mutex);
pp = &sub->head;
while (*pp) {
v = (*pp)->data;
/* Is this element already in destination list? */
if (!_list_find_first_locked(l, f, v)) {
/* Not found: Transfer the element */
_list_node_create(l, l->tail, v);
/* Destroy increases index */
_list_node_destroy(sub, pp);
n++;
} else
/* Found: Just increase index */
pp = &(*pp)->next;
}
slurm_rwlock_unlock(&sub->mutex);
slurm_rwlock_unlock(&l->mutex);
return n;
}
static void *_list_find_first_locked(list_t *l, ListFindF f, void *key)
{
for (list_node_t *p = l->head; p; p = p->next) {
if (f(p->data, key))
return p->data;
}
return NULL;
}
static void *_list_find_first_lock(list_t *l, ListFindF f, void *key,
bool write_lock)
{
void *v = NULL;
LIST_THREAD_LOCK_DEF;
xassert(l != NULL);
xassert(f != NULL);
xassert(l->magic == LIST_MAGIC);
LIST_THREAD_LOCK(l, write_lock);
v = _list_find_first_locked(l, f, key);
LIST_THREAD_UNLOCK(l, write_lock);
return v;
}
extern void *list_find_first(list_t *l, ListFindF f, void *key)
{
return _list_find_first_lock(l, f, key, true);
}
/*
* Same as list_find_first, but use a rdlock instead of wrlock
*/
extern void *list_find_first_ro(list_t *l, ListFindF f, void *key)
{
return _list_find_first_lock(l, f, key, false);
}
extern void *list_remove_first(list_t *l, ListFindF f, void *key)
{
list_node_t **pp;
void *v = NULL;
xassert(l != NULL);
xassert(f != NULL);
xassert(key != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
pp = &l->head;
while (*pp) {
if (f((*pp)->data, key)) {
v = _list_node_destroy(l, pp);
break;
} else {
pp = &(*pp)->next;
}
}
slurm_rwlock_unlock(&l->mutex);
return v;
}
extern int list_delete_all(list_t *l, ListFindF f, void *key)
{
list_node_t **pp;
void *v;
int n = 0;
xassert(l != NULL);
xassert(f != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
pp = &l->head;
while (*pp) {
if (f((*pp)->data, key)) {
if ((v = _list_node_destroy(l, pp))) {
if (l->fDel)
l->fDel(v);
n++;
}
}
else {
pp = &(*pp)->next;
}
}
slurm_rwlock_unlock(&l->mutex);
return n;
}
extern int list_delete_first(list_t *l, ListFindF f, void *key)
{
list_node_t **pp;
void *v;
int n = 0;
xassert(l != NULL);
xassert(f != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
pp = &l->head;
while (*pp) {
int rc = f((*pp)->data, key);
if (rc > 0) {
if ((v = _list_node_destroy(l, pp))) {
if (l->fDel)
l->fDel(v);
}
n = 1;
break;
} else if (rc < 0) {
n = -1;
break;
} else {
pp = &(*pp)->next;
}
}
slurm_rwlock_unlock(&l->mutex);
return n;
}
extern int list_delete_ptr(list_t *l, void *key)
{
list_node_t **pp;
void *v;
int n = 0;
xassert(l);
xassert(key);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
pp = &l->head;
while (*pp) {
if ((*pp)->data == key) {
if ((v = _list_node_destroy(l, pp))) {
if (l->fDel)
l->fDel(v);
n = 1;
break;
}
} else
pp = &(*pp)->next;
}
slurm_rwlock_unlock(&l->mutex);
return n;
}
extern int list_for_each(list_t *l, ListForF f, void *arg)
{
int max = -1; /* all values */
return list_for_each_max(l, &max, f, arg, 1, true);
}
extern int list_for_each_ro(list_t *l, ListForF f, void *arg)
{
int max = -1; /* all values */
return list_for_each_max(l, &max, f, arg, 1, false);
}
extern int list_for_each_nobreak(list_t *l, ListForF f, void *arg)
{
int max = -1; /* all values */
return list_for_each_max(l, &max, f, arg, 0, true);
}
extern int list_for_each_max(list_t *l, int *max, ListForF f, void *arg,
int break_on_fail, int write_lock)
{
list_node_t *p;
int n = 0;
bool failed = false;
LIST_THREAD_LOCK_DEF;
xassert(l != NULL);
xassert(f != NULL);
xassert(l->magic == LIST_MAGIC);
LIST_THREAD_LOCK(l, write_lock);
for (p = l->head; (*max == -1 || n < *max) && p; p = p->next) {
n++;
if (f(p->data, arg) < 0) {
failed = true;
if (break_on_fail)
break;
}
}
*max = l->count - n;
LIST_THREAD_UNLOCK(l, write_lock);
if (failed)
n = -n;
return n;
}
extern int list_flush(list_t *l)
{
return list_flush_max(l, -1);
}
extern int list_flush_max(list_t *l, int max)
{
list_node_t **pp;
void *v;
int n = 0;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
pp = &l->head;
for (int i = 0; (max < 0 || i < max) && *pp; i++) {
if ((v = _list_node_destroy(l, pp))) {
if (l->fDel)
l->fDel(v);
n++;
}
}
slurm_rwlock_unlock(&l->mutex);
return n;
}
extern void list_push(list_t *l, void *x)
{
xassert(l != NULL);
xassert(x != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
_list_node_create(l, &l->head, x);
slurm_rwlock_unlock(&l->mutex);
}
/*
* Handle translation between ListCmpF and signature required by qsort.
* glibc has this as __compar_fn_t, but that's non-standard so we define
* our own instead.
*/
typedef int (*ConstListCmpF) (__const void *, __const void *);
/*
* This function uses the libC qsort().
*/
extern void list_sort(list_t *l, ListCmpF f)
{
char **v;
int n;
int lsize;
void *e;
list_itr_t *i;
xassert(l != NULL);
xassert(f != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
if (l->count <= 1) {
slurm_rwlock_unlock(&l->mutex);
return;
}
lsize = l->count;
v = xmalloc(lsize * sizeof(char *));
n = 0;
while ((e = _list_node_destroy(l, &l->head))) {
v[n] = e;
++n;
}
qsort(v, n, sizeof(char *), (ConstListCmpF)f);
for (n = 0; n < lsize; n++) {
_list_node_create(l, l->tail, v[n]);
}
xfree(v);
/* Reset all iterators on the list to point
* to the head of the list.
*/
for (i = l->iNext; i; i = i->iNext) {
xassert(i->magic == LIST_ITR_MAGIC);
i->pos = i->list->head;
i->prev = &i->list->head;
}
slurm_rwlock_unlock(&l->mutex);
}
/*
* list_flip - not called list_reverse due to collision with MariaDB
*/
extern void list_flip(list_t *l)
{
list_node_t *old_head, *prev = NULL, *curr, *next = NULL;
list_itr_t *i;
xassert(l);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
if (l->count <= 1) {
slurm_rwlock_unlock(&l->mutex);
return;
}
old_head = curr = l->head;
while (curr) {
next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
l->head = prev;
l->tail = &old_head->next;
/*
* Reset all iterators on the list to point
* to the head of the list.
*/
for (i = l->iNext; i; i = i->iNext) {
xassert(i->magic == LIST_ITR_MAGIC);
i->pos = i->list->head;
i->prev = &i->list->head;
}
slurm_rwlock_unlock(&l->mutex);
}
extern void *list_pop(list_t *l)
{
void *v;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
v = _list_node_destroy(l, &l->head);
slurm_rwlock_unlock(&l->mutex);
return v;
}
extern void *list_peek(list_t *l)
{
void *v;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_rdlock(&l->mutex);
v = (l->head) ? l->head->data : NULL;
slurm_rwlock_unlock(&l->mutex);
return v;
}
/* list_enqueue() is aliased to list_append() */
/* list_dequeue() is aliased to list_pop() */
extern list_itr_t *list_iterator_create(list_t *l)
{
list_itr_t *i = xmalloc(sizeof(*i));
xassert(l != NULL);
i->magic = LIST_ITR_MAGIC;
i->list = l;
xassert(l->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&l->mutex);
i->pos = l->head;
i->prev = &l->head;
i->iNext = l->iNext;
l->iNext = i;
slurm_rwlock_unlock(&l->mutex);
return i;
}
extern void list_iterator_reset(list_itr_t *i)
{
xassert(i != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
xassert(i->list->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&i->list->mutex);
i->pos = i->list->head;
i->prev = &i->list->head;
slurm_rwlock_unlock(&i->list->mutex);
}
extern void list_iterator_destroy(list_itr_t *i)
{
list_itr_t **pi;
xassert(i != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
xassert(i->list->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&i->list->mutex);
for (pi = &i->list->iNext; *pi; pi = &(*pi)->iNext) {
xassert((*pi)->magic == LIST_ITR_MAGIC);
if (*pi == i) {
*pi = (*pi)->iNext;
break;
}
}
slurm_rwlock_unlock(&i->list->mutex);
i->magic = ~LIST_ITR_MAGIC;
xfree(i);
}
static void *_list_next_locked(list_itr_t *i)
{
list_node_t *p;
if ((p = i->pos))
i->pos = p->next;
if (*i->prev != p)
i->prev = &(*i->prev)->next;
return (p ? p->data : NULL);
}
extern void *list_next(list_itr_t *i)
{
void *rc;
xassert(i != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
xassert(i->list->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&i->list->mutex);
rc = _list_next_locked(i);
slurm_rwlock_unlock(&i->list->mutex);
return rc;
}
extern void *list_peek_next(list_itr_t *i)
{
list_node_t *p;
xassert(i != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
xassert(i->list->magic == LIST_MAGIC);
slurm_rwlock_rdlock(&i->list->mutex);
p = i->pos;
slurm_rwlock_unlock(&i->list->mutex);
return (p ? p->data : NULL);
}
extern void list_insert(list_itr_t *i, void *x)
{
xassert(i != NULL);
xassert(x != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
xassert(i->list->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&i->list->mutex);
_list_node_create(i->list, i->prev, x);
slurm_rwlock_unlock(&i->list->mutex);
}
extern void *list_find(list_itr_t *i, ListFindF f, void *key)
{
void *v;
xassert(i != NULL);
xassert(f != NULL);
xassert(key != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
slurm_rwlock_wrlock(&i->list->mutex);
xassert(i->list->magic == LIST_MAGIC);
while ((v = _list_next_locked(i)) && !f(v, key)) {;}
slurm_rwlock_unlock(&i->list->mutex);
return v;
}
extern void *list_remove(list_itr_t *i)
{
void *v = NULL;
xassert(i != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
xassert(i->list->magic == LIST_MAGIC);
slurm_rwlock_wrlock(&i->list->mutex);
if (*i->prev != i->pos)
v = _list_node_destroy(i->list, i->prev);
slurm_rwlock_unlock(&i->list->mutex);
return v;
}
extern int list_delete_item(list_itr_t *i)
{
void *v;
xassert(i != NULL);
xassert(i->magic == LIST_ITR_MAGIC);
if ((v = list_remove(i))) {
if (i->list->fDel)
i->list->fDel(v);
return 1;
}
return 0;
}
/*
* Inserts data pointed to by [x] into list [l] after [pp],
* the address of the previous node's "next" ptr.
* Returns a ptr to data [x], or NULL if insertion fails.
* This routine assumes the list is already locked upon entry.
*/
static void _list_node_create(list_t *l, list_node_t **pp, void *x)
{
list_node_t *p;
list_itr_t *i;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
xassert(_list_mutex_is_locked(&l->mutex));
xassert(pp != NULL);
xassert(x != NULL);
/*
* Ran out of spare nodes. Allocate another page worth of nodes,
* then link them together in the free_nodes list.
*/
if (!l->free_nodes) {
list_node_t *ln = xcalloc(nodes_in_page,
sizeof(list_node_t));
/*
* Index 0 is used to link the node_allocations together,
* and thus is unavailable for use as a list node.
*/
ln[0].next = l->node_allocations;
l->node_allocations = &ln[0];
l->free_nodes = &ln[1];
for (int i = 1; i < (nodes_in_page - 1); i++)
ln[i].next = &ln[i + 1];
}
p = l->free_nodes;
l->free_nodes = p->next;
p->data = x;
if (!(p->next = *pp))
l->tail = &p->next;
*pp = p;
l->count++;
for (i = l->iNext; i; i = i->iNext) {
xassert(i->magic == LIST_ITR_MAGIC);
if (i->prev == pp)
i->prev = &p->next;
else if (i->pos == p->next)
i->pos = p;
xassert((i->pos == *i->prev) ||
((*i->prev) && (i->pos == (*i->prev)->next)));
}
}
/*
* Removes the node pointed to by [*pp] from from list [l],
* where [pp] is the address of the previous node's "next" ptr.
* Returns the data ptr associated with list item being removed,
* or NULL if [*pp] points to the NULL element.
* This routine assumes the list is already locked upon entry.
*/
static void *_list_node_destroy(list_t *l, list_node_t **pp)
{
void *v;
list_node_t *p;
list_itr_t *i;
xassert(l != NULL);
xassert(l->magic == LIST_MAGIC);
xassert(_list_mutex_is_locked(&l->mutex));
xassert(pp != NULL);
if (!(p = *pp))
return NULL;
v = p->data;
if (!(*pp = p->next))
l->tail = pp;
l->count--;
for (i = l->iNext; i; i = i->iNext) {
xassert(i->magic == LIST_ITR_MAGIC);
if (i->pos == p)
i->pos = p->next, i->prev = pp;
else if (i->prev == &p->next)
i->prev = pp;
xassert((i->pos == *i->prev) ||
((*i->prev) && (i->pos == (*i->prev)->next)));
}
/* push into free_nodes list */
p->next = l->free_nodes;
l->free_nodes = p;
return v;
}
#ifndef NDEBUG
static int _list_mutex_is_locked(pthread_rwlock_t *mutex)
{
/* Returns true if the mutex is locked; o/w, returns false.
*/
int rc;
xassert(mutex != NULL);
rc = slurm_rwlock_trywrlock(mutex);
return(rc == EBUSY ? 1 : 0);
}
#endif /* !NDEBUG */