Coverage Report

Created: 2025-09-04 06:20

/src/hdf5/src/H5FL.c
Line
Count
Source (jump to first uncovered line)
1
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2
 * Copyright by The HDF Group.                                               *
3
 * All rights reserved.                                                      *
4
 *                                                                           *
5
 * This file is part of HDF5.  The full HDF5 copyright notice, including     *
6
 * terms governing use, modification, and redistribution, is contained in    *
7
 * the LICENSE file, which can be found at the root of the source code       *
8
 * distribution tree, or in https://www.hdfgroup.org/licenses.               *
9
 * If you do not have access to either file, you may request a copy from     *
10
 * help@hdfgroup.org.                                                        *
11
 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
12
13
/*
14
 * Purpose: Manage priority queues of free-lists (of blocks of bytes).
15
 *      These are used in various places in the library which allocate and
16
 *      free differently blocks of bytes repeatedly.  Usually the same size
17
 *      of block is allocated and freed repeatedly in a loop, while writing out
18
 *      chunked data for example, but the blocks may also be of different sizes
19
 *      from different datasets and an attempt is made to optimize access to
20
 *      the proper free list of blocks by using these priority queues to
21
 *      move frequently accessed free lists to the head of the queue.
22
 */
23
24
#include "H5FLmodule.h" /* This source code file is part of the H5FL module */
25
26
/* #define H5FL_DEBUG */
27
28
#include "H5private.h"   /* Generic Functions     */
29
#include "H5Eprivate.h"  /* Error handling        */
30
#include "H5FLprivate.h" /* Free Lists                           */
31
#include "H5MMprivate.h" /* Memory management     */
32
33
/*
34
 * Private type definitions
35
 */
36
37
/*
38
    Default limits on how much memory can accumulate on each free list before
39
    it is garbage collected.
40
 */
41
static size_t H5FL_reg_glb_mem_lim = 1 * 1024 * 1024;  /* Default to 1MB limit on all regular free lists */
42
static size_t H5FL_reg_lst_mem_lim = 1 * 65536;        /* Default to 64KB limit on each regular free list */
43
static size_t H5FL_arr_glb_mem_lim = 4 * 1024 * 1024;  /* Default to 4MB limit on all array free lists */
44
static size_t H5FL_arr_lst_mem_lim = 4 * 65536;        /* Default to 256KB limit on each array free list */
45
static size_t H5FL_blk_glb_mem_lim = 16 * 1024 * 1024; /* Default to 16MB limit on all block free lists */
46
static size_t H5FL_blk_lst_mem_lim = 1024 * 1024; /* Default to 1024KB (1MB) limit on each block free list */
47
static size_t H5FL_fac_glb_mem_lim = 16 * 1024 * 1024; /* Default to 16MB limit on all factory free lists */
48
static size_t H5FL_fac_lst_mem_lim =
49
    1024 * 1024; /* Default to 1024KB (1MB) limit on each factory free list */
50
51
/* A garbage collection node for regular free lists */
52
typedef struct H5FL_reg_gc_node_t {
53
    H5FL_reg_head_t           *list; /* Pointer to the head of the list to garbage collect */
54
    struct H5FL_reg_gc_node_t *next; /* Pointer to the next node in the list of things to garbage collect */
55
} H5FL_reg_gc_node_t;
56
57
/* The garbage collection head for regular free lists */
58
typedef struct H5FL_reg_gc_list_t {
59
    size_t                     mem_freed; /* Amount of free memory on list */
60
    struct H5FL_reg_gc_node_t *first; /* Pointer to the first node in the list of things to garbage collect */
61
} H5FL_reg_gc_list_t;
62
63
/* The head of the list of things to garbage collect */
64
static H5FL_reg_gc_list_t H5FL_reg_gc_head = {0, NULL};
65
66
/* A garbage collection node for array free lists */
67
typedef struct H5FL_gc_arr_node_t {
68
    H5FL_arr_head_t           *list; /* Pointer to the head of the list to garbage collect */
69
    struct H5FL_gc_arr_node_t *next; /* Pointer to the next node in the list of things to garbage collect */
70
} H5FL_gc_arr_node_t;
71
72
/* The garbage collection head for array free lists */
73
typedef struct H5FL_gc_arr_list_t {
74
    size_t                     mem_freed; /* Amount of free memory on list */
75
    struct H5FL_gc_arr_node_t *first; /* Pointer to the first node in the list of things to garbage collect */
76
} H5FL_gc_arr_list_t;
77
78
/* The head of the list of array things to garbage collect */
79
static H5FL_gc_arr_list_t H5FL_arr_gc_head = {0, NULL};
80
81
/* A garbage collection node for blocks */
82
typedef struct H5FL_blk_gc_node_t {
83
    H5FL_blk_head_t           *pq;   /* Pointer to the head of the PQ to garbage collect */
84
    struct H5FL_blk_gc_node_t *next; /* Pointer to the next node in the list of things to garbage collect */
85
} H5FL_blk_gc_node_t;
86
87
/* The garbage collection head for blocks */
88
typedef struct H5FL_blk_gc_list_t {
89
    size_t                     mem_freed; /* Amount of free memory on list */
90
    struct H5FL_blk_gc_node_t *first; /* Pointer to the first node in the list of things to garbage collect */
91
} H5FL_blk_gc_list_t;
92
93
/* The head of the list of PQs to garbage collect */
94
static H5FL_blk_gc_list_t H5FL_blk_gc_head = {0, NULL};
95
96
/* A garbage collection node for factory free lists */
97
struct H5FL_fac_gc_node_t {
98
    H5FL_fac_head_t           *list; /* Pointer to the head of the list to garbage collect */
99
    struct H5FL_fac_gc_node_t *next; /* Pointer to the next node in the list of things to garbage collect */
100
};
101
102
/* The garbage collection head for factory free lists */
103
typedef struct H5FL_fac_gc_list_t {
104
    size_t                     mem_freed; /* Amount of free memory on list */
105
    struct H5FL_fac_gc_node_t *first; /* Pointer to the first node in the list of things to garbage collect */
106
} H5FL_fac_gc_list_t;
107
108
/* Data structure to store each block in factory free list */
109
struct H5FL_fac_node_t {
110
    struct H5FL_fac_node_t *next; /* Pointer to next block in free list */
111
};
112
113
/* Package initialization variable */
114
bool H5_PKG_INIT_VAR = false;
115
116
/* The head of the list of factory things to garbage collect */
117
static H5FL_fac_gc_list_t H5FL_fac_gc_head = {0, NULL};
118
119
/* Forward declarations of local static functions */
120
static void            *H5FL__malloc(size_t mem_size);
121
static herr_t           H5FL__reg_init(H5FL_reg_head_t *head);
122
static herr_t           H5FL__reg_gc(void);
123
static herr_t           H5FL__reg_gc_list(H5FL_reg_head_t *head);
124
static int              H5FL__reg_term(void);
125
static H5FL_blk_node_t *H5FL__blk_find_list(H5FL_blk_node_t **head, size_t size);
126
static H5FL_blk_node_t *H5FL__blk_create_list(H5FL_blk_node_t **head, size_t size);
127
static herr_t           H5FL__blk_init(H5FL_blk_head_t *head);
128
static herr_t           H5FL__blk_gc_list(H5FL_blk_head_t *head);
129
static herr_t           H5FL__blk_gc(void);
130
static int              H5FL__blk_term(void);
131
static herr_t           H5FL__arr_init(H5FL_arr_head_t *head);
132
static herr_t           H5FL__arr_gc_list(H5FL_arr_head_t *head);
133
static herr_t           H5FL__arr_gc(void);
134
static int              H5FL__arr_term(void);
135
static herr_t           H5FL__fac_gc_list(H5FL_fac_head_t *head);
136
static herr_t           H5FL__fac_gc(void);
137
static int              H5FL__fac_term_all(void);
138
139
/* Declare a free list to manage the H5FL_blk_node_t struct */
140
H5FL_DEFINE(H5FL_blk_node_t);
141
142
/* Declare a free list to manage the H5FL_fac_gc_node_t struct */
143
H5FL_DEFINE_STATIC(H5FL_fac_gc_node_t);
144
145
/* Declare a free list to manage the H5FL_fac_head_t struct */
146
H5FL_DEFINE(H5FL_fac_head_t);
147
148
/*--------------------------------------------------------------------------
149
 NAME
150
    H5FL_term_package
151
 PURPOSE
152
    Terminate various H5FL objects
153
 USAGE
154
    void H5FL_term_package()
155
 RETURNS
156
    Success:  Positive if any action might have caused a change in some
157
                other interface; zero otherwise.
158
        Failure:  Negative
159
 DESCRIPTION
160
    Release any resources allocated.
161
 GLOBAL VARIABLES
162
 COMMENTS, BUGS, ASSUMPTIONS
163
     Can't report errors...
164
 EXAMPLES
165
 REVISION LOG
166
--------------------------------------------------------------------------*/
167
int
168
H5FL_term_package(void)
169
2
{
170
2
    int n = 0;
171
172
2
    FUNC_ENTER_NOAPI_NOINIT_NOERR
173
174
2
    if (H5_PKG_INIT_VAR) {
175
        /* Garbage collect any nodes on the free lists */
176
2
        (void)H5FL_garbage_coll();
177
178
        /* Shut down the various kinds of free lists */
179
2
        n += H5FL__reg_term();
180
2
        n += H5FL__fac_term_all();
181
2
        n += H5FL__arr_term();
182
2
        n += H5FL__blk_term();
183
184
        /* Mark interface closed */
185
2
        if (0 == n)
186
2
            H5_PKG_INIT_VAR = false;
187
2
    } /* end if */
188
189
2
    FUNC_LEAVE_NOAPI(n)
190
2
} /* end H5FL_term_package() */
191
192
/*-------------------------------------------------------------------------
193
 * Function:  H5FL__malloc
194
 *
195
 * Purpose: Attempt to allocate space using malloc.  If malloc fails, garbage
196
 *      collect and try again.  If malloc fails again, then return NULL.
197
 *
198
 * Return:  Success:  non-NULL
199
 *    Failure:  NULL
200
 *
201
 *-------------------------------------------------------------------------
202
 */
203
static void *
204
H5FL__malloc(size_t mem_size)
205
4.68k
{
206
4.68k
    void *ret_value = NULL; /* Return value */
207
208
4.68k
    FUNC_ENTER_PACKAGE
209
210
    /* Attempt to allocate the memory requested */
211
4.68k
    if (NULL == (ret_value = H5MM_malloc(mem_size))) {
212
        /* If we can't allocate the memory now, try garbage collecting first */
213
0
        if (H5FL_garbage_coll() < 0)
214
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during allocation");
215
216
        /* Now try allocating the memory again */
217
0
        if (NULL == (ret_value = H5MM_malloc(mem_size)))
218
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for chunk");
219
0
    } /* end if */
220
221
4.68k
done:
222
4.68k
    FUNC_LEAVE_NOAPI(ret_value)
223
4.68k
} /* end H5FL__malloc() */
224
225
/*-------------------------------------------------------------------------
226
 * Function:  H5FL__reg_init
227
 *
228
 * Purpose: Initialize a free list for a certain type.  Right now, this just
229
 *      adds the free list to the list of things to garbage collect.
230
 *
231
 * Return:  Success:  Non-negative
232
 *    Failure:  Negative
233
 *
234
 *-------------------------------------------------------------------------
235
 */
236
static herr_t
237
H5FL__reg_init(H5FL_reg_head_t *head)
238
48
{
239
48
    H5FL_reg_gc_node_t *new_node;            /* Pointer to the node for the new list to garbage collect */
240
48
    herr_t              ret_value = SUCCEED; /* return value*/
241
242
48
    FUNC_ENTER_PACKAGE
243
244
    /* Allocate a new garbage collection node */
245
48
    if (NULL == (new_node = (H5FL_reg_gc_node_t *)H5MM_malloc(sizeof(H5FL_reg_gc_node_t))))
246
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
247
248
    /* Initialize the new garbage collection node */
249
48
    new_node->list = head;
250
251
    /* Link in to the garbage collection list */
252
48
    new_node->next         = H5FL_reg_gc_head.first;
253
48
    H5FL_reg_gc_head.first = new_node;
254
255
    /* Indicate that the free list is initialized */
256
48
    head->init = true;
257
258
    /* Make certain that the space allocated is large enough to store a free list pointer (eventually) */
259
48
    if (head->size < sizeof(H5FL_reg_node_t))
260
0
        head->size = sizeof(H5FL_reg_node_t);
261
262
48
done:
263
48
    FUNC_LEAVE_NOAPI(ret_value)
264
48
} /* end H5FL__reg_init() */
265
266
/*-------------------------------------------------------------------------
267
 * Function:  H5FL_reg_free
268
 *
269
 * Purpose: Release an object & put on free list
270
 *
271
 * Return:  Always returns NULL
272
 *
273
 *-------------------------------------------------------------------------
274
 */
275
void *
276
H5FL_reg_free(H5FL_reg_head_t *head, void *obj)
277
6.48k
{
278
6.48k
    void *ret_value = NULL; /* Return value */
279
280
    /* NOINIT OK here because this must be called after H5FL_reg_malloc/calloc
281
     * -NAF */
282
6.48k
    FUNC_ENTER_NOAPI_NOINIT
283
284
    /* Double check parameters */
285
6.48k
    assert(head);
286
6.48k
    assert(obj);
287
288
#ifdef H5FL_DEBUG
289
    memset(obj, 255, head->size);
290
#endif /* H5FL_DEBUG */
291
292
    /* Make certain that the free list is initialized */
293
6.48k
    assert(head->init);
294
295
    /* Link into the free list */
296
6.48k
    ((H5FL_reg_node_t *)obj)->next = head->list;
297
298
    /* Point free list at the node freed */
299
6.48k
    head->list = (H5FL_reg_node_t *)obj;
300
301
    /* Increment the number of blocks on free list */
302
6.48k
    head->onlist++;
303
304
    /* Increment the amount of "regular" freed memory globally */
305
6.48k
    H5FL_reg_gc_head.mem_freed += head->size;
306
307
    /* Check for exceeding free list memory use limits */
308
    /* First check this particular list */
309
6.48k
    if (head->onlist * head->size > H5FL_reg_lst_mem_lim)
310
27
        if (H5FL__reg_gc_list(head) < 0)
311
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
312
313
    /* Then check the global amount memory on regular free lists */
314
6.48k
    if (H5FL_reg_gc_head.mem_freed > H5FL_reg_glb_mem_lim)
315
0
        if (H5FL__reg_gc() < 0)
316
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
317
318
6.48k
done:
319
6.48k
    FUNC_LEAVE_NOAPI(ret_value)
320
6.48k
} /* end H5FL_reg_free() */
321
322
/*-------------------------------------------------------------------------
323
 * Function:  H5FL_reg_malloc
324
 *
325
 * Purpose: Allocate a block on a free list
326
 *
327
 * Return:  Success:  Pointer to a valid object
328
 *    Failure:  NULL
329
 *
330
 *-------------------------------------------------------------------------
331
 */
332
void *
333
H5FL_reg_malloc(H5FL_reg_head_t *head)
334
6.48k
{
335
6.48k
    void *ret_value = NULL; /* Pointer to object to return */
336
337
6.48k
    FUNC_ENTER_NOAPI(NULL)
338
339
    /* Double check parameters */
340
6.48k
    assert(head);
341
342
    /* Make certain the list is initialized first */
343
6.48k
    if (!head->init)
344
48
        if (H5FL__reg_init(head) < 0)
345
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, NULL, "can't initialize 'regular' blocks");
346
347
    /* Check for nodes available on the free list first */
348
6.48k
    if (head->list != NULL) {
349
        /* Get a pointer to the block on the free list */
350
2.24k
        ret_value = (void *)(head->list);
351
352
        /* Remove node from free list */
353
2.24k
        head->list = head->list->next;
354
355
        /* Decrement the number of blocks & memory on free list */
356
2.24k
        head->onlist--;
357
358
        /* Decrement the amount of global "regular" free list memory in use */
359
2.24k
        H5FL_reg_gc_head.mem_freed -= (head->size);
360
2.24k
    } /* end if */
361
    /* Otherwise allocate a node */
362
4.23k
    else {
363
4.23k
        if (NULL == (ret_value = H5FL__malloc(head->size)))
364
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
365
366
        /* Increment the number of blocks allocated in list */
367
4.23k
        head->allocated++;
368
4.23k
    } /* end else */
369
370
6.48k
done:
371
6.48k
    FUNC_LEAVE_NOAPI(ret_value)
372
6.48k
} /* end H5FL_reg_malloc() */
373
374
/*-------------------------------------------------------------------------
375
 * Function:  H5FL_reg_calloc
376
 *
377
 * Purpose: Allocate a block on a free list and clear it to zeros
378
 *
379
 * Return:  Success:  Pointer to a valid object
380
 *    Failure:  NULL
381
 *
382
 *-------------------------------------------------------------------------
383
 */
384
void *
385
H5FL_reg_calloc(H5FL_reg_head_t *head)
386
1.56k
{
387
1.56k
    void *ret_value = NULL; /* Pointer to object to return */
388
389
1.56k
    FUNC_ENTER_NOAPI(NULL)
390
391
    /* Double check parameters */
392
1.56k
    assert(head);
393
394
    /* Allocate the block */
395
1.56k
    if (NULL == (ret_value = H5FL_reg_malloc(head)))
396
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
397
398
    /* Clear to zeros */
399
1.56k
    memset(ret_value, 0, head->size);
400
401
1.56k
done:
402
1.56k
    FUNC_LEAVE_NOAPI(ret_value)
403
1.56k
} /* end H5FL_reg_calloc() */
404
405
/*-------------------------------------------------------------------------
406
 * Function:  H5FL__reg_gc_list
407
 *
408
 * Purpose: Garbage collect on a particular object free list
409
 *
410
 * Return:  Success:  Non-negative
411
 *    Failure:  Negative
412
 *
413
 *-------------------------------------------------------------------------
414
 */
415
static herr_t
416
H5FL__reg_gc_list(H5FL_reg_head_t *head)
417
75
{
418
75
    H5FL_reg_node_t *free_list; /* Pointer to nodes in free list being garbage collected */
419
420
75
    FUNC_ENTER_PACKAGE_NOERR
421
422
    /* For each free list being garbage collected, walk through the nodes and free them */
423
75
    free_list = head->list;
424
4.31k
    while (free_list != NULL) {
425
4.23k
        H5FL_reg_node_t *tmp; /* Temporary node pointer */
426
427
        /* Get the pointer to the next node */
428
4.23k
        tmp = free_list->next;
429
430
        /* Free the block */
431
4.23k
        H5MM_free(free_list);
432
433
        /* Advance to the next node */
434
4.23k
        free_list = tmp;
435
4.23k
    } /* end while */
436
437
    /* Decrement the count of nodes allocated and free the node */
438
75
    head->allocated -= head->onlist;
439
440
    /* Decrement global count of free memory on "regular" lists */
441
75
    H5FL_reg_gc_head.mem_freed -= (head->onlist * head->size);
442
443
    /* Indicate no free nodes on the free list */
444
75
    head->list   = NULL;
445
75
    head->onlist = 0;
446
447
75
    FUNC_LEAVE_NOAPI(SUCCEED)
448
75
} /* end H5FL__reg_gc_list() */
449
450
/*-------------------------------------------------------------------------
451
 * Function:  H5FL__reg_gc
452
 *
453
 * Purpose: Garbage collect on all the object free lists
454
 *
455
 * Return:  Success:  Non-negative
456
 *    Failure:  Negative
457
 *
458
 *-------------------------------------------------------------------------
459
 */
460
static herr_t
461
H5FL__reg_gc(void)
462
2
{
463
2
    H5FL_reg_gc_node_t *gc_node;             /* Pointer into the list of things to garbage collect */
464
2
    herr_t              ret_value = SUCCEED; /* return value*/
465
466
2
    FUNC_ENTER_PACKAGE
467
468
    /* Walk through all the free lists, free()'ing the nodes */
469
2
    gc_node = H5FL_reg_gc_head.first;
470
50
    while (gc_node != NULL) {
471
        /* Release the free nodes on the list */
472
48
        if (H5FL__reg_gc_list(gc_node->list) < 0)
473
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "garbage collection of list failed");
474
475
        /* Go on to the next free list to garbage collect */
476
48
        gc_node = gc_node->next;
477
48
    } /* end while */
478
479
    /* Double check that all the memory on the free lists is recycled */
480
2
    assert(H5FL_reg_gc_head.mem_freed == 0);
481
482
2
done:
483
2
    FUNC_LEAVE_NOAPI(ret_value)
484
2
} /* end H5FL__reg_gc() */
485
486
/*--------------------------------------------------------------------------
487
 NAME
488
    H5FL_reg_term
489
 PURPOSE
490
    Terminate various H5FL object free lists
491
 USAGE
492
    int H5FL_reg_term()
493
 RETURNS
494
    Success:  Positive if any action might have caused a change in some
495
                other interface; zero otherwise.
496
        Failure:  Negative
497
 DESCRIPTION
498
    Release any resources allocated.
499
 GLOBAL VARIABLES
500
 COMMENTS, BUGS, ASSUMPTIONS
501
     Can't report errors...
502
 EXAMPLES
503
 REVISION LOG
504
        If a list cannot be freed because something is using it then return
505
        zero (failure to free a list doesn't affect any other part of the
506
        library). If some other layer frees something during its termination
507
        it will return non-zero, which will cause this function to get called
508
        again to reclaim this layer's memory.
509
--------------------------------------------------------------------------*/
510
static int
511
H5FL__reg_term(void)
512
2
{
513
2
    H5FL_reg_gc_node_t *left; /* pointer to garbage collection lists with work left */
514
515
2
    FUNC_ENTER_PACKAGE_NOERR
516
517
    /* Free the nodes on the garbage collection list, keeping nodes with allocations outstanding */
518
2
    left = NULL;
519
50
    while (H5FL_reg_gc_head.first != NULL) {
520
48
        H5FL_reg_gc_node_t *tmp; /* Temporary pointer to a garbage collection node */
521
522
        /* Get a copy of the next node */
523
48
        tmp = H5FL_reg_gc_head.first->next;
524
525
#ifdef H5FL_DEBUG
526
        printf("%s: head->name = %s, head->allocated = %d\n", __func__, H5FL_reg_gc_head.first->list->name,
527
               (int)H5FL_reg_gc_head.first->list->allocated);
528
#endif /* H5FL_DEBUG */
529
        /* Check if the list has allocations outstanding */
530
48
        if (H5FL_reg_gc_head.first->list->allocated > 0) {
531
            /* Add free list to the list of nodes with allocations open still */
532
0
            H5FL_reg_gc_head.first->next = left;
533
0
            left                         = H5FL_reg_gc_head.first;
534
0
        } /* end if */
535
        /* No allocations left open for list, get rid of it */
536
48
        else {
537
            /* Reset the "initialized" flag, in case we restart this list somehow (I don't know how..) */
538
48
            H5FL_reg_gc_head.first->list->init = false;
539
540
            /* Free the node from the garbage collection list */
541
48
            H5MM_xfree(H5FL_reg_gc_head.first);
542
48
        } /* end else */
543
544
48
        H5FL_reg_gc_head.first = tmp;
545
48
    } /* end while */
546
547
    /* Point to the list of nodes left with allocations open, if any */
548
2
    H5FL_reg_gc_head.first = left;
549
550
2
    FUNC_LEAVE_NOAPI(H5FL_reg_gc_head.first != NULL ? 1 : 0)
551
2
} /* end H5FL__reg_term() */
552
553
/*-------------------------------------------------------------------------
554
 * Function:  H5FL__blk_find_list
555
 *
556
 * Purpose: Finds the free list for blocks of a given size.  Also moves that
557
 *      free list node to the head of the priority queue (if it isn't there
558
 *      already).  This routine does not manage the actual free list, it just
559
 *      works with the priority queue.
560
 *
561
 * Return:  Success:  valid pointer to the free list node
562
 *
563
 *    Failure:  NULL
564
 *
565
 *-------------------------------------------------------------------------
566
 */
567
static H5FL_blk_node_t *
568
H5FL__blk_find_list(H5FL_blk_node_t **head, size_t size)
569
88
{
570
88
    H5FL_blk_node_t *temp = NULL; /* Temp. pointer to node in the native list */
571
572
88
    FUNC_ENTER_PACKAGE_NOERR
573
574
    /* Find the correct free list */
575
88
    temp = *head;
576
577
    /* Check if the node is at the head of the list */
578
88
    if (temp && temp->size != size) {
579
15
        temp = temp->next;
580
581
15
        while (temp != NULL) {
582
            /* Check if we found the correct node */
583
14
            if (temp->size == size) {
584
                /* Take the node found out of it's current position */
585
14
                if (temp->next == NULL) {
586
14
                    temp->prev->next = NULL;
587
14
                } /* end if */
588
0
                else {
589
0
                    temp->prev->next = temp->next;
590
0
                    temp->next->prev = temp->prev;
591
0
                } /* end else */
592
593
                /* Move the found node to the head of the list */
594
14
                temp->prev    = NULL;
595
14
                temp->next    = *head;
596
14
                (*head)->prev = temp;
597
14
                *head         = temp;
598
599
                /* Get out */
600
14
                break;
601
14
            } /* end if */
602
603
0
            temp = temp->next;
604
0
        } /* end while */
605
15
    }     /* end if */
606
607
88
    FUNC_LEAVE_NOAPI(temp)
608
88
} /* end H5FL__blk_find_list() */
609
610
/*-------------------------------------------------------------------------
611
 * Function:  H5FL__blk_create_list
612
 *
613
 * Purpose: Creates a new free list for blocks of the given size at the
614
 *      head of the priority queue.
615
 *
616
 * Return:  Success:  valid pointer to the free list node
617
 *
618
 *    Failure:  NULL
619
 *
620
 *-------------------------------------------------------------------------
621
 */
622
static H5FL_blk_node_t *
623
H5FL__blk_create_list(H5FL_blk_node_t **head, size_t size)
624
9
{
625
9
    H5FL_blk_node_t *ret_value = NULL; /* Return value */
626
627
9
    FUNC_ENTER_PACKAGE
628
629
    /* Allocate room for the new free list node */
630
9
    if (NULL == (ret_value = H5FL_CALLOC(H5FL_blk_node_t)))
631
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "memory allocation failed for chunk info");
632
633
    /* Set the correct values for the new free list */
634
9
    ret_value->size = size;
635
636
    /* Attach to head of priority queue */
637
9
    if (NULL == *head)
638
8
        *head = ret_value;
639
1
    else {
640
1
        ret_value->next = *head;
641
1
        (*head)->prev   = ret_value;
642
1
        *head           = ret_value;
643
1
    } /* end else */
644
645
9
done:
646
9
    FUNC_LEAVE_NOAPI(ret_value)
647
9
} /* end H5FL__blk_create_list() */
648
649
/*-------------------------------------------------------------------------
650
 * Function:  H5FL__blk_init
651
 *
652
 * Purpose: Initialize a priority queue of a certain type.  Right now, this just
653
 *      adds the PQ to the list of things to garbage collect.
654
 *
655
 * Return:  Success:  Non-negative
656
 *    Failure:  Negative
657
 *
658
 *-------------------------------------------------------------------------
659
 */
660
static herr_t
661
H5FL__blk_init(H5FL_blk_head_t *head)
662
8
{
663
8
    H5FL_blk_gc_node_t *new_node;            /* Pointer to the node for the new list to garbage collect */
664
8
    herr_t              ret_value = SUCCEED; /* return value*/
665
666
8
    FUNC_ENTER_PACKAGE
667
668
    /* Allocate a new garbage collection node */
669
8
    if (NULL == (new_node = (H5FL_blk_gc_node_t *)H5MM_malloc(sizeof(H5FL_blk_gc_node_t))))
670
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
671
672
    /* Initialize the new garbage collection node */
673
8
    new_node->pq = head;
674
675
    /* Link in to the garbage collection list */
676
8
    new_node->next         = H5FL_blk_gc_head.first;
677
8
    H5FL_blk_gc_head.first = new_node;
678
679
    /* Indicate that the PQ is initialized */
680
8
    head->init = true;
681
682
8
done:
683
8
    FUNC_LEAVE_NOAPI(ret_value)
684
8
} /* end H5FL__blk_init() */
685
686
/*-------------------------------------------------------------------------
687
 * Function:  H5FL_blk_free_block_avail
688
 *
689
 * Purpose: Checks if a free block of the appropriate size is available
690
 *      for a given list.
691
 *
692
 * Return:  Success:  non-negative
693
 *    Failure:  negative
694
 *
695
 *-------------------------------------------------------------------------
696
 */
697
htri_t
698
H5FL_blk_free_block_avail(H5FL_blk_head_t *head, size_t size)
699
0
{
700
0
    H5FL_blk_node_t *free_list;        /* The free list of nodes of correct size */
701
0
    htri_t           ret_value = FAIL; /* Return value */
702
703
0
    FUNC_ENTER_NOAPI_NOERR
704
705
    /* Double check parameters */
706
0
    assert(head);
707
708
    /* check if there is a free list for blocks of this size */
709
    /* and if there are any blocks available on the list */
710
0
    if ((free_list = H5FL__blk_find_list(&(head->head), size)) != NULL && free_list->list != NULL)
711
0
        ret_value = true;
712
0
    else
713
0
        ret_value = false;
714
715
0
    FUNC_LEAVE_NOAPI(ret_value)
716
0
} /* end H5FL_blk_free_block_avail() */
717
718
/*-------------------------------------------------------------------------
719
 * Function:  H5FL_blk_malloc
720
 *
721
 * Purpose: Allocates memory for a block.  This routine is used
722
 *      instead of malloc because the block can be kept on a free list so
723
 *      they don't thrash malloc/free as much.
724
 *
725
 * Return:  Success:  valid pointer to the block
726
 *
727
 *    Failure:  NULL
728
 *
729
 *-------------------------------------------------------------------------
730
 */
731
void *
732
H5FL_blk_malloc(H5FL_blk_head_t *head, size_t size)
733
44
{
734
44
    H5FL_blk_node_t *free_list;        /* The free list of nodes of correct size */
735
44
    H5FL_blk_list_t *temp;             /* Temp. ptr to the new native list allocated */
736
44
    void            *ret_value = NULL; /* Pointer to the block to return to the user */
737
738
44
    FUNC_ENTER_NOAPI(NULL)
739
740
    /* Double check parameters */
741
44
    assert(head);
742
44
    assert(size);
743
744
    /* Make certain the list is initialized first */
745
44
    if (!head->init)
746
8
        if (H5FL__blk_init(head) < 0)
747
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, NULL, "can't initialize 'block' list");
748
749
    /* check if there is a free list for blocks of this size */
750
    /* and if there are any blocks available on the list */
751
44
    if (NULL != (free_list = H5FL__blk_find_list(&(head->head), size)) && NULL != free_list->list) {
752
        /* Remove the first node from the free list */
753
35
        temp            = free_list->list;
754
35
        free_list->list = free_list->list->next;
755
756
        /* Decrement the number of blocks & memory used on free list */
757
35
        free_list->onlist--;
758
35
        head->onlist--;
759
35
        head->list_mem -= size;
760
761
        /* Decrement the amount of global "block" free list memory in use */
762
35
        H5FL_blk_gc_head.mem_freed -= size;
763
35
    } /* end if */
764
    /* No free list available, or there are no nodes on the list, allocate a new node to give to the user */
765
9
    else {
766
        /* Check if there was no free list for native blocks of this size */
767
9
        if (NULL == free_list)
768
            /* Create a new list node and insert it to the queue */
769
9
            free_list = H5FL__blk_create_list(&(head->head), size);
770
9
        assert(free_list);
771
772
        /* Allocate new node, with room for the page info header and the actual page data */
773
9
        if (NULL == (temp = (H5FL_blk_list_t *)H5FL__malloc(sizeof(H5FL_blk_list_t) + size)))
774
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for chunk");
775
776
        /* Increment the number of blocks of this size */
777
9
        free_list->allocated++;
778
779
        /* Increment the total number of blocks allocated */
780
9
        head->allocated++;
781
9
    } /* end else */
782
783
    /* Initialize the block allocated */
784
44
    temp->size = size;
785
786
    /* Set the return value to the block itself */
787
44
    ret_value = ((char *)temp) + sizeof(H5FL_blk_list_t);
788
789
44
done:
790
44
    FUNC_LEAVE_NOAPI(ret_value)
791
44
} /* end H5FL_blk_malloc() */
792
793
/*-------------------------------------------------------------------------
794
 * Function:  H5FL_blk_calloc
795
 *
796
 * Purpose: Allocates memory for a block and clear it to zeros.
797
 *      This routine is used
798
 *      instead of malloc because the block can be kept on a free list so
799
 *      they don't thrash malloc/free as much.
800
 *
801
 * Return:  Success:  valid pointer to the block
802
 *
803
 *    Failure:  NULL
804
 *
805
 *-------------------------------------------------------------------------
806
 */
807
void *
808
H5FL_blk_calloc(H5FL_blk_head_t *head, size_t size)
809
0
{
810
0
    void *ret_value = NULL; /* Pointer to the block to return to the user */
811
812
0
    FUNC_ENTER_NOAPI(NULL)
813
814
    /* Double check parameters */
815
0
    assert(head);
816
0
    assert(size);
817
818
    /* Allocate the block */
819
0
    if (NULL == (ret_value = H5FL_blk_malloc(head, size)))
820
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
821
822
    /* Clear the block to zeros */
823
0
    memset(ret_value, 0, size);
824
825
0
done:
826
0
    FUNC_LEAVE_NOAPI(ret_value)
827
0
} /* end H5FL_blk_calloc() */
828
829
/*-------------------------------------------------------------------------
830
 * Function:  H5FL_blk_free
831
 *
832
 * Purpose: Releases memory for a block.  This routine is used
833
 *      instead of free because the blocks can be kept on a free list so
834
 *      they don't thrash malloc/free as much.
835
 *
836
 * Return:  Success:  NULL
837
 *
838
 *    Failure:  never fails
839
 *
840
 *-------------------------------------------------------------------------
841
 */
842
void *
843
H5FL_blk_free(H5FL_blk_head_t *head, void *block)
844
44
{
845
44
    H5FL_blk_node_t *free_list;        /* The free list of nodes of correct size */
846
44
    H5FL_blk_list_t *temp;             /* Temp. ptr to the new free list node allocated */
847
44
    size_t           free_size;        /* Size of the block freed */
848
44
    void            *ret_value = NULL; /* Return value */
849
850
    /* NOINIT OK here because this must be called after H5FL_blk_malloc/calloc
851
     * -NAF */
852
44
    FUNC_ENTER_NOAPI_NOINIT
853
854
    /* Double check parameters */
855
44
    assert(head);
856
44
    assert(block);
857
858
    /* Get the pointer to the native block info header in front of the native block to free */
859
44
    temp = (H5FL_blk_list_t *)((void *)((unsigned char *)block - sizeof(H5FL_blk_list_t)));
860
861
    /* Save the block's size for later */
862
44
    free_size = temp->size;
863
864
#ifdef H5FL_DEBUG
865
    memset(temp, 255, free_size + sizeof(H5FL_blk_list_t));
866
#endif /* H5FL_DEBUG */
867
868
    /* Check if there is a free list for native blocks of this size */
869
44
    if (NULL == (free_list = H5FL__blk_find_list(&(head->head), free_size)))
870
        /* No free list available, create a new list node and insert it to the queue */
871
0
        free_list = H5FL__blk_create_list(&(head->head), free_size);
872
44
    if (NULL == free_list)
873
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTALLOC, NULL, "couldn't create new list node");
874
875
    /* Prepend the free'd native block to the front of the free list */
876
44
    temp->next      = free_list->list; /* Note: Overwrites the size field in union */
877
44
    free_list->list = temp;
878
879
    /* Increment the number of blocks on free list */
880
44
    free_list->onlist++;
881
44
    head->onlist++;
882
44
    head->list_mem += free_size;
883
884
    /* Increment the amount of "block" freed memory globally */
885
44
    H5FL_blk_gc_head.mem_freed += free_size;
886
887
    /* Check for exceeding free list memory use limits */
888
    /* First check this particular list */
889
44
    if (head->list_mem > H5FL_blk_lst_mem_lim)
890
0
        if (H5FL__blk_gc_list(head) < 0)
891
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
892
893
    /* Then check the global amount memory on block free lists */
894
44
    if (H5FL_blk_gc_head.mem_freed > H5FL_blk_glb_mem_lim)
895
0
        if (H5FL__blk_gc() < 0)
896
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
897
898
44
done:
899
44
    FUNC_LEAVE_NOAPI(ret_value)
900
44
} /* end H5FL_blk_free() */
901
902
/*-------------------------------------------------------------------------
903
 * Function:  H5FL_blk_realloc
904
 *
905
 * Purpose: Resizes a block.  This does things the straightforward, simple way,
906
 *      not actually using realloc.
907
 *
908
 * Return:  Success:  NULL
909
 *
910
 *    Failure:  never fails
911
 *
912
 *-------------------------------------------------------------------------
913
 */
914
void *
915
H5FL_blk_realloc(H5FL_blk_head_t *head, void *block, size_t new_size)
916
16
{
917
16
    void *ret_value = NULL; /* Return value */
918
919
16
    FUNC_ENTER_NOAPI(NULL)
920
921
    /* Double check parameters */
922
16
    assert(head);
923
16
    assert(new_size);
924
925
    /* Check if we are actually re-allocating a block */
926
16
    if (block != NULL) {
927
4
        H5FL_blk_list_t *temp; /* Temp. ptr to the new block node allocated */
928
929
        /* Get the pointer to the chunk info header in front of the chunk to free */
930
4
        temp = (H5FL_blk_list_t *)((void *)((unsigned char *)block - sizeof(H5FL_blk_list_t)));
931
932
        /* check if we are actually changing the size of the buffer */
933
4
        if (new_size != temp->size) {
934
4
            size_t blk_size; /* Temporary block size */
935
936
4
            if (NULL == (ret_value = H5FL_blk_malloc(head, new_size)))
937
0
                HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for block");
938
4
            blk_size = MIN(new_size, temp->size);
939
4
            H5MM_memcpy(ret_value, block, blk_size);
940
4
            H5FL_blk_free(head, block);
941
4
        } /* end if */
942
0
        else
943
0
            ret_value = block;
944
4
    } /* end if */
945
    /* Not re-allocating, just allocate a fresh block */
946
12
    else
947
12
        ret_value = H5FL_blk_malloc(head, new_size);
948
949
16
done:
950
16
    FUNC_LEAVE_NOAPI(ret_value)
951
16
} /* end H5FL_blk_realloc() */
952
953
/*-------------------------------------------------------------------------
954
 * Function:  H5FL__blk_gc_list
955
 *
956
 * Purpose: Garbage collect a priority queue
957
 *
958
 * Return:  Success:  Non-negative
959
 *    Failure:  Negative
960
 *
961
 *-------------------------------------------------------------------------
962
 */
963
static herr_t
964
H5FL__blk_gc_list(H5FL_blk_head_t *head)
965
8
{
966
8
    H5FL_blk_node_t *blk_head; /* Temp. ptr to the free list page node */
967
968
8
    FUNC_ENTER_PACKAGE_NOERR
969
970
    /* Loop through all the nodes in the block free list queue */
971
8
    blk_head = head->head;
972
17
    while (blk_head != NULL) {
973
9
        H5FL_blk_node_t *blk_next; /* Temp. ptr to the next free list node */
974
9
        H5FL_blk_list_t *list;     /* The free list of native nodes of a particular size */
975
976
        /* Sanity check */
977
9
        assert((blk_head->onlist && blk_head->list) || (0 == blk_head->onlist && NULL == blk_head->list));
978
979
        /* Loop through all the blocks in the free list, freeing them */
980
9
        list = blk_head->list;
981
18
        while (list != NULL) {
982
9
            H5FL_blk_list_t *next; /* Temp. ptr to the free list list node */
983
984
            /* Get the pointer to the next node */
985
9
            next = list->next;
986
987
            /* Free the block */
988
9
            H5MM_free(list);
989
990
            /* Advance to the next node */
991
9
            list = next;
992
9
        } /* end while */
993
994
        /* Decrement the number of blocks allocated from this list */
995
9
        blk_head->allocated -= blk_head->onlist;
996
9
        head->allocated -= blk_head->onlist;
997
998
        /* Decrement count of free memory on this "block" list */
999
9
        head->list_mem -= (blk_head->onlist * blk_head->size);
1000
1001
        /* Decrement global count of free memory on "block" lists */
1002
9
        H5FL_blk_gc_head.mem_freed -= (blk_head->onlist * blk_head->size);
1003
1004
        /* Indicate no free nodes on the free list */
1005
9
        blk_head->list   = NULL;
1006
9
        blk_head->onlist = 0;
1007
1008
        /* Get pointer to next node */
1009
9
        blk_next = blk_head->next;
1010
1011
        /* Check for list completely unused now */
1012
9
        if (0 == blk_head->allocated) {
1013
            /* Patch this node out of the PQ */
1014
9
            if (head->head == blk_head)
1015
9
                head->head = blk_head->next;
1016
9
            if (blk_head->prev)
1017
0
                blk_head->prev->next = blk_head->next;
1018
9
            if (blk_head->next)
1019
1
                blk_head->next->prev = blk_head->prev;
1020
1021
            /* Free the free list node */
1022
9
            H5FL_FREE(H5FL_blk_node_t, blk_head);
1023
9
        } /* end if */
1024
1025
        /* Advance to the next node */
1026
9
        blk_head = blk_next;
1027
9
    } /* end while */
1028
1029
    /* Indicate no free nodes on the free list */
1030
8
    head->onlist = 0;
1031
1032
    /* Double check that all the memory on this list is recycled */
1033
8
    assert(0 == head->list_mem);
1034
1035
8
    FUNC_LEAVE_NOAPI(SUCCEED)
1036
8
} /* end H5FL__blk_gc_list() */
1037
1038
/*-------------------------------------------------------------------------
1039
 * Function:  H5FL__blk_gc
1040
 *
1041
 * Purpose: Garbage collect on all the priority queues
1042
 *
1043
 * Return:  Success:  Non-negative
1044
 *    Failure:  Negative
1045
 *
1046
 *-------------------------------------------------------------------------
1047
 */
1048
static herr_t
1049
H5FL__blk_gc(void)
1050
2
{
1051
2
    H5FL_blk_gc_node_t *gc_node;             /* Pointer into the list of things to garbage collect */
1052
2
    herr_t              ret_value = SUCCEED; /* return value*/
1053
1054
2
    FUNC_ENTER_PACKAGE
1055
1056
    /* Walk through all the free lists, free()'ing the nodes */
1057
2
    gc_node = H5FL_blk_gc_head.first;
1058
10
    while (gc_node != NULL) {
1059
        /* For each free list being garbage collected, walk through the nodes and free them */
1060
8
        if (H5FL__blk_gc_list(gc_node->pq) < 0)
1061
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "garbage collection of list failed");
1062
1063
        /* Go on to the next free list to garbage collect */
1064
8
        gc_node = gc_node->next;
1065
8
    } /* end while */
1066
1067
    /* Double check that all the memory on the free lists are recycled */
1068
2
    assert(H5FL_blk_gc_head.mem_freed == 0);
1069
1070
2
done:
1071
2
    FUNC_LEAVE_NOAPI(ret_value)
1072
2
} /* end H5FL__blk_gc() */
1073
1074
/*--------------------------------------------------------------------------
1075
 NAME
1076
    H5FL__blk_term
1077
 PURPOSE
1078
    Terminate various H5FL_blk objects
1079
 USAGE
1080
    void H5FL__blk_term()
1081
 RETURNS
1082
    Success:  Positive if any action might have caused a change in some
1083
                other interface; zero otherwise.
1084
        Failure:  Negative
1085
 DESCRIPTION
1086
    Release any resources allocated.
1087
 GLOBAL VARIABLES
1088
 COMMENTS, BUGS, ASSUMPTIONS
1089
     Can't report errors...
1090
 EXAMPLES
1091
 REVISION LOG
1092
--------------------------------------------------------------------------*/
1093
static int
1094
H5FL__blk_term(void)
1095
2
{
1096
2
    H5FL_blk_gc_node_t *left; /* pointer to garbage collection lists with work left */
1097
1098
2
    FUNC_ENTER_PACKAGE_NOERR
1099
1100
    /* Free the nodes on the garbage collection list, keeping nodes with allocations outstanding */
1101
2
    left = NULL;
1102
10
    while (H5FL_blk_gc_head.first != NULL) {
1103
8
        H5FL_blk_gc_node_t *tmp; /* Temporary pointer to a garbage collection node */
1104
1105
8
        tmp = H5FL_blk_gc_head.first->next;
1106
1107
#ifdef H5FL_DEBUG
1108
        printf("%s: head->name = %s, head->allocated = %d\n", __func__, H5FL_blk_gc_head.first->pq->name,
1109
               (int)H5FL_blk_gc_head.first->pq->allocated);
1110
#endif /* H5FL_DEBUG */
1111
1112
        /* Check if the list has allocations outstanding */
1113
8
        if (H5FL_blk_gc_head.first->pq->allocated > 0) {
1114
            /* Add free list to the list of nodes with allocations open still */
1115
0
            H5FL_blk_gc_head.first->next = left;
1116
0
            left                         = H5FL_blk_gc_head.first;
1117
0
        } /* end if */
1118
        /* No allocations left open for list, get rid of it */
1119
8
        else {
1120
            /* Reset the "initialized" flag, in case we restart this list somehow (I don't know how..) */
1121
8
            H5FL_blk_gc_head.first->pq->init = false;
1122
1123
            /* Free the node from the garbage collection list */
1124
8
            H5MM_free(H5FL_blk_gc_head.first);
1125
8
        } /* end else */
1126
1127
8
        H5FL_blk_gc_head.first = tmp;
1128
8
    } /* end while */
1129
1130
    /* Point to the list of nodes left with allocations open, if any */
1131
2
    H5FL_blk_gc_head.first = left;
1132
1133
2
    FUNC_LEAVE_NOAPI(H5FL_blk_gc_head.first != NULL ? 1 : 0)
1134
2
} /* end H5FL__blk_term() */
1135
1136
/*-------------------------------------------------------------------------
1137
 * Function:  H5FL__arr_init
1138
 *
1139
 * Purpose: Initialize a free list for a arrays of certain type.  Right now,
1140
 *      this just adds the free list to the list of things to garbage collect.
1141
 *
1142
 * Return:  Success:  Non-negative
1143
 *    Failure:  Negative
1144
 *
1145
 *-------------------------------------------------------------------------
1146
 */
1147
static herr_t
1148
H5FL__arr_init(H5FL_arr_head_t *head)
1149
0
{
1150
0
    H5FL_gc_arr_node_t *new_node;            /* Pointer to the node for the new list to garbage collect */
1151
0
    size_t              u;                   /* Local index variable */
1152
0
    herr_t              ret_value = SUCCEED; /* return value*/
1153
1154
0
    FUNC_ENTER_PACKAGE
1155
1156
    /* Allocate a new garbage collection node */
1157
0
    if (NULL == (new_node = (H5FL_gc_arr_node_t *)H5MM_malloc(sizeof(H5FL_gc_arr_node_t))))
1158
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
1159
1160
    /* Initialize the new garbage collection node */
1161
0
    new_node->list = head;
1162
1163
    /* Link in to the garbage collection list */
1164
0
    new_node->next         = H5FL_arr_gc_head.first;
1165
0
    H5FL_arr_gc_head.first = new_node;
1166
1167
    /* Allocate room for the free lists */
1168
0
    if (NULL ==
1169
0
        (head->list_arr = (H5FL_arr_node_t *)H5MM_calloc((size_t)head->maxelem * sizeof(H5FL_arr_node_t))))
1170
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, FAIL, "memory allocation failed");
1171
1172
    /* Initialize the size of each array */
1173
0
    for (u = 0; u < (size_t)head->maxelem; u++)
1174
0
        head->list_arr[u].size = head->base_size + (head->elem_size * u);
1175
1176
    /* Indicate that the free list is initialized */
1177
0
    head->init = true;
1178
1179
0
done:
1180
0
    FUNC_LEAVE_NOAPI(ret_value)
1181
0
} /* end H5FL__arr_init() */
1182
1183
/*-------------------------------------------------------------------------
1184
 * Function:  H5FL_arr_free
1185
 *
1186
 * Purpose: Release an array of objects & put on free list
1187
 *
1188
 * Return:  Success:  Non-negative
1189
 *    Failure:  Negative
1190
 *
1191
 *-------------------------------------------------------------------------
1192
 */
1193
void *
1194
H5FL_arr_free(H5FL_arr_head_t *head, void *obj)
1195
0
{
1196
0
    H5FL_arr_list_t *temp;             /* Temp. ptr to the new free list node allocated */
1197
0
    size_t           mem_size;         /* Size of memory being freed */
1198
0
    size_t           free_nelem;       /* Number of elements in node being free'd */
1199
0
    void            *ret_value = NULL; /* Return value */
1200
1201
    /* NOINIT OK here because this must be called after H5FL_arr_malloc/calloc
1202
     * -NAF */
1203
0
    FUNC_ENTER_NOAPI_NOINIT
1204
1205
    /* The H5MM_xfree code allows obj to null */
1206
0
    if (!obj)
1207
0
        HGOTO_DONE(NULL);
1208
1209
    /* Double check parameters */
1210
0
    assert(head);
1211
1212
    /* Make certain that the free list is initialized */
1213
0
    assert(head->init);
1214
1215
    /* Get the pointer to the info header in front of the block to free */
1216
0
    temp = (H5FL_arr_list_t *)((void *)((unsigned char *)obj - sizeof(H5FL_arr_list_t)));
1217
1218
    /* Get the number of elements */
1219
0
    free_nelem = temp->nelem;
1220
1221
    /* Double-check that there is enough room for arrays of this size */
1222
0
    assert((int)free_nelem <= head->maxelem);
1223
1224
    /* Link into the free list */
1225
0
    temp->next = head->list_arr[free_nelem].list;
1226
1227
    /* Point free list at the node freed */
1228
0
    head->list_arr[free_nelem].list = temp;
1229
1230
    /* Get the size of arrays with this many elements */
1231
0
    mem_size = head->list_arr[free_nelem].size;
1232
1233
    /* Increment the number of blocks & memory used on free list */
1234
0
    head->list_arr[free_nelem].onlist++;
1235
0
    head->list_mem += mem_size;
1236
1237
    /* Increment the amount of "array" freed memory globally */
1238
0
    H5FL_arr_gc_head.mem_freed += mem_size;
1239
1240
    /* Check for exceeding free list memory use limits */
1241
    /* First check this particular list */
1242
0
    if (head->list_mem > H5FL_arr_lst_mem_lim)
1243
0
        if (H5FL__arr_gc_list(head) < 0)
1244
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
1245
1246
    /* Then check the global amount memory on array free lists */
1247
0
    if (H5FL_arr_gc_head.mem_freed > H5FL_arr_glb_mem_lim)
1248
0
        if (H5FL__arr_gc() < 0)
1249
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
1250
1251
0
done:
1252
0
    FUNC_LEAVE_NOAPI(ret_value)
1253
0
} /* end H5FL_arr_free() */
1254
1255
/*-------------------------------------------------------------------------
1256
 * Function:  H5FL_arr_malloc
1257
 *
1258
 * Purpose: Allocate an array of objects
1259
 *
1260
 * Return:  Success:  Pointer to a valid array object
1261
 *    Failure:  NULL
1262
 *
1263
 *-------------------------------------------------------------------------
1264
 */
1265
void *
1266
H5FL_arr_malloc(H5FL_arr_head_t *head, size_t elem)
1267
0
{
1268
0
    H5FL_arr_list_t *new_obj;          /* Pointer to the new free list node allocated */
1269
0
    size_t           mem_size;         /* Size of memory block being recycled */
1270
0
    void            *ret_value = NULL; /* Pointer to the block to return */
1271
1272
0
    FUNC_ENTER_NOAPI(NULL)
1273
1274
    /* Double check parameters */
1275
0
    assert(head);
1276
0
    assert(elem);
1277
1278
    /* Make certain the list is initialized first */
1279
0
    if (!head->init)
1280
0
        if (H5FL__arr_init(head) < 0)
1281
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTINIT, NULL, "can't initialize 'array' blocks");
1282
1283
    /* Sanity check that the number of elements is supported */
1284
0
    assert(elem <= (unsigned)head->maxelem);
1285
1286
    /* Get the set of the memory block */
1287
0
    mem_size = head->list_arr[elem].size;
1288
1289
    /* Check for nodes available on the free list first */
1290
0
    if (head->list_arr[elem].list != NULL) {
1291
        /* Get a pointer to the block on the free list */
1292
0
        new_obj = head->list_arr[elem].list;
1293
1294
        /* Remove node from free list */
1295
0
        head->list_arr[elem].list = head->list_arr[elem].list->next;
1296
1297
        /* Decrement the number of blocks & memory used on free list */
1298
0
        head->list_arr[elem].onlist--;
1299
0
        head->list_mem -= mem_size;
1300
1301
        /* Decrement the amount of global "array" free list memory in use */
1302
0
        H5FL_arr_gc_head.mem_freed -= mem_size;
1303
1304
0
    } /* end if */
1305
    /* Otherwise allocate a node */
1306
0
    else {
1307
0
        if (NULL == (new_obj = (H5FL_arr_list_t *)H5FL__malloc(sizeof(H5FL_arr_list_t) + mem_size)))
1308
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1309
1310
        /* Increment the number of blocks of this size */
1311
0
        head->list_arr[elem].allocated++;
1312
1313
        /* Increment the number of blocks allocated in list, of all sizes */
1314
0
        head->allocated++;
1315
0
    } /* end else */
1316
1317
    /* Initialize the new object */
1318
0
    new_obj->nelem = elem;
1319
1320
    /* Get a pointer to the new block */
1321
0
    ret_value = ((char *)new_obj) + sizeof(H5FL_arr_list_t);
1322
1323
0
done:
1324
0
    FUNC_LEAVE_NOAPI(ret_value)
1325
0
} /* end H5FL_arr_malloc() */
1326
1327
/*-------------------------------------------------------------------------
1328
 * Function:  H5FL_arr_calloc
1329
 *
1330
 * Purpose: Allocate an array of objects and clear it to zeros
1331
 *
1332
 * Return:  Success:  Pointer to a valid array object
1333
 *    Failure:  NULL
1334
 *
1335
 *-------------------------------------------------------------------------
1336
 */
1337
void *
1338
H5FL_arr_calloc(H5FL_arr_head_t *head, size_t elem)
1339
0
{
1340
0
    void *ret_value = NULL; /* Pointer to the block to return */
1341
1342
0
    FUNC_ENTER_NOAPI(NULL)
1343
1344
    /* Double check parameters */
1345
0
    assert(head);
1346
0
    assert(elem);
1347
1348
    /* Allocate the array */
1349
0
    if (NULL == (ret_value = H5FL_arr_malloc(head, elem)))
1350
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1351
1352
    /* Clear to zeros */
1353
0
    memset(ret_value, 0, head->list_arr[elem].size);
1354
1355
0
done:
1356
0
    FUNC_LEAVE_NOAPI(ret_value)
1357
0
} /* end H5FL_arr_calloc() */
1358
1359
/*-------------------------------------------------------------------------
1360
 * Function:  H5FL_arr_realloc
1361
 *
1362
 * Purpose: Reallocate an array of objects
1363
 *
1364
 * Return:  Success:  Pointer to a valid array object
1365
 *    Failure:  NULL
1366
 *
1367
 *-------------------------------------------------------------------------
1368
 */
1369
void *
1370
H5FL_arr_realloc(H5FL_arr_head_t *head, void *obj, size_t new_elem)
1371
0
{
1372
0
    void *ret_value = NULL; /* Pointer to the block to return */
1373
1374
0
    FUNC_ENTER_NOAPI_NOERR
1375
1376
    /* Double check parameters */
1377
0
    assert(head);
1378
0
    assert(new_elem);
1379
1380
    /* Check if we are really allocating the object */
1381
0
    if (obj == NULL)
1382
0
        ret_value = H5FL_arr_malloc(head, new_elem);
1383
0
    else {
1384
0
        H5FL_arr_list_t *temp; /* Temp. ptr to the new free list node allocated */
1385
1386
        /* Sanity check that the number of elements is supported */
1387
0
        assert((int)new_elem <= head->maxelem);
1388
1389
        /* Get the pointer to the info header in front of the block to free */
1390
0
        temp = (H5FL_arr_list_t *)((void *)((unsigned char *)obj - sizeof(H5FL_arr_list_t)));
1391
1392
        /* Check if the size is really changing */
1393
0
        if (temp->nelem != new_elem) {
1394
0
            size_t blk_size; /* Size of block */
1395
1396
            /* Get the new array of objects */
1397
0
            ret_value = H5FL_arr_malloc(head, new_elem);
1398
1399
            /* Copy the appropriate amount of elements */
1400
0
            blk_size = head->list_arr[MIN(temp->nelem, new_elem)].size;
1401
0
            H5MM_memcpy(ret_value, obj, blk_size);
1402
1403
            /* Free the old block */
1404
0
            H5FL_arr_free(head, obj);
1405
0
        } /* end if */
1406
0
        else
1407
0
            ret_value = obj;
1408
0
    } /* end else */
1409
1410
0
    FUNC_LEAVE_NOAPI(ret_value)
1411
0
} /* end H5FL_arr_realloc() */
1412
1413
/*-------------------------------------------------------------------------
1414
 * Function:  H5FL__arr_gc_list
1415
 *
1416
 * Purpose: Garbage collect on an array object free list
1417
 *
1418
 * Return:  Success:  Non-negative
1419
 *    Failure:  Negative
1420
 *
1421
 *-------------------------------------------------------------------------
1422
 */
1423
static herr_t
1424
H5FL__arr_gc_list(H5FL_arr_head_t *head)
1425
0
{
1426
0
    unsigned u; /* Counter for array of free lists */
1427
1428
0
    FUNC_ENTER_PACKAGE_NOERR
1429
1430
    /* Walk through the array of free lists */
1431
0
    for (u = 0; u < (unsigned)head->maxelem; u++) {
1432
0
        if (head->list_arr[u].onlist > 0) {
1433
0
            H5FL_arr_list_t *arr_free_list; /* Pointer to nodes in free list being garbage collected */
1434
1435
            /* For each free list being garbage collected, walk through the nodes and free them */
1436
0
            arr_free_list = head->list_arr[u].list;
1437
0
            while (arr_free_list != NULL) {
1438
0
                H5FL_arr_list_t *tmp; /* Temporary node pointer */
1439
1440
                /* Get the pointer to the next node */
1441
0
                tmp = arr_free_list->next;
1442
1443
                /* Free the node */
1444
0
                H5MM_free(arr_free_list);
1445
1446
                /* Advance to the next node */
1447
0
                arr_free_list = tmp;
1448
0
            } /* end while */
1449
1450
            /* Decrement the count of nodes allocated */
1451
0
            head->list_arr[u].allocated -= head->list_arr[u].onlist;
1452
0
            head->allocated -= head->list_arr[u].onlist;
1453
1454
            /* Decrement count of free memory on this "array" list */
1455
0
            head->list_mem -= (head->list_arr[u].onlist * head->list_arr[u].size);
1456
1457
            /* Decrement global count of free memory on "array" lists */
1458
0
            H5FL_arr_gc_head.mem_freed -= (head->list_arr[u].onlist * head->list_arr[u].size);
1459
1460
            /* Indicate no free nodes on the free list */
1461
0
            head->list_arr[u].list   = NULL;
1462
0
            head->list_arr[u].onlist = 0;
1463
0
        } /* end if */
1464
0
    }     /* end for */
1465
1466
    /* Double check that all the memory on this list is recycled */
1467
0
    assert(head->list_mem == 0);
1468
1469
0
    FUNC_LEAVE_NOAPI(SUCCEED)
1470
0
} /* end H5FL__arr_gc_list() */
1471
1472
/*-------------------------------------------------------------------------
1473
 * Function:  H5FL__arr_gc
1474
 *
1475
 * Purpose: Garbage collect on all the array object free lists
1476
 *
1477
 * Return:  Success:  Non-negative
1478
 *    Failure:  Negative
1479
 *
1480
 *-------------------------------------------------------------------------
1481
 */
1482
static herr_t
1483
H5FL__arr_gc(void)
1484
2
{
1485
2
    H5FL_gc_arr_node_t *gc_arr_node;         /* Pointer into the list of things to garbage collect */
1486
2
    herr_t              ret_value = SUCCEED; /* return value*/
1487
1488
2
    FUNC_ENTER_PACKAGE
1489
1490
    /* Walk through all the free lists, free()'ing the nodes */
1491
2
    gc_arr_node = H5FL_arr_gc_head.first;
1492
2
    while (gc_arr_node != NULL) {
1493
        /* Release the free nodes on the list */
1494
0
        if (H5FL__arr_gc_list(gc_arr_node->list) < 0)
1495
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "garbage collection of list failed");
1496
1497
        /* Go on to the next free list to garbage collect */
1498
0
        gc_arr_node = gc_arr_node->next;
1499
0
    } /* end while */
1500
1501
    /* Double check that all the memory on the free lists are recycled */
1502
2
    assert(H5FL_arr_gc_head.mem_freed == 0);
1503
1504
2
done:
1505
2
    FUNC_LEAVE_NOAPI(ret_value)
1506
2
} /* end H5FL__arr_gc() */
1507
1508
/*--------------------------------------------------------------------------
1509
 NAME
1510
    H5FL__arr_term
1511
 PURPOSE
1512
    Terminate various H5FL array object free lists
1513
 USAGE
1514
    int H5FL__arr_term()
1515
 RETURNS
1516
    Success:  Positive if any action might have caused a change in some
1517
                other interface; zero otherwise.
1518
        Failure:  Negative
1519
 DESCRIPTION
1520
    Release any resources allocated.
1521
 GLOBAL VARIABLES
1522
 COMMENTS, BUGS, ASSUMPTIONS
1523
     Can't report errors...
1524
 EXAMPLES
1525
 REVISION LOG
1526
--------------------------------------------------------------------------*/
1527
static int
1528
H5FL__arr_term(void)
1529
2
{
1530
2
    H5FL_gc_arr_node_t *left; /* pointer to garbage collection lists with work left */
1531
1532
2
    FUNC_ENTER_PACKAGE_NOERR
1533
1534
    /* Free the nodes on the garbage collection list, keeping nodes with allocations outstanding */
1535
2
    left = NULL;
1536
2
    while (H5FL_arr_gc_head.first != NULL) {
1537
0
        H5FL_gc_arr_node_t *tmp; /* Temporary pointer to a garbage collection node */
1538
1539
0
        tmp = H5FL_arr_gc_head.first->next;
1540
1541
        /* Check if the list has allocations outstanding */
1542
#ifdef H5FL_DEBUG
1543
        printf("%s: head->name = %s, head->allocated = %d\n", __func__, H5FL_arr_gc_head.first->list->name,
1544
               (int)H5FL_arr_gc_head.first->list->allocated);
1545
#endif /* H5FL_DEBUG */
1546
0
        if (H5FL_arr_gc_head.first->list->allocated > 0) {
1547
            /* Add free list to the list of nodes with allocations open still */
1548
0
            H5FL_arr_gc_head.first->next = left;
1549
0
            left                         = H5FL_arr_gc_head.first;
1550
0
        } /* end if */
1551
        /* No allocations left open for list, get rid of it */
1552
0
        else {
1553
            /* Free the array of free lists */
1554
0
            H5MM_xfree(H5FL_arr_gc_head.first->list->list_arr);
1555
1556
            /* Reset the "initialized" flag, in case we restart this list somehow (I don't know how..) */
1557
0
            H5FL_arr_gc_head.first->list->init = false;
1558
1559
            /* Free the node from the garbage collection list */
1560
0
            H5MM_free(H5FL_arr_gc_head.first);
1561
0
        } /* end else */
1562
1563
0
        H5FL_arr_gc_head.first = tmp;
1564
0
    } /* end while */
1565
1566
    /* Point to the list of nodes left with allocations open, if any */
1567
2
    H5FL_arr_gc_head.first = left;
1568
1569
2
    FUNC_LEAVE_NOAPI(H5FL_arr_gc_head.first != NULL ? 1 : 0)
1570
2
} /* end H5FL__arr_term() */
1571
1572
/*-------------------------------------------------------------------------
1573
 * Function:  H5FL_seq_free
1574
 *
1575
 * Purpose: Release a sequence of objects & put on free list
1576
 *
1577
 * Return:  Success:  Non-negative
1578
 *    Failure:  Negative
1579
 *
1580
 *-------------------------------------------------------------------------
1581
 */
1582
void *
1583
H5FL_seq_free(H5FL_seq_head_t *head, void *obj)
1584
20
{
1585
    /* NOINIT OK here because this must be called after H5FL_seq_malloc/calloc
1586
     * -NAF */
1587
20
    FUNC_ENTER_NOAPI_NOINIT_NOERR
1588
1589
    /* Double check parameters */
1590
20
    assert(head);
1591
20
    assert(obj);
1592
1593
    /* Make certain that the free list is initialized */
1594
20
    assert(head->queue.init);
1595
1596
    /* Use block routine */
1597
20
    H5FL_blk_free(&(head->queue), obj);
1598
1599
20
    FUNC_LEAVE_NOAPI(NULL)
1600
20
} /* end H5FL_seq_free() */
1601
1602
/*-------------------------------------------------------------------------
1603
 * Function:  H5FL_seq_malloc
1604
 *
1605
 * Purpose: Allocate a sequence of objects
1606
 *
1607
 * Return:  Success:  Pointer to a valid sequence object
1608
 *    Failure:  NULL
1609
 *
1610
 *-------------------------------------------------------------------------
1611
 */
1612
void *
1613
H5FL_seq_malloc(H5FL_seq_head_t *head, size_t elem)
1614
12
{
1615
12
    void *ret_value = NULL; /* Pointer to the block to return */
1616
1617
12
    FUNC_ENTER_NOAPI_NOERR
1618
1619
    /* Double check parameters */
1620
12
    assert(head);
1621
12
    assert(elem);
1622
1623
    /* Use block routine */
1624
12
    ret_value = H5FL_blk_malloc(&(head->queue), head->size * elem);
1625
1626
12
    FUNC_LEAVE_NOAPI(ret_value)
1627
12
} /* end H5FL_seq_malloc() */
1628
1629
/*-------------------------------------------------------------------------
1630
 * Function:  H5FL_seq_calloc
1631
 *
1632
 * Purpose: Allocate a sequence of objects and clear it to zeros
1633
 *
1634
 * Return:  Success:  Pointer to a valid array object
1635
 *    Failure:  NULL
1636
 *
1637
 *-------------------------------------------------------------------------
1638
 */
1639
void *
1640
H5FL_seq_calloc(H5FL_seq_head_t *head, size_t elem)
1641
0
{
1642
0
    void *ret_value = NULL; /* Pointer to the block to return */
1643
1644
0
    FUNC_ENTER_NOAPI_NOERR
1645
1646
    /* Double check parameters */
1647
0
    assert(head);
1648
0
    assert(elem);
1649
1650
    /* Use block routine */
1651
0
    ret_value = H5FL_blk_calloc(&(head->queue), head->size * elem);
1652
1653
0
    FUNC_LEAVE_NOAPI(ret_value)
1654
0
} /* end H5FL_seq_calloc() */
1655
1656
/*-------------------------------------------------------------------------
1657
 * Function:  H5FL_seq_realloc
1658
 *
1659
 * Purpose: Reallocate a sequence of objects
1660
 *
1661
 * Return:  Success:  Pointer to a valid sequence object
1662
 *    Failure:  NULL
1663
 *
1664
 *-------------------------------------------------------------------------
1665
 */
1666
void *
1667
H5FL_seq_realloc(H5FL_seq_head_t *head, void *obj, size_t new_elem)
1668
12
{
1669
12
    void *ret_value = NULL; /* Pointer to the block to return */
1670
1671
12
    FUNC_ENTER_NOAPI_NOERR
1672
1673
    /* Double check parameters */
1674
12
    assert(head);
1675
12
    assert(new_elem);
1676
1677
    /* Use block routine */
1678
12
    ret_value = H5FL_blk_realloc(&(head->queue), obj, head->size * new_elem);
1679
1680
12
    FUNC_LEAVE_NOAPI(ret_value)
1681
12
} /* end H5FL_seq_realloc() */
1682
1683
/*-------------------------------------------------------------------------
1684
 * Function:  H5FL_fac_init
1685
 *
1686
 * Purpose: Initialize a block factory
1687
 *
1688
 * Return:  Success:  Pointer to factory object
1689
 *    Failure:  NULL
1690
 *
1691
 *-------------------------------------------------------------------------
1692
 */
1693
H5FL_fac_head_t *
1694
H5FL_fac_init(size_t size)
1695
8
{
1696
8
    H5FL_fac_gc_node_t *new_node  = NULL; /* Pointer to the node for the new list to garbage collect */
1697
8
    H5FL_fac_head_t    *factory   = NULL; /* Pointer to new block factory */
1698
8
    H5FL_fac_head_t    *ret_value = NULL; /* Return value */
1699
1700
8
    FUNC_ENTER_NOAPI(NULL)
1701
1702
    /* Sanity check */
1703
8
    assert(size > 0);
1704
1705
    /* Allocate room for the new factory */
1706
8
    if (NULL == (factory = (H5FL_fac_head_t *)H5FL_CALLOC(H5FL_fac_head_t)))
1707
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed for factory object");
1708
1709
    /* Set size of blocks for factory */
1710
8
    factory->size = size;
1711
1712
    /* Allocate a new garbage collection node */
1713
8
    if (NULL == (new_node = (H5FL_fac_gc_node_t *)H5FL_MALLOC(H5FL_fac_gc_node_t)))
1714
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1715
1716
    /* Initialize the new garbage collection node */
1717
8
    new_node->list = factory;
1718
1719
    /* Link in to the garbage collection list */
1720
8
    new_node->next         = H5FL_fac_gc_head.first;
1721
8
    H5FL_fac_gc_head.first = new_node;
1722
8
    if (new_node->next)
1723
6
        new_node->next->list->prev_gc = new_node;
1724
    /* The new factory's prev_gc field will be set to NULL */
1725
1726
    /* Make certain that the space allocated is large enough to store a free list pointer (eventually) */
1727
8
    if (factory->size < sizeof(H5FL_fac_node_t))
1728
0
        factory->size = sizeof(H5FL_fac_node_t);
1729
1730
    /* Indicate that the free list is initialized */
1731
8
    factory->init = true;
1732
1733
    /* Set return value */
1734
8
    ret_value = factory;
1735
1736
8
done:
1737
8
    if (!ret_value) {
1738
0
        if (factory)
1739
0
            factory = H5FL_FREE(H5FL_fac_head_t, factory);
1740
0
        if (new_node)
1741
0
            new_node = H5FL_FREE(H5FL_fac_gc_node_t, new_node);
1742
0
    } /* end if */
1743
1744
8
    FUNC_LEAVE_NOAPI(ret_value)
1745
8
} /* end H5FL_fac_init() */
1746
1747
/*-------------------------------------------------------------------------
1748
 * Function:  H5FL_fac_free
1749
 *
1750
 * Purpose: Release a block back to a factory & put on free list
1751
 *
1752
 * Return:  NULL
1753
 *
1754
 *-------------------------------------------------------------------------
1755
 */
1756
void *
1757
H5FL_fac_free(H5FL_fac_head_t *head, void *obj)
1758
3.62k
{
1759
3.62k
    void *ret_value = NULL; /* Return value */
1760
1761
    /* NOINIT OK here because this must be called after H5FL_fac_init -NAF */
1762
3.62k
    FUNC_ENTER_NOAPI_NOINIT
1763
1764
    /* Double check parameters */
1765
3.62k
    assert(head);
1766
3.62k
    assert(obj);
1767
1768
#ifdef H5FL_DEBUG
1769
    memset(obj, 255, head->size);
1770
#endif /* H5FL_DEBUG */
1771
1772
    /* Make certain that the free list is initialized */
1773
3.62k
    assert(head->init);
1774
1775
    /* Link into the free list */
1776
3.62k
    ((H5FL_fac_node_t *)obj)->next = head->list;
1777
1778
    /* Point free list at the node freed */
1779
3.62k
    head->list = (H5FL_fac_node_t *)obj;
1780
1781
    /* Increment the number of blocks on free list */
1782
3.62k
    head->onlist++;
1783
1784
    /* Increment the amount of "factory" freed memory globally */
1785
3.62k
    H5FL_fac_gc_head.mem_freed += head->size;
1786
1787
    /* Check for exceeding free list memory use limits */
1788
    /* First check this particular list */
1789
3.62k
    if (head->onlist * head->size > H5FL_fac_lst_mem_lim)
1790
0
        if (H5FL__fac_gc_list(head) < 0)
1791
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
1792
1793
    /* Then check the global amount memory on factory free lists */
1794
3.62k
    if (H5FL_fac_gc_head.mem_freed > H5FL_fac_glb_mem_lim)
1795
0
        if (H5FL__fac_gc() < 0)
1796
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, NULL, "garbage collection failed during free");
1797
1798
3.62k
done:
1799
3.62k
    FUNC_LEAVE_NOAPI(ret_value)
1800
3.62k
} /* end H5FL_fac_free() */
1801
1802
/*-------------------------------------------------------------------------
1803
 * Function:  H5FL_fac_malloc
1804
 *
1805
 * Purpose: Allocate a block from a factory
1806
 *
1807
 * Return:  Success:  Pointer to a valid sequence object
1808
 *    Failure:  NULL
1809
 *
1810
 *-------------------------------------------------------------------------
1811
 */
1812
void *
1813
H5FL_fac_malloc(H5FL_fac_head_t *head)
1814
3.62k
{
1815
3.62k
    void *ret_value = NULL; /* Pointer to the block to return */
1816
1817
    /* NOINIT OK here because this must be called after H5FL_fac_init -NAF */
1818
3.62k
    FUNC_ENTER_NOAPI_NOINIT
1819
1820
    /* Double check parameters */
1821
3.62k
    assert(head);
1822
3.62k
    assert(head->init);
1823
1824
    /* Check for nodes available on the free list first */
1825
3.62k
    if (head->list != NULL) {
1826
        /* Get a pointer to the block on the free list */
1827
3.19k
        ret_value = (void *)(head->list);
1828
1829
        /* Remove node from free list */
1830
3.19k
        head->list = head->list->next;
1831
1832
        /* Decrement the number of blocks & memory on free list */
1833
3.19k
        head->onlist--;
1834
1835
        /* Decrement the amount of global "factory" free list memory in use */
1836
3.19k
        H5FL_fac_gc_head.mem_freed -= (head->size);
1837
3.19k
    } /* end if */
1838
    /* Otherwise allocate a node */
1839
436
    else {
1840
436
        if (NULL == (ret_value = H5FL__malloc(head->size)))
1841
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1842
1843
        /* Increment the number of blocks allocated in list */
1844
436
        head->allocated++;
1845
436
    } /* end else */
1846
1847
3.62k
done:
1848
3.62k
    FUNC_LEAVE_NOAPI(ret_value)
1849
3.62k
} /* end H5FL_fac_malloc() */
1850
1851
/*-------------------------------------------------------------------------
1852
 * Function:  H5FL_fac_calloc
1853
 *
1854
 * Purpose: Allocate a block from a factory and clear it to zeros
1855
 *
1856
 * Return:  Success:  Pointer to a valid array object
1857
 *    Failure:  NULL
1858
 *
1859
 *-------------------------------------------------------------------------
1860
 */
1861
void *
1862
H5FL_fac_calloc(H5FL_fac_head_t *head)
1863
0
{
1864
0
    void *ret_value = NULL; /* Pointer to the block to return */
1865
1866
    /* NOINIT OK here because this must be called after H5FL_fac_init -NAF */
1867
0
    FUNC_ENTER_NOAPI_NOINIT
1868
1869
    /* Double check parameters */
1870
0
    assert(head);
1871
1872
    /* Allocate the block */
1873
0
    if (NULL == (ret_value = H5FL_fac_malloc(head)))
1874
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_NOSPACE, NULL, "memory allocation failed");
1875
1876
    /* Clear to zeros */
1877
0
    memset(ret_value, 0, head->size);
1878
1879
0
done:
1880
0
    FUNC_LEAVE_NOAPI(ret_value)
1881
0
} /* end H5FL_fac_calloc() */
1882
1883
/*-------------------------------------------------------------------------
1884
 * Function:  H5FL__fac_gc_list
1885
 *
1886
 * Purpose: Garbage collect on a particular factory free list
1887
 *
1888
 * Return:  Success:  Non-negative
1889
 *    Failure:  Negative
1890
 *
1891
 *-------------------------------------------------------------------------
1892
 */
1893
static herr_t
1894
H5FL__fac_gc_list(H5FL_fac_head_t *head)
1895
8
{
1896
8
    H5FL_fac_node_t *free_list; /* Pointer to nodes in free list being garbage collected */
1897
1898
8
    FUNC_ENTER_PACKAGE_NOERR
1899
1900
    /* For each free list being garbage collected, walk through the nodes and free them */
1901
8
    free_list = head->list;
1902
444
    while (free_list != NULL) {
1903
436
        H5FL_fac_node_t *tmp; /* Temporary node pointer */
1904
1905
        /* Get the pointer to the next node */
1906
436
        tmp = free_list->next;
1907
1908
        /* Free the block */
1909
436
        H5MM_free(free_list);
1910
1911
        /* Advance to the next node */
1912
436
        free_list = tmp;
1913
436
    } /* end while */
1914
1915
    /* Decrement the count of nodes allocated and free the node */
1916
8
    head->allocated -= head->onlist;
1917
1918
    /* Decrement global count of free memory on "factory" lists */
1919
8
    H5FL_fac_gc_head.mem_freed -= (head->onlist * head->size);
1920
1921
    /* Indicate no free nodes on the free list */
1922
8
    head->list   = NULL;
1923
8
    head->onlist = 0;
1924
1925
8
    FUNC_LEAVE_NOAPI(SUCCEED)
1926
8
} /* end H5FL__fac_gc_list() */
1927
1928
/*-------------------------------------------------------------------------
1929
 * Function:  H5FL__fac_gc
1930
 *
1931
 * Purpose: Garbage collect on all the factory free lists
1932
 *
1933
 * Return:  Success:  Non-negative
1934
 *    Failure:  Negative
1935
 *
1936
 *-------------------------------------------------------------------------
1937
 */
1938
static herr_t
1939
H5FL__fac_gc(void)
1940
2
{
1941
2
    H5FL_fac_gc_node_t *gc_node;             /* Pointer into the list of things to garbage collect */
1942
2
    herr_t              ret_value = SUCCEED; /* return value*/
1943
1944
2
    FUNC_ENTER_PACKAGE
1945
1946
    /* Walk through all the free lists, free()'ing the nodes */
1947
2
    gc_node = H5FL_fac_gc_head.first;
1948
2
    while (gc_node != NULL) {
1949
        /* Release the free nodes on the list */
1950
0
        if (H5FL__fac_gc_list(gc_node->list) < 0)
1951
0
            HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "garbage collection of list failed");
1952
1953
        /* Go on to the next free list to garbage collect */
1954
0
        gc_node = gc_node->next;
1955
0
    } /* end while */
1956
1957
    /* Double check that all the memory on the free lists is recycled */
1958
2
    assert(H5FL_fac_gc_head.mem_freed == 0);
1959
1960
2
done:
1961
2
    FUNC_LEAVE_NOAPI(ret_value)
1962
2
} /* end H5FL__fac_gc() */
1963
1964
/*-------------------------------------------------------------------------
1965
 * Function:  H5FL_fac_term
1966
 *
1967
 * Purpose: Terminate a block factory
1968
 *
1969
 * Return:  Success:  non-negative
1970
 *    Failure:  negative
1971
 *
1972
 *-------------------------------------------------------------------------
1973
 */
1974
herr_t
1975
H5FL_fac_term(H5FL_fac_head_t *factory)
1976
8
{
1977
8
    H5FL_fac_gc_node_t *tmp;                 /* Temporary pointer to a garbage collection node */
1978
8
    herr_t              ret_value = SUCCEED; /* Return value */
1979
1980
    /* NOINIT OK here because this must be called after H5FL_fac_init -NAF */
1981
8
    FUNC_ENTER_NOAPI_NOINIT
1982
1983
    /* Sanity check */
1984
8
    assert(factory);
1985
1986
    /* Garbage collect all the blocks in the factory's free list */
1987
8
    if (H5FL__fac_gc_list(factory) < 0)
1988
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "garbage collection of factory failed");
1989
1990
    /* Verify that all the blocks have been freed */
1991
8
    if (factory->allocated > 0)
1992
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTRELEASE, FAIL, "factory still has objects allocated");
1993
1994
    /* Unlink block free list for factory from global free list */
1995
8
    if (factory->prev_gc) {
1996
6
        H5FL_fac_gc_node_t *last =
1997
6
            factory->prev_gc; /* Garbage collection node before the one being removed */
1998
1999
6
        assert(last->next->list == factory);
2000
6
        tmp        = last->next->next;
2001
6
        last->next = H5FL_FREE(H5FL_fac_gc_node_t, last->next);
2002
6
        last->next = tmp;
2003
6
        if (tmp)
2004
0
            tmp->list->prev_gc = last;
2005
6
    }
2006
2
    else {
2007
2
        assert(H5FL_fac_gc_head.first->list == factory);
2008
2
        tmp                    = H5FL_fac_gc_head.first->next;
2009
2
        H5FL_fac_gc_head.first = H5FL_FREE(H5FL_fac_gc_node_t, H5FL_fac_gc_head.first);
2010
2
        H5FL_fac_gc_head.first = tmp;
2011
2
        if (tmp)
2012
0
            tmp->list->prev_gc = NULL;
2013
2
    } /* end else */
2014
2015
    /* Free factory info */
2016
8
    factory = H5FL_FREE(H5FL_fac_head_t, factory);
2017
2018
8
done:
2019
8
    FUNC_LEAVE_NOAPI(ret_value)
2020
8
} /* end H5FL_fac_term() */
2021
2022
/*-------------------------------------------------------------------------
2023
 * Function:  H5FL__fac_term_all
2024
 *
2025
 * Purpose: Terminate all block factories
2026
 *
2027
 * Return:  0.  There should never be any outstanding allocations
2028
 *              when this is called.
2029
 *
2030
 *-------------------------------------------------------------------------
2031
 */
2032
static int
2033
H5FL__fac_term_all(void)
2034
2
{
2035
2
    FUNC_ENTER_PACKAGE_NOERR
2036
2037
    /* Free the nodes on the garbage collection list */
2038
2
    while (H5FL_fac_gc_head.first != NULL) {
2039
0
        H5FL_fac_gc_node_t *tmp; /* Temporary pointer to a garbage collection node */
2040
2041
0
        tmp = H5FL_fac_gc_head.first->next;
2042
2043
#ifdef H5FL_DEBUG
2044
        printf("%s: head->size = %d, head->allocated = %d\n", __func__,
2045
               (int)H5FL_fac_gc_head.first->list->size, (int)H5FL_fac_gc_head.first->list->allocated);
2046
#endif /* H5FL_DEBUG */
2047
2048
        /* The list cannot have any allocations outstanding */
2049
0
        assert(H5FL_fac_gc_head.first->list->allocated == 0);
2050
2051
        /* Reset the "initialized" flag, in case we restart this list somehow (I don't know how..) */
2052
0
        H5FL_fac_gc_head.first->list->init = false;
2053
2054
        /* Free the node from the garbage collection list */
2055
0
        H5FL_fac_gc_head.first = H5FL_FREE(H5FL_fac_gc_node_t, H5FL_fac_gc_head.first);
2056
2057
0
        H5FL_fac_gc_head.first = tmp;
2058
0
    } /* end while */
2059
2060
2
    FUNC_LEAVE_NOAPI(0)
2061
2
} /* end H5FL__fac_term_all() */
2062
2063
/*-------------------------------------------------------------------------
2064
 * Function:  H5FL_garbage_coll
2065
 *
2066
 * Purpose: Garbage collect on all the free lists
2067
 *
2068
 * Return:  Success:  Non-negative
2069
 *    Failure:  Negative
2070
 *
2071
 *-------------------------------------------------------------------------
2072
 */
2073
herr_t
2074
H5FL_garbage_coll(void)
2075
2
{
2076
2
    herr_t ret_value = SUCCEED;
2077
2078
2
    FUNC_ENTER_NOAPI(FAIL)
2079
2080
    /* Garbage collect the free lists for array objects */
2081
2
    if (H5FL__arr_gc() < 0)
2082
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "can't garbage collect array objects");
2083
2084
    /* Garbage collect free lists for blocks */
2085
2
    if (H5FL__blk_gc() < 0)
2086
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "can't garbage collect block objects");
2087
2088
    /* Garbage collect the free lists for regular objects */
2089
2
    if (H5FL__reg_gc() < 0)
2090
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "can't garbage collect regular objects");
2091
2092
    /* Garbage collect the free lists for factory objects */
2093
2
    if (H5FL__fac_gc() < 0)
2094
0
        HGOTO_ERROR(H5E_RESOURCE, H5E_CANTGC, FAIL, "can't garbage collect factory objects");
2095
2096
2
done:
2097
2
    FUNC_LEAVE_NOAPI(ret_value)
2098
2
} /* end H5FL_garbage_coll() */
2099
2100
/*-------------------------------------------------------------------------
2101
 * Function:  H5FL_set_free_list_limits
2102
 *
2103
 * Purpose: Sets limits on the different kinds of free lists.  Setting a value
2104
 *      of -1 for a limit means no limit of that type.  These limits are global
2105
 *      for the entire library.  Each "global" limit only applies to free lists
2106
 *      of that type, so if an application sets a limit of 1 MB on each of the
2107
 *      global lists, up to 3 MB of total storage might be allocated (1MB on
2108
 *      each of regular, array and block type lists).
2109
 *
2110
 * Parameters:
2111
 *  int reg_global_lim;  IN: The limit on all "regular" free list memory used
2112
 *  int reg_list_lim;    IN: The limit on memory used in each "regular" free list
2113
 *  int arr_global_lim;  IN: The limit on all "array" free list memory used
2114
 *  int arr_list_lim;    IN: The limit on memory used in each "array" free list
2115
 *  int blk_global_lim;  IN: The limit on all "block" free list memory used
2116
 *  int blk_list_lim;    IN: The limit on memory used in each "block" free list
2117
 *
2118
 * Return:  Success:  non-negative
2119
 *
2120
 *    Failure:  negative
2121
 *
2122
 *-------------------------------------------------------------------------
2123
 */
2124
herr_t
2125
H5FL_set_free_list_limits(int reg_global_lim, int reg_list_lim, int arr_global_lim, int arr_list_lim,
2126
                          int blk_global_lim, int blk_list_lim, int fac_global_lim, int fac_list_lim)
2127
0
{
2128
0
    herr_t ret_value = SUCCEED; /* Return value */
2129
2130
0
    FUNC_ENTER_NOAPI_NOERR
2131
2132
    /* Set the limit variables */
2133
    /* limit on all regular free lists */
2134
0
    H5FL_reg_glb_mem_lim = (reg_global_lim == -1 ? UINT_MAX : (size_t)reg_global_lim);
2135
    /* limit on each regular free list */
2136
0
    H5FL_reg_lst_mem_lim = (reg_list_lim == -1 ? UINT_MAX : (size_t)reg_list_lim);
2137
    /* limit on all array free lists */
2138
0
    H5FL_arr_glb_mem_lim = (arr_global_lim == -1 ? UINT_MAX : (size_t)arr_global_lim);
2139
    /* limit on each array free list */
2140
0
    H5FL_arr_lst_mem_lim = (arr_list_lim == -1 ? UINT_MAX : (size_t)arr_list_lim);
2141
    /* limit on all block free lists */
2142
0
    H5FL_blk_glb_mem_lim = (blk_global_lim == -1 ? UINT_MAX : (size_t)blk_global_lim);
2143
    /* limit on each block free list */
2144
0
    H5FL_blk_lst_mem_lim = (blk_list_lim == -1 ? UINT_MAX : (size_t)blk_list_lim);
2145
    /* limit on all factory free lists */
2146
0
    H5FL_fac_glb_mem_lim = (fac_global_lim == -1 ? UINT_MAX : (size_t)fac_global_lim);
2147
    /* limit on each factory free list */
2148
0
    H5FL_fac_lst_mem_lim = (fac_list_lim == -1 ? UINT_MAX : (size_t)fac_list_lim);
2149
2150
0
    FUNC_LEAVE_NOAPI(ret_value)
2151
0
} /* end H5FL_set_free_list_limits() */
2152
2153
/*-------------------------------------------------------------------------
2154
 * Function:  H5FL_get_free_list_sizes
2155
 *
2156
 * Purpose: Gets the current size of the different kinds of free lists.
2157
 *      These lists are global for the entire library.  The size returned
2158
 *      included nodes that are freed and awaiting garbage collection /
2159
 *      reallocation.
2160
 *
2161
 * Parameters:
2162
 *  size_t *reg_size;    OUT: The current size of all "regular" free list memory used
2163
 *  size_t *arr_size;    OUT: The current size of all "array" free list memory used
2164
 *  size_t *blk_size;    OUT: The current size of all "block" free list memory used
2165
 *  size_t *fac_size;    OUT: The current size of all "factory" free list memory used
2166
 *
2167
 * Return:  Success:  non-negative
2168
 *    Failure:  negative
2169
 *
2170
 *-------------------------------------------------------------------------
2171
 */
2172
herr_t
2173
H5FL_get_free_list_sizes(size_t *reg_size, size_t *arr_size, size_t *blk_size, size_t *fac_size)
2174
0
{
2175
0
    FUNC_ENTER_NOAPI_NOERR
2176
2177
    /* Retrieve the amount of "regular" memory used */
2178
0
    if (reg_size) {
2179
0
        H5FL_reg_gc_node_t *gc_node; /* Pointer into the list of lists */
2180
2181
        /* Walk through all the free lists, counting the amount of memory */
2182
0
        *reg_size = 0;
2183
0
        gc_node   = H5FL_reg_gc_head.first;
2184
0
        while (gc_node != NULL) {
2185
0
            H5FL_reg_head_t *reg_list = gc_node->list; /* Head of list */
2186
2187
            /* Sanity check */
2188
0
            assert(reg_list->init);
2189
2190
            /* Add the amount of memory for this list */
2191
0
            *reg_size += (reg_list->size * reg_list->allocated);
2192
2193
            /* Go on to the next free list */
2194
0
            gc_node = gc_node->next;
2195
0
        } /* end while */
2196
0
    }     /* end if */
2197
2198
    /* Retrieve the amount of "array" memory used */
2199
0
    if (arr_size) {
2200
0
        H5FL_gc_arr_node_t *gc_arr_node; /* Pointer into the list of things to garbage collect */
2201
2202
        /* Walk through all the free lists, counting the amount of memory */
2203
0
        *arr_size   = 0;
2204
0
        gc_arr_node = H5FL_arr_gc_head.first;
2205
0
        while (gc_arr_node != NULL) {
2206
0
            H5FL_arr_head_t *head = gc_arr_node->list; /* Head of array list elements */
2207
2208
            /* Sanity check */
2209
0
            assert(head->init);
2210
2211
            /* Check for any allocated elements in this list */
2212
0
            if (head->allocated > 0) {
2213
0
                unsigned u;
2214
2215
                /* Walk through the free lists for array sizes */
2216
0
                for (u = 0; u < (unsigned)head->maxelem; u++)
2217
                    /* Add the amount of memory for this size */
2218
0
                    *arr_size += head->list_arr[u].allocated * head->list_arr[u].size;
2219
0
            } /* end if */
2220
2221
            /* Go on to the next free list */
2222
0
            gc_arr_node = gc_arr_node->next;
2223
0
        } /* end while */
2224
0
    }     /* end if */
2225
2226
    /* Retrieve the amount of "block" memory used */
2227
0
    if (blk_size) {
2228
0
        H5FL_blk_gc_node_t *gc_blk_node; /* Pointer into the list of things */
2229
2230
        /* Walk through all the free lists */
2231
0
        gc_blk_node = H5FL_blk_gc_head.first;
2232
0
        *blk_size   = 0;
2233
0
        while (gc_blk_node != NULL) {
2234
0
            H5FL_blk_node_t *blk_head; /* Temp. ptr to the free list block node */
2235
2236
            /* Loop through all the nodes in the block free list queue */
2237
0
            blk_head = gc_blk_node->pq->head;
2238
0
            while (blk_head != NULL) {
2239
                /* Add size of blocks on this list */
2240
0
                *blk_size += (blk_head->allocated * blk_head->size);
2241
2242
                /* Get pointer to next node */
2243
0
                blk_head = blk_head->next;
2244
0
            } /* end while */
2245
2246
            /* Go on to the next free list */
2247
0
            gc_blk_node = gc_blk_node->next;
2248
0
        } /* end while */
2249
0
    }     /* end if */
2250
2251
    /* Retrieve the amount of "factory" memory used */
2252
0
    if (fac_size) {
2253
0
        H5FL_fac_gc_node_t *gc_fac_node; /* Pointer into the list of things to garbage collect */
2254
2255
        /* Walk through all the free lists */
2256
0
        gc_fac_node = H5FL_fac_gc_head.first;
2257
0
        *fac_size   = 0;
2258
0
        while (gc_fac_node != NULL) {
2259
0
            H5FL_fac_head_t *fac_head = gc_fac_node->list; /* Head node for factory list */
2260
2261
            /* Add size of blocks on this list */
2262
0
            *fac_size += (fac_head->allocated * fac_head->size);
2263
2264
            /* Go on to the next free list to garbage collect */
2265
0
            gc_fac_node = gc_fac_node->next;
2266
0
        } /* end while */
2267
0
    }     /* end if */
2268
2269
0
    FUNC_LEAVE_NOAPI(SUCCEED)
2270
0
} /* end H5FL_get_free_list_sizes() */