Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sal/rtl/alloc_arena.cxx
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/*
3
 * This file is part of the LibreOffice project.
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * This file incorporates work covered by the following license notice:
10
 *
11
 *   Licensed to the Apache Software Foundation (ASF) under one or more
12
 *   contributor license agreements. See the NOTICE file distributed
13
 *   with this work for additional information regarding copyright
14
 *   ownership. The ASF licenses this file to you under the Apache
15
 *   License, Version 2.0 (the "License"); you may not use this file
16
 *   except in compliance with the License. You may obtain a copy of
17
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18
 */
19
20
#include <sal/config.h>
21
22
#include "alloc_arena.hxx"
23
24
#include "alloc_impl.hxx"
25
#include <rtllifecycle.h>
26
27
#include <cassert>
28
#include <string.h>
29
#include <stdio.h>
30
31
#if defined(SAL_UNX)
32
#include <unistd.h>
33
#endif /* SAL_UNX */
34
35
namespace {
36
37
/**
38
    @internal
39
*/
40
struct rtl_arena_list_st
41
{
42
    rtl_memory_lock_type m_lock;
43
    rtl_arena_type       m_arena_head;
44
};
45
46
}
47
48
static rtl_arena_list_st g_arena_list;
49
50
/**
51
    provided for arena_type allocations, and hash_table resizing.
52
53
    @internal
54
*/
55
static rtl_arena_type * gp_arena_arena = nullptr;
56
57
/**
58
    Low level virtual memory (pseudo) arena
59
    (platform dependent implementation)
60
61
    @internal
62
 */
63
static rtl_arena_type * gp_machdep_arena = nullptr;
64
65
rtl_arena_type * gp_default_arena = nullptr;
66
67
namespace
68
{
69
70
void * rtl_machdep_alloc(
71
    rtl_arena_type * pArena,
72
    sal_Size *       pSize
73
);
74
75
void rtl_machdep_free(
76
    rtl_arena_type * pArena,
77
    void *           pAddr,
78
    sal_Size         nSize
79
);
80
81
sal_Size rtl_machdep_pagesize();
82
83
void rtl_arena_segment_constructor(void * obj)
84
0
{
85
0
    rtl_arena_segment_type * segment = static_cast<rtl_arena_segment_type*>(obj);
86
87
0
    QUEUE_START_NAMED(segment, s);
88
0
    QUEUE_START_NAMED(segment, f);
89
0
}
90
91
void rtl_arena_segment_destructor(void * obj)
92
0
{
93
0
    rtl_arena_segment_type * segment = static_cast< rtl_arena_segment_type * >(
94
0
        obj);
95
0
    assert(QUEUE_STARTED_NAMED(segment, s));
96
0
    assert(QUEUE_STARTED_NAMED(segment, f));
97
0
    (void) segment; // avoid warnings
98
0
}
99
100
/**
101
    @precond  arena->m_lock acquired.
102
 */
103
bool rtl_arena_segment_populate(rtl_arena_type * arena)
104
0
{
105
0
    rtl_arena_segment_type *span;
106
0
    sal_Size                size = rtl_machdep_pagesize();
107
108
0
    span = static_cast< rtl_arena_segment_type * >(
109
0
        rtl_machdep_alloc(gp_machdep_arena, &size));
110
0
    if (span)
111
0
    {
112
0
        rtl_arena_segment_type *first, *last, *head;
113
0
        sal_Size                count = size / sizeof(rtl_arena_segment_type);
114
115
        /* insert onto reserve span list */
116
0
        QUEUE_INSERT_TAIL_NAMED(&(arena->m_segment_reserve_span_head), span, s);
117
0
        QUEUE_START_NAMED(span, f);
118
0
        span->m_addr = reinterpret_cast<sal_uIntPtr>(span);
119
0
        span->m_size = size;
120
0
        span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN;
121
122
        /* insert onto reserve list */
123
0
        head  = &(arena->m_segment_reserve_head);
124
0
        for (first = span + 1, last = span + count; first < last; ++first)
125
0
        {
126
0
            QUEUE_INSERT_TAIL_NAMED(head, first, s);
127
0
            QUEUE_START_NAMED(first, f);
128
0
            first->m_addr = 0;
129
0
            first->m_size = 0;
130
0
            first->m_type = 0;
131
0
        }
132
0
    }
133
0
    return (span != nullptr);
134
0
}
135
136
/**
137
    @precond  arena->m_lock acquired.
138
    @precond  (*ppSegment == 0)
139
*/
140
void rtl_arena_segment_get(
141
    rtl_arena_type * arena,
142
    rtl_arena_segment_type ** ppSegment
143
)
144
0
{
145
0
    rtl_arena_segment_type * head;
146
147
0
    assert(!*ppSegment);
148
149
0
    head = &(arena->m_segment_reserve_head);
150
0
    if (head->m_snext != head || rtl_arena_segment_populate (arena))
151
0
    {
152
0
        (*ppSegment) = head->m_snext;
153
0
        QUEUE_REMOVE_NAMED(*ppSegment, s);
154
0
    }
155
0
}
156
157
/**
158
    @precond  arena->m_lock acquired.
159
    @postcond (*ppSegment == 0)
160
 */
161
void rtl_arena_segment_put(
162
    rtl_arena_type * arena,
163
    rtl_arena_segment_type ** ppSegment
164
)
165
0
{
166
0
    rtl_arena_segment_type * head;
167
168
0
    assert(QUEUE_STARTED_NAMED(*ppSegment, s));
169
0
    assert(QUEUE_STARTED_NAMED(*ppSegment, f));
170
171
0
    (*ppSegment)->m_addr = 0;
172
0
    (*ppSegment)->m_size = 0;
173
174
0
    assert((*ppSegment)->m_type != RTL_ARENA_SEGMENT_TYPE_HEAD);
175
0
    (*ppSegment)->m_type = 0;
176
177
    /* keep as reserve */
178
0
    head = &(arena->m_segment_reserve_head);
179
0
    QUEUE_INSERT_HEAD_NAMED(head, (*ppSegment), s);
180
181
    /* clear */
182
0
    (*ppSegment) = nullptr;
183
0
}
184
185
/**
186
    @precond arena->m_lock acquired.
187
*/
188
void rtl_arena_freelist_insert (
189
    rtl_arena_type * arena,
190
    rtl_arena_segment_type * segment
191
)
192
0
{
193
0
    rtl_arena_segment_type * head;
194
0
    const auto bit = highbit(segment->m_size);
195
0
    assert(bit > 0);
196
0
    head = &(arena->m_freelist_head[bit - 1]);
197
0
    QUEUE_INSERT_TAIL_NAMED(head, segment, f);
198
199
0
    arena->m_freelist_bitmap |= head->m_size;
200
0
}
201
202
/**
203
    @precond arena->m_lock acquired.
204
*/
205
void rtl_arena_freelist_remove(
206
    rtl_arena_type * arena,
207
    rtl_arena_segment_type * segment
208
)
209
0
{
210
0
    if (segment->m_fnext->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD &&
211
0
        segment->m_fprev->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD)
212
0
    {
213
0
        rtl_arena_segment_type * head;
214
215
0
        head = segment->m_fprev;
216
0
        assert(arena->m_freelist_bitmap & head->m_size);
217
0
        arena->m_freelist_bitmap ^= head->m_size;
218
0
    }
219
0
    QUEUE_REMOVE_NAMED(segment, f);
220
0
}
221
222
#define RTL_ARENA_HASH_INDEX_IMPL(a, s, q, m) \
223
0
     ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m))
224
225
#define RTL_ARENA_HASH_INDEX(arena, addr) \
226
0
    RTL_ARENA_HASH_INDEX_IMPL((addr), (arena)->m_hash_shift, (arena)->m_quantum_shift, ((arena)->m_hash_size - 1))
227
228
/**
229
   @precond arena->m_lock released.
230
*/
231
void rtl_arena_hash_rescale(
232
    rtl_arena_type * arena,
233
    sal_Size new_size
234
)
235
0
{
236
0
    assert(new_size != 0);
237
238
0
    rtl_arena_segment_type ** new_table;
239
0
    sal_Size                  new_bytes;
240
241
0
    new_bytes = new_size * sizeof(rtl_arena_segment_type*);
242
0
    new_table = static_cast<rtl_arena_segment_type **>(rtl_arena_alloc (gp_arena_arena, &new_bytes));
243
244
0
    if (new_table)
245
0
    {
246
0
        rtl_arena_segment_type ** old_table;
247
0
        sal_Size                  old_size, i;
248
249
0
        memset (new_table, 0, new_bytes);
250
251
0
        RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
252
253
0
        old_table = arena->m_hash_table;
254
0
        old_size  = arena->m_hash_size;
255
256
0
        arena->m_hash_table = new_table;
257
0
        arena->m_hash_size  = new_size;
258
0
        arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
259
260
0
        for (i = 0; i < old_size; i++)
261
0
        {
262
0
            rtl_arena_segment_type * curr = old_table[i];
263
0
            while (curr)
264
0
            {
265
0
                rtl_arena_segment_type  * next = curr->m_fnext;
266
0
                rtl_arena_segment_type ** head;
267
268
0
                head = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, curr->m_addr)]);
269
0
                curr->m_fnext = (*head);
270
0
                (*head) = curr;
271
272
0
                curr = next;
273
0
            }
274
0
            old_table[i] = nullptr;
275
0
        }
276
277
0
        RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
278
279
0
        if (old_table != arena->m_hash_table_0)
280
0
        {
281
0
            sal_Size old_bytes = old_size * sizeof(rtl_arena_segment_type*);
282
0
            rtl_arena_free (gp_arena_arena, old_table, old_bytes);
283
0
        }
284
0
    }
285
0
}
286
287
/**
288
    Insert arena hash, and update stats.
289
*/
290
void rtl_arena_hash_insert(
291
    rtl_arena_type * arena,
292
    rtl_arena_segment_type * segment
293
)
294
0
{
295
0
    rtl_arena_segment_type ** ppSegment;
296
297
0
    ppSegment = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, segment->m_addr)]);
298
299
0
    segment->m_fnext = (*ppSegment);
300
0
    (*ppSegment) = segment;
301
302
0
    arena->m_stats.m_alloc     += 1;
303
0
    arena->m_stats.m_mem_alloc += segment->m_size;
304
0
}
305
306
/**
307
    Remove arena hash, and update stats.
308
*/
309
rtl_arena_segment_type * rtl_arena_hash_remove(
310
    rtl_arena_type * arena,
311
    sal_uIntPtr addr,
312
    sal_Size size
313
)
314
0
{
315
0
    rtl_arena_segment_type *segment, **segpp;
316
0
    sal_Size lookups = 0;
317
318
0
    segpp = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, addr)]);
319
0
    while ((segment = *segpp))
320
0
    {
321
0
        if (segment->m_addr == addr)
322
0
        {
323
0
            *segpp = segment->m_fnext;
324
0
            segment->m_fnext = segment->m_fprev = segment;
325
0
            break;
326
0
        }
327
328
        /* update lookup miss stats */
329
0
        lookups += 1;
330
0
        segpp = &(segment->m_fnext);
331
0
    }
332
333
0
    assert(segment); // bad free
334
0
    if (segment)
335
0
    {
336
0
        assert(segment->m_size == size);
337
0
        (void) size; // avoid warnings
338
339
0
        arena->m_stats.m_free      += 1;
340
0
        arena->m_stats.m_mem_alloc -= segment->m_size;
341
342
0
        if (lookups > 1)
343
0
        {
344
0
            sal_Size nseg = static_cast<sal_Size>(arena->m_stats.m_alloc - arena->m_stats.m_free);
345
0
            if (nseg > 4 * arena->m_hash_size)
346
0
            {
347
0
                if (!(arena->m_flags & RTL_ARENA_FLAG_RESCALE))
348
0
                {
349
0
                    sal_Size ave = nseg >> arena->m_hash_shift;
350
0
                    assert(ave != 0);
351
0
                    sal_Size new_size = arena->m_hash_size << (highbit(ave) - 1);
352
353
0
                    arena->m_flags |= RTL_ARENA_FLAG_RESCALE;
354
0
                    RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
355
0
                    rtl_arena_hash_rescale (arena, new_size);
356
0
                    RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
357
0
                    arena->m_flags &= ~RTL_ARENA_FLAG_RESCALE;
358
0
                }
359
0
            }
360
0
        }
361
0
    }
362
363
0
    return segment;
364
0
}
365
366
/**
367
    allocate (and remove) segment from freelist
368
369
    @precond arena->m_lock acquired
370
    @precond (*ppSegment == 0)
371
*/
372
bool rtl_arena_segment_alloc(
373
    rtl_arena_type * arena,
374
    sal_Size size,
375
    rtl_arena_segment_type ** ppSegment
376
)
377
0
{
378
0
    int index = 0;
379
380
0
    assert(!*ppSegment);
381
0
    if (!RTL_MEMORY_ISP2(size))
382
0
    {
383
0
        unsigned int msb = highbit(size);
384
0
        if (RTL_ARENA_FREELIST_SIZE == msb)
385
0
        {
386
            /* highest possible freelist: fall back to first fit */
387
0
            rtl_arena_segment_type *head, *segment;
388
389
0
            head = &(arena->m_freelist_head[msb - 1]);
390
0
            for (segment = head->m_fnext; segment != head; segment = segment->m_fnext)
391
0
            {
392
0
                if (segment->m_size >= size)
393
0
                {
394
                    /* allocate first fit segment */
395
0
                    (*ppSegment) = segment;
396
0
                    break;
397
0
                }
398
0
            }
399
0
            goto dequeue_and_leave;
400
0
        }
401
402
        /* roundup to next power of 2 */
403
0
        size = ((sal_Size(1)) << msb);
404
0
    }
405
406
0
    index = lowbit(RTL_MEMORY_P2ALIGN(arena->m_freelist_bitmap, size));
407
0
    if (index > 0)
408
0
    {
409
        /* instant fit: allocate first free segment */
410
0
        rtl_arena_segment_type *head;
411
412
0
        head = &(arena->m_freelist_head[index - 1]);
413
0
        (*ppSegment) = head->m_fnext;
414
0
        assert((*ppSegment) != head);
415
0
    }
416
417
0
dequeue_and_leave:
418
0
    if (*ppSegment)
419
0
    {
420
        /* remove from freelist */
421
0
        rtl_arena_freelist_remove (arena, (*ppSegment));
422
0
    }
423
0
    return (*ppSegment != nullptr);
424
0
}
425
426
/**
427
    import new (span) segment from source arena
428
429
    @precond arena->m_lock acquired
430
    @precond (*ppSegment == 0)
431
*/
432
bool rtl_arena_segment_create(
433
    rtl_arena_type * arena,
434
    sal_Size size,
435
    rtl_arena_segment_type ** ppSegment
436
)
437
0
{
438
0
    assert(!*ppSegment);
439
0
    if (arena->m_source_alloc)
440
0
    {
441
0
        rtl_arena_segment_get (arena, ppSegment);
442
0
        if (*ppSegment)
443
0
        {
444
0
            rtl_arena_segment_type * span = nullptr;
445
0
            rtl_arena_segment_get (arena, &span);
446
0
            if (span)
447
0
            {
448
                /* import new span from source arena */
449
0
                RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
450
451
0
                span->m_size = size;
452
0
                span->m_addr = reinterpret_cast<sal_uIntPtr>(
453
0
                    (arena->m_source_alloc)(
454
0
                        arena->m_source_arena, &(span->m_size)));
455
456
0
                RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
457
0
                if (span->m_addr != 0)
458
0
                {
459
                    /* insert onto segment list, update stats */
460
0
                    span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN;
461
0
                    QUEUE_INSERT_HEAD_NAMED(&(arena->m_segment_head), span, s);
462
0
                    arena->m_stats.m_mem_total += span->m_size;
463
464
0
                    (*ppSegment)->m_addr = span->m_addr;
465
0
                    (*ppSegment)->m_size = span->m_size;
466
0
                    (*ppSegment)->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
467
0
                    QUEUE_INSERT_HEAD_NAMED(span, (*ppSegment), s);
468
469
                    /* report success */
470
0
                    return true;
471
0
                }
472
0
                rtl_arena_segment_put (arena, &span);
473
0
            }
474
0
            rtl_arena_segment_put (arena, ppSegment);
475
0
        }
476
0
    }
477
0
    return false; // failure
478
0
}
479
480
/**
481
    mark as free and join with adjacent free segment(s)
482
483
    @precond arena->m_lock acquired
484
    @precond segment marked 'used'
485
*/
486
void rtl_arena_segment_coalesce(
487
    rtl_arena_type * arena,
488
    rtl_arena_segment_type * segment
489
)
490
0
{
491
0
    rtl_arena_segment_type *next, *prev;
492
493
    /* mark segment free */
494
0
    assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_USED);
495
0
    segment->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
496
497
    /* try to merge w/ next segment */
498
0
    next = segment->m_snext;
499
0
    if (next->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
500
0
    {
501
0
        assert(segment->m_addr + segment->m_size == next->m_addr);
502
0
        segment->m_size += next->m_size;
503
504
        /* remove from freelist */
505
0
        rtl_arena_freelist_remove (arena, next);
506
507
        /* remove from segment list */
508
0
        QUEUE_REMOVE_NAMED(next, s);
509
510
        /* release segment descriptor */
511
0
        rtl_arena_segment_put (arena, &next);
512
0
    }
513
514
    /* try to merge w/ prev segment */
515
0
    prev = segment->m_sprev;
516
0
    if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
517
0
    {
518
0
        assert(prev->m_addr + prev->m_size == segment->m_addr);
519
0
        segment->m_addr  = prev->m_addr;
520
0
        segment->m_size += prev->m_size;
521
522
        /* remove from freelist */
523
0
        rtl_arena_freelist_remove (arena, prev);
524
525
        /* remove from segment list */
526
0
        QUEUE_REMOVE_NAMED(prev, s);
527
528
        /* release segment descriptor */
529
0
        rtl_arena_segment_put (arena, &prev);
530
0
    }
531
0
}
532
533
void rtl_arena_constructor(void * obj)
534
0
{
535
0
    rtl_arena_type * arena = static_cast<rtl_arena_type*>(obj);
536
0
    rtl_arena_segment_type * head;
537
0
    size_t i;
538
539
0
    memset (arena, 0, sizeof(rtl_arena_type));
540
541
0
    QUEUE_START_NAMED(arena, arena_);
542
543
0
    RTL_MEMORY_LOCK_INIT(&(arena->m_lock));
544
545
0
    head = &(arena->m_segment_reserve_span_head);
546
0
    rtl_arena_segment_constructor (head);
547
0
    head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
548
549
0
    head = &(arena->m_segment_reserve_head);
550
0
    rtl_arena_segment_constructor (head);
551
0
    head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
552
553
0
    head = &(arena->m_segment_head);
554
0
    rtl_arena_segment_constructor (head);
555
0
    head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
556
557
0
    for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++)
558
0
    {
559
0
        head = &(arena->m_freelist_head[i]);
560
0
        rtl_arena_segment_constructor (head);
561
562
0
        head->m_size = ((sal_Size(1)) << i);
563
0
        head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
564
0
    }
565
566
0
    arena->m_hash_table = arena->m_hash_table_0;
567
0
    arena->m_hash_size  = RTL_ARENA_HASH_SIZE;
568
0
    arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
569
0
}
570
571
void rtl_arena_destructor(void * obj)
572
0
{
573
0
    rtl_arena_type * arena = static_cast<rtl_arena_type*>(obj);
574
0
    rtl_arena_segment_type * head;
575
0
    size_t i;
576
577
0
    assert(QUEUE_STARTED_NAMED(arena, arena_));
578
579
0
    RTL_MEMORY_LOCK_DESTROY(&(arena->m_lock));
580
581
0
    head = &(arena->m_segment_reserve_span_head);
582
0
    assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
583
0
    rtl_arena_segment_destructor (head);
584
585
0
    head = &(arena->m_segment_reserve_head);
586
0
    assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
587
0
    rtl_arena_segment_destructor (head);
588
589
0
    head = &(arena->m_segment_head);
590
0
    assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
591
0
    rtl_arena_segment_destructor (head);
592
593
0
    for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++)
594
0
    {
595
0
        head = &(arena->m_freelist_head[i]);
596
597
0
        assert(head->m_size == ((sal_Size(1)) << i));
598
0
        assert(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
599
600
0
        rtl_arena_segment_destructor (head);
601
0
    }
602
603
0
    assert(arena->m_hash_table == arena->m_hash_table_0);
604
0
    assert(arena->m_hash_size  == RTL_ARENA_HASH_SIZE);
605
0
    assert(arena->m_hash_shift == highbit(arena->m_hash_size) - 1);
606
0
}
607
608
rtl_arena_type * rtl_arena_activate(
609
    rtl_arena_type * arena,
610
    const char *     name,
611
    sal_Size         quantum,
612
    rtl_arena_type * source_arena,
613
    void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *),
614
    void   (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size)
615
)
616
0
{
617
0
    assert(arena);
618
0
    if (arena)
619
0
    {
620
0
        (void) snprintf (arena->m_name, sizeof(arena->m_name), "%s", name);
621
622
0
        if (!RTL_MEMORY_ISP2(quantum))
623
0
        {
624
            /* roundup to next power of 2 */
625
0
            quantum = ((sal_Size(1)) << highbit(quantum));
626
0
        }
627
628
0
        arena->m_quantum = quantum;
629
0
        arena->m_quantum_shift = highbit(arena->m_quantum) - 1;
630
631
0
        arena->m_source_arena = source_arena;
632
0
        arena->m_source_alloc = source_alloc;
633
0
        arena->m_source_free  = source_free;
634
635
        /* insert into arena list */
636
0
        RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
637
0
        QUEUE_INSERT_TAIL_NAMED(&(g_arena_list.m_arena_head), arena, arena_);
638
0
        RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
639
0
    }
640
0
    return arena;
641
0
}
642
643
void rtl_arena_deactivate(rtl_arena_type * arena)
644
0
{
645
0
    rtl_arena_segment_type * head, * segment;
646
647
    /* remove from arena list */
648
0
    RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
649
0
    QUEUE_REMOVE_NAMED(arena, arena_);
650
0
    RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
651
652
    /* check for leaked segments */
653
0
    if (arena->m_stats.m_alloc > arena->m_stats.m_free)
654
0
    {
655
0
        sal_Size i, n;
656
657
        /* cleanup still used segment(s) */
658
0
        for (i = 0, n = arena->m_hash_size; i < n; i++)
659
0
        {
660
0
            while ((segment = arena->m_hash_table[i]))
661
0
            {
662
                /* pop from hash table */
663
0
                arena->m_hash_table[i] = segment->m_fnext;
664
0
                segment->m_fnext = segment->m_fprev = segment;
665
666
                /* coalesce w/ adjacent free segment(s) */
667
0
                rtl_arena_segment_coalesce (arena, segment);
668
669
                /* insert onto freelist */
670
0
                rtl_arena_freelist_insert (arena, segment);
671
0
            }
672
0
        }
673
0
    }
674
675
    /* cleanup hash table */
676
0
    if (arena->m_hash_table != arena->m_hash_table_0)
677
0
    {
678
0
        rtl_arena_free(
679
0
            gp_arena_arena,
680
0
            arena->m_hash_table,
681
0
            arena->m_hash_size * sizeof(rtl_arena_segment_type*));
682
683
0
        arena->m_hash_table = arena->m_hash_table_0;
684
0
        arena->m_hash_size = RTL_ARENA_HASH_SIZE;
685
0
        arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
686
0
    }
687
688
    /* cleanup segment list */
689
0
    head = &(arena->m_segment_head);
690
0
    for (segment = head->m_snext; segment != head; segment = head->m_snext)
691
0
    {
692
0
        if (segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
693
0
        {
694
            /* remove from freelist */
695
0
            rtl_arena_freelist_remove (arena, segment);
696
0
        }
697
0
        else
698
0
        {
699
            /* can have only free and span segments here */
700
0
            assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN);
701
0
        }
702
703
        /* remove from segment list */
704
0
        QUEUE_REMOVE_NAMED(segment, s);
705
706
        /* release segment descriptor */
707
0
        rtl_arena_segment_put (arena, &segment);
708
0
    }
709
710
    /* cleanup segment reserve list */
711
0
    head = &(arena->m_segment_reserve_head);
712
0
    for (segment = head->m_snext; segment != head; segment = head->m_snext)
713
0
    {
714
        /* remove from segment list */
715
0
        QUEUE_REMOVE_NAMED(segment, s);
716
0
    }
717
718
    /* cleanup segment reserve span(s) */
719
0
    head = &(arena->m_segment_reserve_span_head);
720
0
    for (segment = head->m_snext; segment != head; segment = head->m_snext)
721
0
    {
722
        /* can have only span segments here */
723
0
        assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN);
724
725
        /* remove from segment list */
726
0
        QUEUE_REMOVE_NAMED(segment, s);
727
728
        /* return span to g_machdep_arena */
729
0
        rtl_machdep_free (gp_machdep_arena, reinterpret_cast<void*>(segment->m_addr), segment->m_size);
730
0
    }
731
0
}
732
733
} // namespace
734
735
rtl_arena_type * SAL_CALL rtl_arena_create(
736
    const char *       name,
737
    sal_Size           quantum,
738
    sal_Size,
739
    rtl_arena_type *   source_arena,
740
    void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *),
741
    void   (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size),
742
    SAL_UNUSED_PARAMETER int
743
) noexcept
744
0
{
745
0
    rtl_arena_type * result = nullptr;
746
0
    sal_Size         size   = sizeof(rtl_arena_type);
747
748
0
try_alloc:
749
0
    result = static_cast<rtl_arena_type*>(rtl_arena_alloc (gp_arena_arena, &size));
750
0
    if (result)
751
0
    {
752
0
        rtl_arena_type * arena = result;
753
0
        rtl_arena_constructor (arena);
754
755
0
        if (!source_arena)
756
0
        {
757
0
            assert(gp_default_arena);
758
0
            source_arena = gp_default_arena;
759
0
        }
760
761
0
        result = rtl_arena_activate (
762
0
            arena,
763
0
            name,
764
0
            quantum,
765
0
            source_arena,
766
0
            source_alloc,
767
0
            source_free
768
0
        );
769
770
0
        if (!result)
771
0
        {
772
0
            rtl_arena_deactivate (arena);
773
0
            rtl_arena_destructor (arena);
774
0
            rtl_arena_free (gp_arena_arena, arena, size);
775
0
        }
776
0
    }
777
0
    else if (!gp_arena_arena)
778
0
    {
779
0
        ensureArenaSingleton();
780
0
        if (gp_arena_arena)
781
0
        {
782
            /* try again */
783
0
            goto try_alloc;
784
0
        }
785
0
    }
786
0
    return result;
787
0
}
788
789
void SAL_CALL rtl_arena_destroy(rtl_arena_type * arena) noexcept
790
0
{
791
0
    if (arena)
792
0
    {
793
0
        rtl_arena_deactivate (arena);
794
0
        rtl_arena_destructor (arena);
795
0
        rtl_arena_free (gp_arena_arena, arena, sizeof(rtl_arena_type));
796
0
    }
797
0
}
798
799
void * SAL_CALL rtl_arena_alloc(
800
    rtl_arena_type * arena,
801
    sal_Size *       pSize
802
) noexcept
803
0
{
804
0
    void * addr = nullptr;
805
806
0
    if (arena && pSize)
807
0
    {
808
0
        sal_Size size;
809
810
0
        size = RTL_MEMORY_ALIGN(*pSize, arena->m_quantum);
811
0
        if (size > 0)
812
0
        {
813
            /* allocate from segment list */
814
0
            rtl_arena_segment_type *segment = nullptr;
815
816
0
            RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
817
0
            if (rtl_arena_segment_alloc (arena, size, &segment) ||
818
0
                rtl_arena_segment_create(arena, size, &segment)    )
819
0
            {
820
                /* shrink to fit */
821
0
                sal_Size oversize;
822
823
                /* mark segment used */
824
0
                assert(segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE);
825
0
                segment->m_type = RTL_ARENA_SEGMENT_TYPE_USED;
826
827
                /* resize */
828
0
                assert(segment->m_size >= size);
829
0
                oversize = segment->m_size - size;
830
0
                if (oversize >= arena->m_quantum)
831
0
                {
832
0
                    rtl_arena_segment_type * remainder = nullptr;
833
0
                    rtl_arena_segment_get (arena, &remainder);
834
0
                    if (remainder)
835
0
                    {
836
0
                        segment->m_size = size;
837
838
0
                        remainder->m_addr = segment->m_addr + segment->m_size;
839
0
                        remainder->m_size = oversize;
840
0
                        remainder->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
841
0
                        QUEUE_INSERT_HEAD_NAMED(segment, remainder, s);
842
843
0
                        rtl_arena_freelist_insert (arena, remainder);
844
0
                    }
845
0
                }
846
847
0
                rtl_arena_hash_insert (arena, segment);
848
849
0
                (*pSize) = segment->m_size;
850
0
                addr = reinterpret_cast<void*>(segment->m_addr);
851
0
            }
852
0
            RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
853
0
        }
854
0
    }
855
0
    return addr;
856
0
}
857
858
void SAL_CALL rtl_arena_free (
859
    rtl_arena_type * arena,
860
    void *           addr,
861
    sal_Size         size
862
) noexcept
863
0
{
864
0
    if (arena)
865
0
    {
866
0
        size = RTL_MEMORY_ALIGN(size, arena->m_quantum);
867
0
        if (size > 0)
868
0
        {
869
            /* free to segment list */
870
0
            rtl_arena_segment_type * segment;
871
872
0
            RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
873
874
0
            segment = rtl_arena_hash_remove (arena, reinterpret_cast<sal_uIntPtr>(addr), size);
875
0
            if (segment)
876
0
            {
877
0
                rtl_arena_segment_type *next, *prev;
878
879
                /* coalesce w/ adjacent free segment(s) */
880
0
                rtl_arena_segment_coalesce (arena, segment);
881
882
                /* determine (new) next and prev segment */
883
0
                next = segment->m_snext;
884
0
                prev = segment->m_sprev;
885
886
                /* entire span free when prev is a span, and next is either a span or a list head */
887
0
                if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN &&
888
0
                    ((next->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN)  ||
889
0
                     (next->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD)))
890
0
                {
891
0
                    assert(
892
0
                        prev->m_addr == segment->m_addr
893
0
                        && prev->m_size == segment->m_size);
894
895
0
                    if (arena->m_source_free)
896
0
                    {
897
0
                        addr = reinterpret_cast<void*>(prev->m_addr);
898
0
                        size = prev->m_size;
899
900
                        /* remove from segment list */
901
0
                        QUEUE_REMOVE_NAMED(segment, s);
902
903
                        /* release segment descriptor */
904
0
                        rtl_arena_segment_put (arena, &segment);
905
906
                        /* remove from segment list */
907
0
                        QUEUE_REMOVE_NAMED(prev, s);
908
909
                        /* release (span) segment descriptor */
910
0
                        rtl_arena_segment_put (arena, &prev);
911
912
                        /* update stats, return span to source arena */
913
0
                        arena->m_stats.m_mem_total -= size;
914
0
                        RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
915
916
0
                        (arena->m_source_free)(arena->m_source_arena, addr, size);
917
0
                        return;
918
0
                    }
919
0
                }
920
921
                /* insert onto freelist */
922
0
                rtl_arena_freelist_insert (arena, segment);
923
0
            }
924
925
0
            RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
926
0
        }
927
0
    }
928
0
}
929
930
void rtl_arena_foreach (rtl_arena_type *arena, ArenaForeachFn foreachFn)
931
0
{
932
    /* used segments */
933
0
    for (int i = 0, n = arena->m_hash_size; i < n; i++)
934
0
    {
935
0
        for (rtl_arena_segment_type *segment = arena->m_hash_table[i];
936
0
             segment != nullptr; segment = segment->m_fnext)
937
0
        {
938
0
            foreachFn(reinterpret_cast<void *>(segment->m_addr),
939
0
                      segment->m_size);
940
0
        }
941
0
    }
942
0
}
943
944
#if defined(SAL_UNX)
945
#include <sys/mman.h>
946
#elif defined(_WIN32)
947
#define MAP_FAILED nullptr
948
#endif /* SAL_UNX || _WIN32 */
949
950
namespace
951
{
952
953
void * rtl_machdep_alloc(
954
    rtl_arena_type * pArena,
955
    sal_Size *       pSize
956
)
957
0
{
958
0
    void *   addr;
959
0
    sal_Size size = *pSize;
960
961
0
    assert(pArena == gp_machdep_arena);
962
963
#if defined(__sun) && defined(SPARC)
964
    /* see @ mmap(2) man pages */
965
    size += (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */
966
    if (size > (4 << 20))
967
        size = RTL_MEMORY_P2ROUNDUP(size, (4 << 20));
968
    else if (size > (512 << 10))
969
        size = RTL_MEMORY_P2ROUNDUP(size, (512 << 10));
970
    else
971
        size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10));
972
    size -= (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */
973
#else
974
    /* default allocation granularity */
975
0
    if (pArena->m_quantum < (64 << 10))
976
0
    {
977
0
        size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10));
978
0
    }
979
0
    else
980
0
    {
981
0
        size = RTL_MEMORY_P2ROUNDUP(size, pArena->m_quantum);
982
0
    }
983
0
#endif
984
985
0
#if defined(SAL_UNX)
986
0
    addr = mmap (nullptr, static_cast<size_t>(size), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
987
#elif defined(_WIN32)
988
    addr = VirtualAlloc (nullptr, static_cast<SIZE_T>(size), MEM_COMMIT, PAGE_READWRITE);
989
#endif /* (SAL_UNX || _WIN32) */
990
991
0
    if (addr != MAP_FAILED)
992
0
    {
993
0
        pArena->m_stats.m_alloc += 1;
994
0
        pArena->m_stats.m_mem_total += size;
995
0
        pArena->m_stats.m_mem_alloc += size;
996
997
0
        (*pSize) = size;
998
0
        return addr;
999
0
    }
1000
0
    return nullptr;
1001
0
}
1002
1003
void rtl_machdep_free(
1004
    rtl_arena_type * pArena,
1005
    void *           pAddr,
1006
    sal_Size         nSize
1007
)
1008
0
{
1009
0
    assert(pArena == gp_machdep_arena);
1010
1011
0
    pArena->m_stats.m_free += 1;
1012
0
    pArena->m_stats.m_mem_total -= nSize;
1013
0
    pArena->m_stats.m_mem_alloc -= nSize;
1014
1015
0
#if defined(SAL_UNX)
1016
0
    (void) munmap(pAddr, nSize);
1017
#elif defined(_WIN32)
1018
    (void) VirtualFree (pAddr, SIZE_T(0), MEM_RELEASE);
1019
#endif /* (SAL_UNX || _WIN32) */
1020
0
}
1021
1022
sal_Size rtl_machdep_pagesize()
1023
0
{
1024
0
#if defined(SAL_UNX)
1025
#if defined(FREEBSD) || defined(NETBSD) || defined(DRAGONFLY)
1026
    return (sal_Size)getpagesize();
1027
#else  /* POSIX */
1028
0
    return static_cast<sal_Size>(sysconf(_SC_PAGESIZE));
1029
0
#endif /* xBSD || POSIX */
1030
#elif defined(_WIN32)
1031
    SYSTEM_INFO info;
1032
    GetSystemInfo (&info);
1033
    return static_cast<sal_Size>(info.dwPageSize);
1034
#endif /* (SAL_UNX || _WIN32) */
1035
0
}
1036
1037
} //namespace
1038
1039
void rtl_arena_init()
1040
0
{
1041
0
    {
1042
        /* list of arenas */
1043
0
        RTL_MEMORY_LOCK_INIT(&(g_arena_list.m_lock));
1044
0
        rtl_arena_constructor (&(g_arena_list.m_arena_head));
1045
0
    }
1046
0
    {
1047
        /* machdep (pseudo) arena */
1048
0
        static rtl_arena_type g_machdep_arena;
1049
1050
0
        assert(!gp_machdep_arena);
1051
0
        rtl_arena_constructor (&g_machdep_arena);
1052
1053
0
        gp_machdep_arena = rtl_arena_activate (
1054
0
            &g_machdep_arena,
1055
0
            "rtl_machdep_arena",
1056
0
            rtl_machdep_pagesize(),
1057
0
            nullptr, nullptr, nullptr  /* no source */
1058
0
        );
1059
0
        assert(gp_machdep_arena);
1060
0
    }
1061
0
    {
1062
        /* default arena */
1063
0
        static rtl_arena_type g_default_arena;
1064
1065
0
        assert(!gp_default_arena);
1066
0
        rtl_arena_constructor (&g_default_arena);
1067
1068
0
        gp_default_arena = rtl_arena_activate (
1069
0
            &g_default_arena,
1070
0
            "rtl_default_arena",
1071
0
            rtl_machdep_pagesize(),
1072
0
            gp_machdep_arena,  /* source */
1073
0
            rtl_machdep_alloc,
1074
0
            rtl_machdep_free
1075
0
        );
1076
0
        assert(gp_default_arena);
1077
0
    }
1078
0
    {
1079
        /* arena internal arena */
1080
0
        static rtl_arena_type g_arena_arena;
1081
1082
0
        assert(!gp_arena_arena);
1083
0
        rtl_arena_constructor (&g_arena_arena);
1084
1085
0
        gp_arena_arena = rtl_arena_activate(
1086
0
            &g_arena_arena,
1087
0
            "rtl_arena_internal_arena",
1088
0
            64,                /* quantum */
1089
0
            gp_default_arena,  /* source */
1090
0
            rtl_arena_alloc,
1091
0
            rtl_arena_free
1092
0
        );
1093
0
        assert(gp_arena_arena);
1094
0
    }
1095
0
}
1096
1097
void rtl_arena_fini()
1098
0
{
1099
0
    if (gp_arena_arena)
1100
0
    {
1101
0
        rtl_arena_type * arena, * head;
1102
1103
0
        RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
1104
0
        head = &(g_arena_list.m_arena_head);
1105
1106
0
        for (arena = head->m_arena_next; arena != head; arena = arena->m_arena_next)
1107
0
        {
1108
            // noop
1109
0
        }
1110
0
        RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
1111
0
    }
1112
0
}
1113
1114
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */