PageRenderTime 67ms CodeModel.GetById 2ms app.highlight 60ms RepoModel.GetById 1ms app.codeStats 0ms

/src/common/list.c

https://code.google.com/
C | 838 lines | 632 code | 134 blank | 72 comment | 155 complexity | 773918d6c103aa9688c417098abf584e MD5 | raw file
  1/*****************************************************************************
  2 *  $Id$
  3 *****************************************************************************
  4 *  $LSDId: list.c,v 1.28 2003/05/20 23:53:22 dun Exp $
  5 *****************************************************************************
  6 *  Copyright (C) 2001-2006 The Regents of the University of California.
  7 *  Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
  8 *  Written by Chris Dunlap <cdunlap@llnl.gov>.
  9 *  
 10 *  This file is from LSD-Tools, the LLNL Software Development Toolbox.
 11 *
 12 *  LSD-Tools is free software; you can redistribute it and/or modify it under
 13 *  the terms of the GNU General Public License as published by the Free
 14 *  Software Foundation; either version 2 of the License, or (at your option)
 15 *  any later version.
 16 *
 17 *  LSD-Tools is distributed in the hope that it will be useful, but WITHOUT
 18 *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 19 *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 20 *  more details.
 21 *
 22 *  You should have received a copy of the GNU General Public License along
 23 *  with LSD-Tools; if not, write to the Free Software Foundation, Inc.,
 24 *  59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.
 25 *****************************************************************************
 26 *  Refer to "list.h" for documentation on public functions.
 27 *****************************************************************************/
 28
 29
 30#ifdef HAVE_CONFIG_H
 31#  include "config.h"
 32#endif /* HAVE_CONFIG_H */
 33
 34#ifdef WITH_PTHREADS
 35#  include <pthread.h>
 36#endif /* WITH_PTHREADS */
 37
 38#include <assert.h>
 39#include <errno.h>
 40#include <stdlib.h>
 41#include <string.h>
 42#include "list.h"
 43
 44
 45/*********************
 46 *  lsd_fatal_error  *
 47 *********************/
 48
 49#ifdef WITH_LSD_FATAL_ERROR_FUNC
 50#  undef lsd_fatal_error
 51   extern void lsd_fatal_error(char *file, int line, char *mesg);
 52#else /* !WITH_LSD_FATAL_ERROR_FUNC */
 53#  ifndef lsd_fatal_error
 54#    include <errno.h>
 55#    include <stdio.h>
 56#    include <string.h>
 57#    define lsd_fatal_error(file, line, mesg)                                 \
 58       do {                                                                   \
 59           fprintf(stderr, "ERROR: [%s:%d] %s: %s\n",                         \
 60                   file, line, mesg, strerror(errno));                        \
 61       } while (0)
 62#  endif /* !lsd_fatal_error */
 63#endif /* !WITH_LSD_FATAL_ERROR_FUNC */
 64
 65
 66/*********************
 67 *  lsd_nomem_error  *
 68 *********************/
 69
 70#ifdef WITH_LSD_NOMEM_ERROR_FUNC
 71#  undef lsd_nomem_error
 72   extern void * lsd_nomem_error(char *file, int line, char *mesg);
 73#else /* !WITH_LSD_NOMEM_ERROR_FUNC */
 74#  ifndef lsd_nomem_error
 75#    define lsd_nomem_error(file, line, mesg) (NULL)
 76#  endif /* !lsd_nomem_error */
 77#endif /* !WITH_LSD_NOMEM_ERROR_FUNC */
 78
 79
 80/***************
 81 *  Constants  *
 82 ***************/
 83
 84#define LIST_ALLOC 32
 85#define LIST_MAGIC 0xDEADBEEF
 86
 87
 88/****************
 89 *  Data Types  *
 90 ****************/
 91
 92struct listNode {
 93    void                 *data;         /* node's data                       */
 94    struct listNode      *next;         /* next node in list                 */
 95};
 96
 97struct listIterator {
 98    struct list          *list;         /* the list being iterated           */
 99    struct listNode      *pos;          /* the next node to be iterated      */
100    struct listNode     **prev;         /* addr of 'next' ptr to prv It node */
101    struct listIterator  *iNext;        /* iterator chain for list_destroy() */
102#ifndef NDEBUG
103    unsigned int          magic;        /* sentinel for asserting validity   */
104#endif /* !NDEBUG */
105};
106
107struct list {
108    struct listNode      *head;         /* head of the list                  */
109    struct listNode     **tail;         /* addr of last node's 'next' ptr    */
110    struct listIterator  *iNext;        /* iterator chain for list_destroy() */
111    ListDelF              fDel;         /* function to delete node data      */
112    int                   count;        /* number of nodes in list           */
113#ifdef WITH_PTHREADS
114    pthread_mutex_t       mutex;        /* mutex to protect access to list   */
115#endif /* WITH_PTHREADS */
116#ifndef NDEBUG
117    unsigned int          magic;        /* sentinel for asserting validity   */
118#endif /* !NDEBUG */
119};
120
121typedef struct listNode * ListNode;
122
123
124/****************
125 *  Prototypes  *
126 ****************/
127
128static void * list_node_create (List l, ListNode *pp, void *x);
129static void * list_node_destroy (List l, ListNode *pp);
130static List list_alloc (void);
131static void list_free (List l);
132static ListNode list_node_alloc (void);
133static void list_node_free (ListNode p);
134static ListIterator list_iterator_alloc (void);
135static void list_iterator_free (ListIterator i);
136static void * list_alloc_aux (int size, void *pfreelist);
137static void list_free_aux (void *x, void *pfreelist);
138
139
140/***************
141 *  Variables  *
142 ***************/
143
144static List list_free_lists = NULL;
145static ListNode list_free_nodes = NULL;
146static ListIterator list_free_iterators = NULL;
147
148#ifdef WITH_PTHREADS
149static pthread_mutex_t list_free_lock = PTHREAD_MUTEX_INITIALIZER;
150#endif /* WITH_PTHREADS */
151
152
153/************
154 *  Macros  *
155 ************/
156
157#ifdef WITH_PTHREADS
158
159#  define list_mutex_init(mutex)                                              \
160     do {                                                                     \
161         int e = pthread_mutex_init(mutex, NULL);                             \
162         if (e != 0) {                                                        \
163             errno = e;                                                       \
164             lsd_fatal_error(__FILE__, __LINE__, "list mutex init");          \
165             abort();                                                         \
166         }                                                                    \
167     } while (0)
168
169#  define list_mutex_lock(mutex)                                              \
170     do {                                                                     \
171         int e = pthread_mutex_lock(mutex);                                   \
172         if (e != 0) {                                                        \
173             errno = e;                                                       \
174             lsd_fatal_error(__FILE__, __LINE__, "list mutex lock");          \
175             abort();                                                         \
176         }                                                                    \
177     } while (0)
178
179#  define list_mutex_unlock(mutex)                                            \
180     do {                                                                     \
181         int e = pthread_mutex_unlock(mutex);                                 \
182         if (e != 0) {                                                        \
183             errno = e;                                                       \
184             lsd_fatal_error(__FILE__, __LINE__, "list mutex unlock");        \
185             abort();                                                         \
186         }                                                                    \
187     } while (0)
188
189#  define list_mutex_destroy(mutex)                                           \
190     do {                                                                     \
191         int e = pthread_mutex_destroy(mutex);                                \
192         if (e != 0) {                                                        \
193             errno = e;                                                       \
194             lsd_fatal_error(__FILE__, __LINE__, "list mutex destroy");       \
195             abort();                                                         \
196         }                                                                    \
197     } while (0)
198
199#  ifndef NDEBUG
200     static int list_mutex_is_locked (pthread_mutex_t *mutex);
201#  endif /* !NDEBUG */
202
203#else /* !WITH_PTHREADS */
204
205#  define list_mutex_init(mutex)
206#  define list_mutex_lock(mutex)
207#  define list_mutex_unlock(mutex)
208#  define list_mutex_destroy(mutex)
209#  define list_mutex_is_locked(mutex) (1)
210
211#endif /* !WITH_PTHREADS */
212
213
214/***************
215 *  Functions  *
216 ***************/
217
218List
219list_create (ListDelF f)
220{
221    List l;
222
223    if (!(l = list_alloc()))
224        return(lsd_nomem_error(__FILE__, __LINE__, "list create"));
225    l->head = NULL;
226    l->tail = &l->head;
227    l->iNext = NULL;
228    l->fDel = f;
229    l->count = 0;
230    list_mutex_init(&l->mutex);
231    assert(l->magic = LIST_MAGIC);      /* set magic via assert abuse */
232    return(l);
233}
234
235
236void
237list_destroy (List l)
238{
239    ListIterator i, iTmp;
240    ListNode p, pTmp;
241
242    assert(l != NULL);
243    list_mutex_lock(&l->mutex);
244    assert(l->magic == LIST_MAGIC);
245    i = l->iNext;
246    while (i) {
247        assert(i->magic == LIST_MAGIC);
248        iTmp = i->iNext;
249        assert(i->magic = ~LIST_MAGIC); /* clear magic via assert abuse */
250        list_iterator_free(i);
251        i = iTmp;
252    }
253    p = l->head;
254    while (p) {
255        pTmp = p->next;
256        if (p->data && l->fDel)
257            l->fDel(p->data);
258        list_node_free(p);
259        p = pTmp;
260    }
261    assert(l->magic = ~LIST_MAGIC);     /* clear magic via assert abuse */
262    list_mutex_unlock(&l->mutex);
263    list_mutex_destroy(&l->mutex);
264    list_free(l);
265    return;
266}
267
268
269int
270list_is_empty (List l)
271{
272    int n;
273
274    assert(l != NULL);
275    list_mutex_lock(&l->mutex);
276    assert(l->magic == LIST_MAGIC);
277    n = l->count;
278    list_mutex_unlock(&l->mutex);
279    return(n == 0);
280}
281
282
283int
284list_count (List l)
285{
286    int n;
287
288    assert(l != NULL);
289    list_mutex_lock(&l->mutex);
290    assert(l->magic == LIST_MAGIC);
291    n = l->count;
292    list_mutex_unlock(&l->mutex);
293    return(n);
294}
295
296
297void *
298list_append (List l, void *x)
299{
300    void *v;
301
302    assert(l != NULL);
303    assert(x != NULL);
304    list_mutex_lock(&l->mutex);
305    assert(l->magic == LIST_MAGIC);
306    v = list_node_create(l, l->tail, x);
307    list_mutex_unlock(&l->mutex);
308    return(v);
309}
310
311
312void *
313list_prepend (List l, void *x)
314{
315    void *v;
316
317    assert(l != NULL);
318    assert(x != NULL);
319    list_mutex_lock(&l->mutex);
320    assert(l->magic == LIST_MAGIC);
321    v = list_node_create(l, &l->head, x);
322    list_mutex_unlock(&l->mutex);
323    return(v);
324}
325
326
327void *
328list_find_first (List l, ListFindF f, void *key)
329{
330    ListNode p;
331    void *v = NULL;
332
333    assert(l != NULL);
334    assert(f != NULL);
335    assert(key != NULL);
336    list_mutex_lock(&l->mutex);
337    assert(l->magic == LIST_MAGIC);
338    for (p=l->head; p; p=p->next) {
339        if (f(p->data, key)) {
340            v = p->data;
341            break;
342        }
343    }
344    list_mutex_unlock(&l->mutex);
345    return(v);
346}
347
348
349int
350list_delete_all (List l, ListFindF f, void *key)
351{
352    ListNode *pp;
353    void *v;
354    int n = 0;
355
356    assert(l != NULL);
357    assert(f != NULL);
358    assert(key != NULL);
359    list_mutex_lock(&l->mutex);
360    assert(l->magic == LIST_MAGIC);
361    pp = &l->head;
362    while (*pp) {
363        if (f((*pp)->data, key)) {
364            if ((v = list_node_destroy(l, pp))) {
365                if (l->fDel)
366                    l->fDel(v);
367                n++;
368            }
369        }
370        else {
371            pp = &(*pp)->next;
372        }
373    }
374    list_mutex_unlock(&l->mutex);
375    return(n);
376}
377
378
379int
380list_for_each (List l, ListForF f, void *arg)
381{
382    ListNode p;
383    int n = 0;
384
385    assert(l != NULL);
386    assert(f != NULL);
387    list_mutex_lock(&l->mutex);
388    assert(l->magic == LIST_MAGIC);
389    for (p=l->head; p; p=p->next) {
390        n++;
391        if (f(p->data, arg) < 0) {
392            n = -n;
393            break;
394        }
395    }
396    list_mutex_unlock(&l->mutex);
397    return(n);
398}
399
400
401void
402list_sort (List l, ListCmpF f)
403{
404/*  Note: Time complexity O(n^2).
405 */
406    ListNode *pp, *ppPrev, *ppPos, pTmp;
407    ListIterator i;
408
409    assert(l != NULL);
410    assert(f != NULL);
411    list_mutex_lock(&l->mutex);
412    assert(l->magic == LIST_MAGIC);
413    if (l->count > 1) {
414        ppPrev = &l->head;
415        pp = &(*ppPrev)->next;
416        while (*pp) {
417            if (f((*pp)->data, (*ppPrev)->data) < 0) {
418                ppPos = &l->head;
419                while (f((*pp)->data, (*ppPos)->data) >= 0)
420                    ppPos = &(*ppPos)->next;
421                pTmp = (*pp)->next;
422                (*pp)->next = *ppPos;
423                *ppPos = *pp;
424                *pp = pTmp;
425                if (ppPrev == ppPos)
426                    ppPrev = &(*ppPrev)->next;
427            }
428            else {
429                ppPrev = pp;
430                pp = &(*pp)->next;
431            }
432        }
433        l->tail = pp;
434
435        for (i=l->iNext; i; i=i->iNext) {
436            assert(i->magic == LIST_MAGIC);
437            i->pos = i->list->head;
438            i->prev = &i->list->head;
439        }
440    }
441    list_mutex_unlock(&l->mutex);
442    return;
443}
444
445
446void *
447list_push (List l, void *x)
448{
449    void *v;
450
451    assert(l != NULL);
452    assert(x != NULL);
453    list_mutex_lock(&l->mutex);
454    assert(l->magic == LIST_MAGIC);
455    v = list_node_create(l, &l->head, x);
456    list_mutex_unlock(&l->mutex);
457    return(v);
458}
459
460
461void *
462list_pop (List l)
463{
464    void *v;
465
466    assert(l != NULL);
467    list_mutex_lock(&l->mutex);
468    assert(l->magic == LIST_MAGIC);
469    v = list_node_destroy(l, &l->head);
470    list_mutex_unlock(&l->mutex);
471    return(v);
472}
473
474
475void *
476list_peek (List l)
477{
478    void *v;
479
480    assert(l != NULL);
481    list_mutex_lock(&l->mutex);
482    assert(l->magic == LIST_MAGIC);
483    v = (l->head) ? l->head->data : NULL;
484    list_mutex_unlock(&l->mutex);
485    return(v);
486}
487
488
489void *
490list_enqueue (List l, void *x)
491{
492    void *v;
493
494    assert(l != NULL);
495    assert(x != NULL);
496    list_mutex_lock(&l->mutex);
497    assert(l->magic == LIST_MAGIC);
498    v = list_node_create(l, l->tail, x);
499    list_mutex_unlock(&l->mutex);
500    return(v);
501}
502
503
504void *
505list_dequeue (List l)
506{
507    void *v;
508
509    assert(l != NULL);
510    list_mutex_lock(&l->mutex);
511    assert(l->magic == LIST_MAGIC);
512    v = list_node_destroy(l, &l->head);
513    list_mutex_unlock(&l->mutex);
514    return(v);
515}
516
517
518ListIterator
519list_iterator_create (List l)
520{
521    ListIterator i;
522
523    assert(l != NULL);
524    if (!(i = list_iterator_alloc()))
525        return(lsd_nomem_error(__FILE__, __LINE__, "list iterator create"));
526    i->list = l;
527    list_mutex_lock(&l->mutex);
528    assert(l->magic == LIST_MAGIC);
529    i->pos = l->head;
530    i->prev = &l->head;
531    i->iNext = l->iNext;
532    l->iNext = i;
533    assert(i->magic = LIST_MAGIC);      /* set magic via assert abuse */
534    list_mutex_unlock(&l->mutex);
535    return(i);
536}
537
538
539void
540list_iterator_reset (ListIterator i)
541{
542    assert(i != NULL);
543    assert(i->magic == LIST_MAGIC);
544    list_mutex_lock(&i->list->mutex);
545    assert(i->list->magic == LIST_MAGIC);
546    i->pos = i->list->head;
547    i->prev = &i->list->head;
548    list_mutex_unlock(&i->list->mutex);
549    return;
550}
551
552
553void
554list_iterator_destroy (ListIterator i)
555{
556    ListIterator *pi;
557
558    assert(i != NULL);
559    assert(i->magic == LIST_MAGIC);
560    list_mutex_lock(&i->list->mutex);
561    assert(i->list->magic == LIST_MAGIC);
562    for (pi=&i->list->iNext; *pi; pi=&(*pi)->iNext) {
563        assert((*pi)->magic == LIST_MAGIC);
564        if (*pi == i) {
565            *pi = (*pi)->iNext;
566            break;
567        }
568    }
569    list_mutex_unlock(&i->list->mutex);
570    assert(i->magic = ~LIST_MAGIC);     /* clear magic via assert abuse */
571    list_iterator_free(i);
572    return;
573}
574
575
576void *
577list_next (ListIterator i)
578{
579    ListNode p;
580
581    assert(i != NULL);
582    assert(i->magic == LIST_MAGIC);
583    list_mutex_lock(&i->list->mutex);
584    assert(i->list->magic == LIST_MAGIC);
585    if ((p = i->pos))
586        i->pos = p->next;
587    if (*i->prev != p)
588        i->prev = &(*i->prev)->next;
589    list_mutex_unlock(&i->list->mutex);
590    return(p ? p->data : NULL);
591}
592
593
594void *
595list_insert (ListIterator i, void *x)
596{
597    void *v;
598
599    assert(i != NULL);
600    assert(x != NULL);
601    assert(i->magic == LIST_MAGIC);
602    list_mutex_lock(&i->list->mutex);
603    assert(i->list->magic == LIST_MAGIC);
604    v = list_node_create(i->list, i->prev, x);
605    list_mutex_unlock(&i->list->mutex);
606    return(v);
607}
608
609
610void *
611list_find (ListIterator i, ListFindF f, void *key)
612{
613    void *v;
614
615    assert(i != NULL);
616    assert(f != NULL);
617    assert(key != NULL);
618    assert(i->magic == LIST_MAGIC);
619    while ((v=list_next(i)) && !f(v,key)) {;}
620    return(v);
621}
622
623
624void *
625list_remove (ListIterator i)
626{
627    void *v = NULL;
628
629    assert(i != NULL);
630    assert(i->magic == LIST_MAGIC);
631    list_mutex_lock(&i->list->mutex);
632    assert(i->list->magic == LIST_MAGIC);
633    if (*i->prev != i->pos)
634        v = list_node_destroy(i->list, i->prev);
635    list_mutex_unlock(&i->list->mutex);
636    return(v);
637}
638
639
640int
641list_delete (ListIterator i)
642{
643    void *v;
644
645    assert(i != NULL);
646    assert(i->magic == LIST_MAGIC);
647    if ((v = list_remove(i))) {
648        if (i->list->fDel)
649            i->list->fDel(v);
650        return(1);
651    }
652    return(0);
653}
654
655
656static void *
657list_node_create (List l, ListNode *pp, void *x)
658{
659/*  Inserts data pointed to by [x] into list [l] after [pp],
660 *    the address of the previous node's "next" ptr.
661 *  Returns a ptr to data [x], or NULL if insertion fails.
662 *  This routine assumes the list is already locked upon entry.
663 */
664    ListNode p;
665    ListIterator i;
666
667    assert(l != NULL);
668    assert(l->magic == LIST_MAGIC);
669    assert(list_mutex_is_locked(&l->mutex));
670    assert(pp != NULL);
671    assert(x != NULL);
672    if (!(p = list_node_alloc()))
673        return(lsd_nomem_error(__FILE__, __LINE__, "list node create"));
674    p->data = x;
675    if (!(p->next = *pp))
676        l->tail = &p->next;
677    *pp = p;
678    l->count++;
679    for (i=l->iNext; i; i=i->iNext) {
680        assert(i->magic == LIST_MAGIC);
681        if (i->prev == pp)
682            i->prev = &p->next;
683        else if (i->pos == p->next)
684            i->pos = p;
685        assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next));
686    }
687    return(x);
688}
689
690
691static void *
692list_node_destroy (List l, ListNode *pp)
693{
694/*  Removes the node pointed to by [*pp] from from list [l],
695 *    where [pp] is the address of the previous node's "next" ptr.
696 *  Returns the data ptr associated with list item being removed,
697 *    or NULL if [*pp] points to the NULL element.
698 *  This routine assumes the list is already locked upon entry.
699 */
700    void *v;
701    ListNode p;
702    ListIterator i;
703
704    assert(l != NULL);
705    assert(l->magic == LIST_MAGIC);
706    assert(list_mutex_is_locked(&l->mutex));
707    assert(pp != NULL);
708    if (!(p = *pp))
709        return(NULL);
710    v = p->data;
711    if (!(*pp = p->next))
712        l->tail = pp;
713    l->count--;
714    for (i=l->iNext; i; i=i->iNext) {
715        assert(i->magic == LIST_MAGIC);
716        if (i->pos == p)
717            i->pos = p->next, i->prev = pp;
718        else if (i->prev == &p->next)
719            i->prev = pp;
720        assert((i->pos == *i->prev) || (i->pos == (*i->prev)->next));
721    }
722    list_node_free(p);
723    return(v);
724}
725
726
727static List
728list_alloc (void)
729{
730    return(list_alloc_aux(sizeof(struct list), &list_free_lists));
731}
732
733
734static void
735list_free (List l)
736{
737    list_free_aux(l, &list_free_lists);
738    return;
739}
740
741
742static ListNode
743list_node_alloc (void)
744{
745    return(list_alloc_aux(sizeof(struct listNode), &list_free_nodes));
746}
747
748
749static void
750list_node_free (ListNode p)
751{
752    list_free_aux(p, &list_free_nodes);
753    return;
754}
755
756
757static ListIterator
758list_iterator_alloc (void)
759{
760    return(list_alloc_aux(sizeof(struct listIterator), &list_free_iterators));
761}
762
763
764static void
765list_iterator_free (ListIterator i)
766{
767    list_free_aux(i, &list_free_iterators);
768    return;
769}
770
771
772static void *
773list_alloc_aux (int size, void *pfreelist)
774{
775/*  Allocates an object of [size] bytes from the freelist [*pfreelist].
776 *  Memory is added to the freelist in chunks of size LIST_ALLOC.
777 *  Returns a ptr to the object, or NULL if the memory request fails.
778 */
779    void **px;
780    void **pfree = pfreelist;
781    void **plast;
782
783    assert(sizeof(char) == 1);
784    assert(size >= sizeof(void *));
785    assert(pfreelist != NULL);
786    assert(LIST_ALLOC > 0);
787    list_mutex_lock(&list_free_lock);
788    if (!*pfree) {
789        if ((*pfree = malloc(LIST_ALLOC * size))) {
790            px = *pfree;
791            plast = (void **) ((char *) *pfree + ((LIST_ALLOC - 1) * size));
792            while (px < plast)
793                *px = (char *) px + size, px = *px;
794            *plast = NULL;
795        }
796    }
797    if ((px = *pfree))
798        *pfree = *px;
799    else
800        errno = ENOMEM;
801    list_mutex_unlock(&list_free_lock);
802    return(px);
803}
804
805
806static void
807list_free_aux (void *x, void *pfreelist)
808{
809/*  Frees the object [x], returning it to the freelist [*pfreelist].
810 */
811    void **px = x;
812    void **pfree = pfreelist;
813
814    assert(x != NULL);
815    assert(pfreelist != NULL);
816    list_mutex_lock(&list_free_lock);
817    *px = *pfree;
818    *pfree = px;
819    list_mutex_unlock(&list_free_lock);
820    return;
821}
822
823
824#ifndef NDEBUG
825#ifdef WITH_PTHREADS
826static int
827list_mutex_is_locked (pthread_mutex_t *mutex)
828{
829/*  Returns true if the mutex is locked; o/w, returns false.
830 */
831    int rc;
832
833    assert(mutex != NULL);
834    rc = pthread_mutex_trylock(mutex);
835    return(rc == EBUSY ? 1 : 0);
836}
837#endif /* WITH_PTHREADS */
838#endif /* !NDEBUG */