/*****************************************************************************
 *  $Id: list.c,v 1.28 2003/05/20 23:53:22 dun Exp $
 *****************************************************************************
 *  $LSDId: list.c,v 1.28 2003/05/20 23:53:22 dun Exp $
 *****************************************************************************
 *  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.
 *
 *  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.
 *****************************************************************************/


#ifdef HAVE_CONFIG_H
#  include "config.h"
#endif /* HAVE_CONFIG_H */

#ifdef WITH_PTHREADS
#  include <pthread.h>
#endif /* WITH_PTHREADS */

#include <assert.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"
#include "macros.h"
#include "xmalloc.h"
#include "src/common/log.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_append,	slurm_list_append);
strong_alias(list_append_list,	slurm_list_append_list);
strong_alias(list_transfer,	slurm_list_transfer);
strong_alias(list_prepend,	slurm_list_prepend);
strong_alias(list_find_first,	slurm_list_find_first);
strong_alias(list_delete_all,	slurm_list_delete_all);
strong_alias(list_for_each,	slurm_list_for_each);
strong_alias(list_flush,	slurm_list_flush);
strong_alias(list_sort,		slurm_list_sort);
strong_alias(list_push,		slurm_list_push);
strong_alias(list_pop,		slurm_list_pop);
strong_alias(list_peek,		slurm_list_peek);
strong_alias(list_enqueue,	slurm_list_enqueue);
strong_alias(list_dequeue,	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_delete_item,	slurm_list_delete_item);
strong_alias(list_install_fork_handlers, slurm_list_install_fork_handlers);
/*********************
 *  lsd_fatal_error  *
 *********************/

#include <unistd.h>
#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
	static void lsd_fatal_error(char *file, int line, char *mesg)
	{
		log_fatal(file, line, mesg, strerror(errno));
	}
#  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
	static void * lsd_nomem_error(char *file, int line, char *mesg)
	{
		
		log_oom(file, line, mesg);
		abort();
		return NULL;
	}
#  endif /* !lsd_nomem_error */
#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */


/***************
 *  Constants  *
 ***************/

/**************************************************************************\
 * To test for memory leaks associated with the use of list functions (not
 * necessarily within the list module), set MEMORY_LEAK_DEBUG to 1 using
 * "configure --enable-memory-leak" then execute
 * > valgrind --tool=memcheck --leak-check=yes --num-callers=6
 *    --leak-resolution=med [slurmctld | slurmd] -D
 *
 * Do not leave MEMORY_LEAK_DEBUG set for production use
 *
 * When MEMORY_LEAK_DEBUG is set to 1, the cache is disabled. Each memory
 * request will be satisified with a separate xmalloc request. When the
 * memory is no longer required, it is immeditately freed. This means
 * valgrind can identify where exactly any leak associated with the use
 * of the list functions originates.
\**************************************************************************/
#ifdef MEMORY_LEAK_DEBUG
#  define LIST_ALLOC 1
#else
#  define LIST_ALLOC 128
#endif
#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);
}


int
list_append_list (List l, List sub)
{
    ListIterator itr;
    void *v;
    int n = 0;

    assert(l != NULL);
    assert(l->fDel == NULL);
    assert(sub != NULL);
    itr = list_iterator_create(sub);
    while((v = list_next(itr))) {
	if (list_append(l, v))
	    n++;
	else
	    break;
    }
    list_iterator_destroy(itr);

    return n;
}

int
list_transfer (List l, List sub)
{
    void *v;
    int n = 0;

    assert(l != NULL);
    assert(sub != NULL);
    assert(l->fDel == sub->fDel);
    while((v = list_pop(sub))) {
	if (list_append(l, v))
	    n++;
	else {
	    if (l->fDel)
		l->fDel(v);
	    break;
	}
    }

    return n;
}

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);
    assert(key != 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);
}


int
list_flush (List l)
{
    ListNode *pp;
    void *v;
    int n = 0;

    assert(l != NULL);
    list_mutex_lock(&l->mutex);
    assert(l->magic == LIST_MAGIC);
    pp = &l->head;
    while (*pp) {
	if ((v = list_node_destroy(l, pp))) {
	    if (l->fDel)
		l->fDel(v);
	    n++;
	}
    }
    list_mutex_unlock(&l->mutex);
    return(n);
}

void
list_sort (List l, ListCmpF f)
{
    ListIterator it;

    ListNode p, q, e, tail, head;
    int insize, nmerges, psize, qsize, i;

    assert(l != NULL);
    assert(f != NULL);
    list_mutex_lock(&l->mutex);
    assert(l->magic == LIST_MAGIC);
    head = l->head;
    if (l->count > 1) {
        insize=1;
        while(1) {
           p = head;
           head = NULL;
           tail = NULL;


           nmerges = 0;
           while(p) {
              nmerges++;
              q = p;

              psize=0;
              for (i = 0; i < insize; i++) {
                psize++;
                q = q->next;

                if (!q) break;
              }
              qsize = insize;
              while (psize > 0 || (qsize > 0 && q)) {
                 if (psize == 0) {
                    e = q;
                    q = q->next;
                    qsize--;
                 } else if (qsize == 0 || !q ) {
                    e = p;
                    p = p->next;
                    psize--;
                 } else if (f(p->data,q->data) <= 0) {
                    e = p;
                    p = p->next;
                    psize--;
                 } else {
                    e = q;
                    q = q->next;
                    qsize--;
                 }
                 if (tail) {
                    tail->next = e;
                 } else {
                    head = e;
                 }
                 tail = e;
              }
              p = q;

           }
           tail->next = NULL;
           if(nmerges <= 1) {
              l->head = head;
	      l->tail = &tail->next;
              for (it=l->iNext; it; it=it->iNext) {
                  assert(it->magic == LIST_MAGIC);
                  it->pos = it->list->head;
                  it->prev = &it->list->head;
              }

              list_mutex_unlock(&l->mutex);
              return;
           }
           insize *=2;
        }
    }

    list_mutex_unlock(&l->mutex);
    return;
}

void
list_sort2 (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_pop_top (List l, ListCmpF f)
{
    void *v = NULL;
    ListNode *pp, *pTop;
    assert(l != NULL);
    assert(f != NULL);
    list_mutex_lock(&l->mutex);
    assert(l->magic == LIST_MAGIC);
    pTop = &l->head;
    if (*pTop) {
        pp = &(*pTop)->next;
        while (*pp) {
            if (f((*pp)->data, (*pTop)->data) > 0)
                pTop = pp;
            pp = &(*pp)->next;
        }
        v = list_node_destroy(l, pTop);
    }
    list_mutex_unlock(&l->mutex);
    return (v);
}


void *
list_pop_bottom (List l, ListCmpF f)
{
    void *v = NULL;
    ListNode *pp, *pBottom;
    assert(l != NULL);
    assert(f != NULL);
    list_mutex_lock(&l->mutex);
    assert(l->magic == LIST_MAGIC);
    pBottom = &l->head;
    if (*pBottom) {
        pp = &(*pBottom)->next;
        while (*pp) {
            if (f((*pp)->data, (*pBottom)->data) < 0)
                pBottom = pp;
            pp = &(*pp)->next;
        }
        v = list_node_destroy(l, pBottom);
    }
    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(key != 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_item (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 >= sizeof(void *));
    assert(pfreelist != NULL);
    assert(LIST_ALLOC > 0);
    list_mutex_lock(&list_free_lock);
    if (!*pfree) {
	if ((*pfree = xmalloc(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].
 */
#ifdef MEMORY_LEAK_DEBUG
    xfree(x);
#else
    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);
#endif
    return;
}

#ifdef WITH_PTHREADS
static void
list_reinit_mutexes (void)
{
	list_mutex_init(&list_free_lock);
}

void list_install_fork_handlers (void)
{
	int err;
	if ((err = pthread_atfork(NULL, NULL, &list_reinit_mutexes))) {
		lsd_fatal_error(__FILE__, __LINE__, "list atfork install");
		abort();
	}
	return;
}
#else
void list_install_fork_handlers (void)
{
	return;
}
#endif

#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 */
