| /***************************************************************************** |
| * $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 <stdlib.h> |
| #include <string.h> |
| #include "list.h" |
| #include "macros.h" |
| #include "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_append, slurm_list_append); |
| 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_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, slurm_list_delete); |
| 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 |
| # 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 * |
| ***************/ |
| |
| /**************************************************************************\ |
| * 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); |
| } |
| |
| |
| 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); |
| } |
| |
| |
| 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(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 (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"); |
| 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 */ |