mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-11-18 10:21:01 +03:00
0cbaeb117a
We need dependent packages to be able to include spl_config.h so they can leverage the configure checks the SPL has done. This is important because several of the spl headers need the results of these checks to work properly. Unfortunately, the autoheader build product is always private to a particular build and defined certain common things. (PACKAGE, VERSION, etc). This prevents other packages which also use autoheader from being include because the definitions conflict. To avoid this problem the SPL build system leverage AH_BOTTOM to include a spl_unconfig.h at the botton of the autoheader build product. This custom include undefs all known shared symbols to prevent the confict. This does however mean that those definition are also not availble to the SPL package either. The SPL package therefore uses the equivilant SPL_META_* definitions.
834 lines
21 KiB
C
834 lines
21 KiB
C
/*****************************************************************************
|
|
* $Id: list.c 3709 2006-11-29 00:51:22Z dun $
|
|
*****************************************************************************
|
|
* Copyright (C) 2001-2002 The Regents of the University of California.
|
|
* 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.
|
|
*
|
|
* 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.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
|
*****************************************************************************
|
|
* Refer to "list.h" for documentation on public functions.
|
|
*****************************************************************************/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
# include <spl_config.h>
|
|
#endif /* HAVE_CONFIG_H */
|
|
|
|
#ifdef WITH_PTHREADS
|
|
# include <pthread.h>
|
|
#endif /* WITH_PTHREADS */
|
|
|
|
#include <assert.h>
|
|
#include <errno.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include "list.h"
|
|
|
|
|
|
/*********************
|
|
* lsd_fatal_error *
|
|
*********************/
|
|
|
|
#ifdef WITH_LSD_FATAL_ERROR_FUNC
|
|
# undef lsd_fatal_error
|
|
extern void lsd_fatal_error(char *file, int line, char *mesg);
|
|
#else /* !WITH_LSD_FATAL_ERROR_FUNC */
|
|
# ifndef lsd_fatal_error
|
|
# include <errno.h>
|
|
# include <stdio.h>
|
|
# include <string.h>
|
|
# define lsd_fatal_error(file, line, mesg) \
|
|
do { \
|
|
fprintf(stderr, "ERROR: [%s:%d] %s: %s\n", \
|
|
file, line, mesg, strerror(errno)); \
|
|
} while (0)
|
|
# endif /* !lsd_fatal_error */
|
|
#endif /* !WITH_LSD_FATAL_ERROR_FUNC */
|
|
|
|
|
|
/*********************
|
|
* lsd_nomem_error *
|
|
*********************/
|
|
|
|
#ifdef WITH_LSD_NOMEM_ERROR_FUNC
|
|
# undef lsd_nomem_error
|
|
extern void * lsd_nomem_error(char *file, int line, char *mesg);
|
|
#else /* !WITH_LSD_NOMEM_ERROR_FUNC */
|
|
# ifndef lsd_nomem_error
|
|
# define lsd_nomem_error(file, line, mesg) (NULL)
|
|
# endif /* !lsd_nomem_error */
|
|
#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */
|
|
|
|
|
|
/***************
|
|
* Constants *
|
|
***************/
|
|
|
|
#define LIST_ALLOC 32
|
|
#define LIST_MAGIC 0xDEADBEEF
|
|
|
|
|
|
/****************
|
|
* Data Types *
|
|
****************/
|
|
|
|
struct listNode {
|
|
void *data; /* node's data */
|
|
struct listNode *next; /* next node in list */
|
|
};
|
|
|
|
struct listIterator {
|
|
struct list *list; /* the list being iterated */
|
|
struct listNode *pos; /* the next node to be iterated */
|
|
struct listNode **prev; /* addr of 'next' ptr to prv It node */
|
|
struct listIterator *iNext; /* iterator chain for list_destroy() */
|
|
#ifndef NDEBUG
|
|
unsigned int magic; /* sentinel for asserting validity */
|
|
#endif /* !NDEBUG */
|
|
};
|
|
|
|
struct list {
|
|
struct listNode *head; /* head of the list */
|
|
struct listNode **tail; /* addr of last node's 'next' ptr */
|
|
struct listIterator *iNext; /* iterator chain for list_destroy() */
|
|
ListDelF fDel; /* function to delete node data */
|
|
int count; /* number of nodes in list */
|
|
#ifdef WITH_PTHREADS
|
|
pthread_mutex_t mutex; /* mutex to protect access to list */
|
|
#endif /* WITH_PTHREADS */
|
|
#ifndef NDEBUG
|
|
unsigned int magic; /* sentinel for asserting validity */
|
|
#endif /* !NDEBUG */
|
|
};
|
|
|
|
typedef struct listNode * ListNode;
|
|
|
|
|
|
/****************
|
|
* Prototypes *
|
|
****************/
|
|
|
|
static void * list_node_create (List l, ListNode *pp, void *x);
|
|
static void * list_node_destroy (List l, ListNode *pp);
|
|
static List list_alloc (void);
|
|
static void list_free (List l);
|
|
static ListNode list_node_alloc (void);
|
|
static void list_node_free (ListNode p);
|
|
static ListIterator list_iterator_alloc (void);
|
|
static void list_iterator_free (ListIterator i);
|
|
static void * list_alloc_aux (int size, void *pfreelist);
|
|
static void list_free_aux (void *x, void *pfreelist);
|
|
|
|
|
|
/***************
|
|
* Variables *
|
|
***************/
|
|
|
|
static List list_free_lists = NULL;
|
|
static ListNode list_free_nodes = NULL;
|
|
static ListIterator list_free_iterators = NULL;
|
|
|
|
#ifdef WITH_PTHREADS
|
|
static pthread_mutex_t list_free_lock = PTHREAD_MUTEX_INITIALIZER;
|
|
#endif /* WITH_PTHREADS */
|
|
|
|
|
|
/************
|
|
* Macros *
|
|
************/
|
|
|
|
#ifdef WITH_PTHREADS
|
|
|
|
# define list_mutex_init(mutex) \
|
|
do { \
|
|
int e = pthread_mutex_init(mutex, NULL); \
|
|
if (e != 0) { \
|
|
errno = e; \
|
|
lsd_fatal_error(__FILE__, __LINE__, "list mutex init"); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
# define list_mutex_lock(mutex) \
|
|
do { \
|
|
int e = pthread_mutex_lock(mutex); \
|
|
if (e != 0) { \
|
|
errno = e; \
|
|
lsd_fatal_error(__FILE__, __LINE__, "list mutex lock"); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
# define list_mutex_unlock(mutex) \
|
|
do { \
|
|
int e = pthread_mutex_unlock(mutex); \
|
|
if (e != 0) { \
|
|
errno = e; \
|
|
lsd_fatal_error(__FILE__, __LINE__, "list mutex unlock"); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
# define list_mutex_destroy(mutex) \
|
|
do { \
|
|
int e = pthread_mutex_destroy(mutex); \
|
|
if (e != 0) { \
|
|
errno = e; \
|
|
lsd_fatal_error(__FILE__, __LINE__, "list mutex destroy"); \
|
|
abort(); \
|
|
} \
|
|
} while (0)
|
|
|
|
# ifndef NDEBUG
|
|
static int list_mutex_is_locked (pthread_mutex_t *mutex);
|
|
# endif /* !NDEBUG */
|
|
|
|
#else /* !WITH_PTHREADS */
|
|
|
|
# define list_mutex_init(mutex)
|
|
# define list_mutex_lock(mutex)
|
|
# define list_mutex_unlock(mutex)
|
|
# define list_mutex_destroy(mutex)
|
|
# define list_mutex_is_locked(mutex) (1)
|
|
|
|
#endif /* !WITH_PTHREADS */
|
|
|
|
|
|
/***************
|
|
* Functions *
|
|
***************/
|
|
|
|
List
|
|
list_create (ListDelF f)
|
|
{
|
|
List l;
|
|
|
|
if (!(l = list_alloc()))
|
|
return(lsd_nomem_error(__FILE__, __LINE__, "list create"));
|
|
l->head = NULL;
|
|
l->tail = &l->head;
|
|
l->iNext = NULL;
|
|
l->fDel = f;
|
|
l->count = 0;
|
|
list_mutex_init(&l->mutex);
|
|
assert(l->magic = LIST_MAGIC); /* set magic via assert abuse */
|
|
return(l);
|
|
}
|
|
|
|
|
|
void
|
|
list_destroy (List l)
|
|
{
|
|
ListIterator i, iTmp;
|
|
ListNode p, pTmp;
|
|
|
|
assert(l != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
i = l->iNext;
|
|
while (i) {
|
|
assert(i->magic == LIST_MAGIC);
|
|
iTmp = i->iNext;
|
|
assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */
|
|
list_iterator_free(i);
|
|
i = iTmp;
|
|
}
|
|
p = l->head;
|
|
while (p) {
|
|
pTmp = p->next;
|
|
if (p->data && l->fDel)
|
|
l->fDel(p->data);
|
|
list_node_free(p);
|
|
p = pTmp;
|
|
}
|
|
assert(l->magic = ~LIST_MAGIC); /* clear magic via assert abuse */
|
|
list_mutex_unlock(&l->mutex);
|
|
list_mutex_destroy(&l->mutex);
|
|
list_free(l);
|
|
return;
|
|
}
|
|
|
|
|
|
int
|
|
list_is_empty (List l)
|
|
{
|
|
int n;
|
|
|
|
assert(l != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
n = l->count;
|
|
list_mutex_unlock(&l->mutex);
|
|
return(n == 0);
|
|
}
|
|
|
|
|
|
int
|
|
list_count (List l)
|
|
{
|
|
int n;
|
|
|
|
assert(l != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
n = l->count;
|
|
list_mutex_unlock(&l->mutex);
|
|
return(n);
|
|
}
|
|
|
|
|
|
void *
|
|
list_append (List l, void *x)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
assert(x != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = list_node_create(l, l->tail, x);
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_prepend (List l, void *x)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
assert(x != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = list_node_create(l, &l->head, x);
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_find_first (List l, ListFindF f, void *key)
|
|
{
|
|
ListNode p;
|
|
void *v = NULL;
|
|
|
|
assert(l != NULL);
|
|
assert(f != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
for (p=l->head; p; p=p->next) {
|
|
if (f(p->data, key)) {
|
|
v = p->data;
|
|
break;
|
|
}
|
|
}
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
int
|
|
list_delete_all (List l, ListFindF f, void *key)
|
|
{
|
|
ListNode *pp;
|
|
void *v;
|
|
int n = 0;
|
|
|
|
assert(l != NULL);
|
|
assert(f != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
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;
|
|
}
|
|
}
|
|
list_mutex_unlock(&l->mutex);
|
|
return(n);
|
|
}
|
|
|
|
|
|
int
|
|
list_for_each (List l, ListForF f, void *arg)
|
|
{
|
|
ListNode p;
|
|
int n = 0;
|
|
|
|
assert(l != NULL);
|
|
assert(f != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
for (p=l->head; p; p=p->next) {
|
|
n++;
|
|
if (f(p->data, arg) < 0) {
|
|
n = -n;
|
|
break;
|
|
}
|
|
}
|
|
list_mutex_unlock(&l->mutex);
|
|
return(n);
|
|
}
|
|
|
|
|
|
void
|
|
list_sort (List l, ListCmpF f)
|
|
{
|
|
/* Note: Time complexity O(n^2).
|
|
*/
|
|
ListNode *pp, *ppPrev, *ppPos, pTmp;
|
|
ListIterator i;
|
|
|
|
assert(l != NULL);
|
|
assert(f != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
if (l->count > 1) {
|
|
ppPrev = &l->head;
|
|
pp = &(*ppPrev)->next;
|
|
while (*pp) {
|
|
if (f((*pp)->data, (*ppPrev)->data) < 0) {
|
|
ppPos = &l->head;
|
|
while (f((*pp)->data, (*ppPos)->data) >= 0)
|
|
ppPos = &(*ppPos)->next;
|
|
pTmp = (*pp)->next;
|
|
(*pp)->next = *ppPos;
|
|
*ppPos = *pp;
|
|
*pp = pTmp;
|
|
if (ppPrev == ppPos)
|
|
ppPrev = &(*ppPrev)->next;
|
|
}
|
|
else {
|
|
ppPrev = pp;
|
|
pp = &(*pp)->next;
|
|
}
|
|
}
|
|
l->tail = pp;
|
|
|
|
for (i=l->iNext; i; i=i->iNext) {
|
|
assert(i->magic == LIST_MAGIC);
|
|
i->pos = i->list->head;
|
|
i->prev = &i->list->head;
|
|
}
|
|
}
|
|
list_mutex_unlock(&l->mutex);
|
|
return;
|
|
}
|
|
|
|
|
|
void *
|
|
list_push (List l, void *x)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
assert(x != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = list_node_create(l, &l->head, x);
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_pop (List l)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = list_node_destroy(l, &l->head);
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_peek (List l)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = (l->head) ? l->head->data : NULL;
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_enqueue (List l, void *x)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
assert(x != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = list_node_create(l, l->tail, x);
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_dequeue (List l)
|
|
{
|
|
void *v;
|
|
|
|
assert(l != NULL);
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
v = list_node_destroy(l, &l->head);
|
|
list_mutex_unlock(&l->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
ListIterator
|
|
list_iterator_create (List l)
|
|
{
|
|
ListIterator i;
|
|
|
|
assert(l != NULL);
|
|
if (!(i = list_iterator_alloc()))
|
|
return(lsd_nomem_error(__FILE__, __LINE__, "list iterator create"));
|
|
i->list = l;
|
|
list_mutex_lock(&l->mutex);
|
|
assert(l->magic == LIST_MAGIC);
|
|
i->pos = l->head;
|
|
i->prev = &l->head;
|
|
i->iNext = l->iNext;
|
|
l->iNext = i;
|
|
assert(i->magic = LIST_MAGIC); /* set magic via assert abuse */
|
|
list_mutex_unlock(&l->mutex);
|
|
return(i);
|
|
}
|
|
|
|
|
|
void
|
|
list_iterator_reset (ListIterator i)
|
|
{
|
|
assert(i != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
list_mutex_lock(&i->list->mutex);
|
|
assert(i->list->magic == LIST_MAGIC);
|
|
i->pos = i->list->head;
|
|
i->prev = &i->list->head;
|
|
list_mutex_unlock(&i->list->mutex);
|
|
return;
|
|
}
|
|
|
|
|
|
void
|
|
list_iterator_destroy (ListIterator i)
|
|
{
|
|
ListIterator *pi;
|
|
|
|
assert(i != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
list_mutex_lock(&i->list->mutex);
|
|
assert(i->list->magic == LIST_MAGIC);
|
|
for (pi=&i->list->iNext; *pi; pi=&(*pi)->iNext) {
|
|
assert((*pi)->magic == LIST_MAGIC);
|
|
if (*pi == i) {
|
|
*pi = (*pi)->iNext;
|
|
break;
|
|
}
|
|
}
|
|
list_mutex_unlock(&i->list->mutex);
|
|
assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */
|
|
list_iterator_free(i);
|
|
return;
|
|
}
|
|
|
|
|
|
void *
|
|
list_next (ListIterator i)
|
|
{
|
|
ListNode p;
|
|
|
|
assert(i != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
list_mutex_lock(&i->list->mutex);
|
|
assert(i->list->magic == LIST_MAGIC);
|
|
if ((p = i->pos))
|
|
i->pos = p->next;
|
|
if (*i->prev != p)
|
|
i->prev = &(*i->prev)->next;
|
|
list_mutex_unlock(&i->list->mutex);
|
|
return(p ? p->data : NULL);
|
|
}
|
|
|
|
|
|
void *
|
|
list_insert (ListIterator i, void *x)
|
|
{
|
|
void *v;
|
|
|
|
assert(i != NULL);
|
|
assert(x != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
list_mutex_lock(&i->list->mutex);
|
|
assert(i->list->magic == LIST_MAGIC);
|
|
v = list_node_create(i->list, i->prev, x);
|
|
list_mutex_unlock(&i->list->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_find (ListIterator i, ListFindF f, void *key)
|
|
{
|
|
void *v;
|
|
|
|
assert(i != NULL);
|
|
assert(f != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
while ((v=list_next(i)) && !f(v,key)) {;}
|
|
return(v);
|
|
}
|
|
|
|
|
|
void *
|
|
list_remove (ListIterator i)
|
|
{
|
|
void *v = NULL;
|
|
|
|
assert(i != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
list_mutex_lock(&i->list->mutex);
|
|
assert(i->list->magic == LIST_MAGIC);
|
|
if (*i->prev != i->pos)
|
|
v = list_node_destroy(i->list, i->prev);
|
|
list_mutex_unlock(&i->list->mutex);
|
|
return(v);
|
|
}
|
|
|
|
|
|
int
|
|
list_delete (ListIterator i)
|
|
{
|
|
void *v;
|
|
|
|
assert(i != NULL);
|
|
assert(i->magic == LIST_MAGIC);
|
|
if ((v = list_remove(i))) {
|
|
if (i->list->fDel)
|
|
i->list->fDel(v);
|
|
return(1);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
|
|
static void *
|
|
list_node_create (List l, ListNode *pp, void *x)
|
|
{
|
|
/* 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.
|
|
*/
|
|
ListNode p;
|
|
ListIterator i;
|
|
|
|
assert(l != NULL);
|
|
assert(l->magic == LIST_MAGIC);
|
|
assert(list_mutex_is_locked(&l->mutex));
|
|
assert(pp != NULL);
|
|
assert(x != NULL);
|
|
if (!(p = list_node_alloc()))
|
|
return(lsd_nomem_error(__FILE__, __LINE__, "list node create"));
|
|
p->data = x;
|
|
if (!(p->next = *pp))
|
|
l->tail = &p->next;
|
|
*pp = p;
|
|
l->count++;
|
|
for (i=l->iNext; i; i=i->iNext) {
|
|
assert(i->magic == LIST_MAGIC);
|
|
if (i->prev == pp)
|
|
i->prev = &p->next;
|
|
else if (i->pos == p->next)
|
|
i->pos = p;
|
|
assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next));
|
|
}
|
|
return(x);
|
|
}
|
|
|
|
|
|
static void *
|
|
list_node_destroy (List l, ListNode *pp)
|
|
{
|
|
/* 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.
|
|
*/
|
|
void *v;
|
|
ListNode p;
|
|
ListIterator i;
|
|
|
|
assert(l != NULL);
|
|
assert(l->magic == LIST_MAGIC);
|
|
assert(list_mutex_is_locked(&l->mutex));
|
|
assert(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) {
|
|
assert(i->magic == LIST_MAGIC);
|
|
if (i->pos == p)
|
|
i->pos = p->next, i->prev = pp;
|
|
else if (i->prev == &p->next)
|
|
i->prev = pp;
|
|
assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next));
|
|
}
|
|
list_node_free(p);
|
|
return(v);
|
|
}
|
|
|
|
|
|
static List
|
|
list_alloc (void)
|
|
{
|
|
return(list_alloc_aux(sizeof(struct list), &list_free_lists));
|
|
}
|
|
|
|
|
|
static void
|
|
list_free (List l)
|
|
{
|
|
list_free_aux(l, &list_free_lists);
|
|
return;
|
|
}
|
|
|
|
|
|
static ListNode
|
|
list_node_alloc (void)
|
|
{
|
|
return(list_alloc_aux(sizeof(struct listNode), &list_free_nodes));
|
|
}
|
|
|
|
|
|
static void
|
|
list_node_free (ListNode p)
|
|
{
|
|
list_free_aux(p, &list_free_nodes);
|
|
return;
|
|
}
|
|
|
|
|
|
static ListIterator
|
|
list_iterator_alloc (void)
|
|
{
|
|
return(list_alloc_aux(sizeof(struct listIterator), &list_free_iterators));
|
|
}
|
|
|
|
|
|
static void
|
|
list_iterator_free (ListIterator i)
|
|
{
|
|
list_free_aux(i, &list_free_iterators);
|
|
return;
|
|
}
|
|
|
|
|
|
static void *
|
|
list_alloc_aux (int size, void *pfreelist)
|
|
{
|
|
/* Allocates an object of [size] bytes from the freelist [*pfreelist].
|
|
* Memory is added to the freelist in chunks of size LIST_ALLOC.
|
|
* Returns a ptr to the object, or NULL if the memory request fails.
|
|
*/
|
|
void **px;
|
|
void **pfree = pfreelist;
|
|
void **plast;
|
|
|
|
assert(sizeof(char) == 1);
|
|
assert(size >= (int)sizeof(void *));
|
|
assert(pfreelist != NULL);
|
|
assert(LIST_ALLOC > 0);
|
|
list_mutex_lock(&list_free_lock);
|
|
if (!*pfree) {
|
|
if ((*pfree = malloc(LIST_ALLOC * size))) {
|
|
px = *pfree;
|
|
plast = (void **) ((char *) *pfree + ((LIST_ALLOC - 1) * size));
|
|
while (px < plast)
|
|
*px = (char *) px + size, px = *px;
|
|
*plast = NULL;
|
|
}
|
|
}
|
|
if ((px = *pfree))
|
|
*pfree = *px;
|
|
else
|
|
errno = ENOMEM;
|
|
list_mutex_unlock(&list_free_lock);
|
|
return(px);
|
|
}
|
|
|
|
|
|
static void
|
|
list_free_aux (void *x, void *pfreelist)
|
|
{
|
|
/* Frees the object [x], returning it to the freelist [*pfreelist].
|
|
*/
|
|
void **px = x;
|
|
void **pfree = pfreelist;
|
|
|
|
assert(x != NULL);
|
|
assert(pfreelist != NULL);
|
|
list_mutex_lock(&list_free_lock);
|
|
*px = *pfree;
|
|
*pfree = px;
|
|
list_mutex_unlock(&list_free_lock);
|
|
return;
|
|
}
|
|
|
|
|
|
#ifndef NDEBUG
|
|
#ifdef WITH_PTHREADS
|
|
static int
|
|
list_mutex_is_locked (pthread_mutex_t *mutex)
|
|
{
|
|
/* Returns true if the mutex is locked; o/w, returns false.
|
|
*/
|
|
int rc;
|
|
|
|
assert(mutex != NULL);
|
|
rc = pthread_mutex_trylock(mutex);
|
|
return(rc == EBUSY ? 1 : 0);
|
|
}
|
|
#endif /* WITH_PTHREADS */
|
|
#endif /* !NDEBUG */
|