mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-25 18:59:33 +03:00
716154c592
Updated AUTHORS, COPYING, DISCLAIMER, and INSTALL files. Added standardized headers to all source file to clearly indicate the copyright, license, and to give credit where credit is due.
828 lines
20 KiB
C
828 lines
20 KiB
C
/*****************************************************************************
|
|
* Copyright (C) 2007-2010 Lawrence Livermore National Security, LLC.
|
|
* Copyright (C) 2001-2007 The Regents of the University of California.
|
|
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
|
|
* Written by Chris Dunlap <cdunlap@llnl.gov>.
|
|
* UCRL-CODE-235197
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*****************************************************************************
|
|
* Refer to "list.h" for documentation on public functions.
|
|
*****************************************************************************/
|
|
|
|
#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 */
|