Coverage Report

Created: 2026-06-07 06:10

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/glslang/glslang/MachineIndependent/PoolAlloc.cpp
Line
Count
Source
1
//
2
// Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
3
// All rights reserved.
4
//
5
// Redistribution and use in source and binary forms, with or without
6
// modification, are permitted provided that the following conditions
7
// are met:
8
//
9
//    Redistributions of source code must retain the above copyright
10
//    notice, this list of conditions and the following disclaimer.
11
//
12
//    Redistributions in binary form must reproduce the above
13
//    copyright notice, this list of conditions and the following
14
//    disclaimer in the documentation and/or other materials provided
15
//    with the distribution.
16
//
17
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
18
//    contributors may be used to endorse or promote products derived
19
//    from this software without specific prior written permission.
20
//
21
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25
// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27
// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
// POSSIBILITY OF SUCH DAMAGE.
33
//
34
35
#include "../Include/Common.h"
36
#include "../Include/PoolAlloc.h"
37
38
// Mostly here for target that do not support threads such as WASI.
39
#ifdef DISABLE_THREAD_SUPPORT
40
#define THREAD_LOCAL 
41
#else
42
0
#define THREAD_LOCAL thread_local
43
#endif
44
45
namespace glslang {
46
47
namespace {
48
THREAD_LOCAL TPoolAllocator* threadPoolAllocator = nullptr;
49
50
TPoolAllocator* GetDefaultThreadPoolAllocator()
51
0
{
52
0
    THREAD_LOCAL TPoolAllocator defaultAllocator;
53
0
    return &defaultAllocator;
54
0
}
55
} // anonymous namespace
56
57
// Return the thread-specific current pool.
58
TPoolAllocator& GetThreadPoolAllocator()
59
11.8M
{
60
11.8M
    return *(threadPoolAllocator ? threadPoolAllocator : GetDefaultThreadPoolAllocator());
61
11.8M
}
62
63
// Set the thread-specific current pool.
64
void SetThreadPoolAllocator(TPoolAllocator* poolAllocator)
65
395
{
66
395
    threadPoolAllocator = poolAllocator;
67
395
}
68
69
//
70
// Implement the functionality of the TPoolAllocator class, which
71
// is documented in PoolAlloc.h.
72
//
73
TPoolAllocator::TPoolAllocator(int growthIncrement, int allocationAlignment) :
74
396
    pageSize(growthIncrement),
75
396
    alignment(allocationAlignment),
76
396
    freeList(nullptr),
77
396
    inUseList(nullptr),
78
396
    numCalls(0)
79
396
{
80
    //
81
    // Don't allow page sizes we know are smaller than all common
82
    // OS page sizes.
83
    //
84
396
    if (pageSize < 4*1024)
85
0
        pageSize = 4*1024;
86
87
    //
88
    // A large currentPageOffset indicates a new page needs to
89
    // be obtained to allocate memory.
90
    //
91
396
    currentPageOffset = pageSize;
92
93
    //
94
    // Adjust alignment to be at least pointer aligned and
95
    // power of 2.
96
    //
97
396
    size_t minAlign = sizeof(void*);
98
396
    alignment &= ~(minAlign - 1);
99
396
    if (alignment < minAlign)
100
0
        alignment = minAlign;
101
396
    size_t a = 1;
102
1.98k
    while (a < alignment)
103
1.58k
        a <<= 1;
104
396
    alignment = a;
105
396
    alignmentMask = a - 1;
106
107
    //
108
    // Align header skip
109
    //
110
396
    headerSkip = minAlign;
111
396
    if (headerSkip < sizeof(tHeader)) {
112
396
        headerSkip = (sizeof(tHeader) + alignmentMask) & ~alignmentMask;
113
396
    }
114
115
396
    push();
116
396
}
117
118
TPoolAllocator::~TPoolAllocator()
119
396
{
120
71.9k
    while (inUseList) {
121
71.5k
        tHeader* next = inUseList->nextPage;
122
71.5k
        inUseList->~tHeader();
123
71.5k
        delete [] reinterpret_cast<char*>(inUseList);
124
71.5k
        inUseList = next;
125
71.5k
    }
126
127
    //
128
    // Always delete the free list memory - it can't be being
129
    // (correctly) referenced, whether the pool allocator was
130
    // global or not.  We should not check the guard blocks
131
    // here, because we did it already when the block was
132
    // placed into the free list.
133
    //
134
396
    while (freeList) {
135
0
        tHeader* next = freeList->nextPage;
136
0
        delete [] reinterpret_cast<char*>(freeList);
137
0
        freeList = next;
138
0
    }
139
396
}
140
141
//
142
// Check a single guard block for damage
143
//
144
#ifdef GUARD_BLOCKS
145
void TAllocation::checkGuardBlock(unsigned char* blockMem, unsigned char val, const char* locText) const
146
#else
147
void TAllocation::checkGuardBlock(unsigned char*, unsigned char, const char*) const
148
#endif
149
0
{
150
#ifdef GUARD_BLOCKS
151
    for (size_t x = 0; x < guardBlockSize(); x++) {
152
        if (blockMem[x] != val) {
153
            const int maxSize = 80;
154
            char assertMsg[maxSize];
155
156
            // We don't print the assert message.  It's here just to be helpful.
157
            snprintf(assertMsg, maxSize, "PoolAlloc: Damage %s %zu byte allocation at 0x%p\n",
158
                      locText, size, data());
159
            assert(0 && "PoolAlloc: Damage in guard block");
160
        }
161
    }
162
#else
163
0
    assert(guardBlockSize() == 0);
164
0
#endif
165
0
}
166
167
void TPoolAllocator::push()
168
495
{
169
495
    tAllocState state = { currentPageOffset, inUseList };
170
171
495
    stack.push_back(state);
172
173
    //
174
    // Indicate there is no current page to allocate from.
175
    //
176
495
    currentPageOffset = pageSize;
177
495
}
178
179
//
180
// Do a mass-deallocation of all the individual allocations
181
// that have occurred since the last push(), or since the
182
// last pop(), or since the object's creation.
183
//
184
// The deallocated pages are saved for future allocations.
185
//
186
void TPoolAllocator::pop()
187
0
{
188
0
    if (stack.size() < 1)
189
0
        return;
190
191
0
    tHeader* page = stack.back().page;
192
0
    currentPageOffset = stack.back().offset;
193
194
0
    while (inUseList != page) {
195
0
        tHeader* nextInUse = inUseList->nextPage;
196
0
        size_t pageCount = inUseList->pageCount;
197
198
        // This technically ends the lifetime of the header as C++ object,
199
        // but we will still control the memory and reuse it.
200
0
        inUseList->~tHeader(); // currently, just a debug allocation checker
201
202
0
        if (pageCount > 1) {
203
0
            delete [] reinterpret_cast<char*>(inUseList);
204
0
        } else {
205
0
            inUseList->nextPage = freeList;
206
0
            freeList = inUseList;
207
0
        }
208
0
        inUseList = nextInUse;
209
0
    }
210
211
0
    stack.pop_back();
212
0
}
213
214
//
215
// Do a mass-deallocation of all the individual allocations
216
// that have occurred.
217
//
218
void TPoolAllocator::popAll()
219
0
{
220
0
    while (stack.size() > 0)
221
0
        pop();
222
0
}
223
224
void* TPoolAllocator::allocate(size_t numBytes)
225
5.17M
{
226
    // If we are using guard blocks, all allocations are bracketed by
227
    // them: [guardblock][allocation][guardblock].  numBytes is how
228
    // much memory the caller asked for.  allocationSize is the total
229
    // size including guard blocks.  In release build,
230
    // guardBlockSize=0 and this all gets optimized away.
231
5.17M
    size_t allocationSize = TAllocation::allocationSize(numBytes);
232
233
    //
234
    // Just keep some interesting statistics.
235
    //
236
5.17M
    ++numCalls;
237
5.17M
    totalBytes += numBytes;
238
239
    //
240
    // Do the allocation, most likely case first, for efficiency.
241
    // This step could be moved to be inline sometime.
242
    //
243
5.17M
    if (currentPageOffset + allocationSize <= pageSize) {
244
        //
245
        // Safe to allocate from currentPageOffset.
246
        //
247
5.10M
        unsigned char* memory = reinterpret_cast<unsigned char*>(inUseList) + currentPageOffset;
248
5.10M
        currentPageOffset += allocationSize;
249
5.10M
        currentPageOffset = (currentPageOffset + alignmentMask) & ~alignmentMask;
250
251
5.10M
        return initializeAllocation(inUseList, memory, numBytes);
252
5.10M
    }
253
254
71.5k
    if (allocationSize + headerSkip > pageSize) {
255
        //
256
        // Do a multi-page allocation.  Don't mix these with the others.
257
        // The OS is efficient and allocating and free-ing multiple pages.
258
        //
259
343
        size_t numBytesToAlloc = allocationSize + headerSkip;
260
343
        tHeader* memory = reinterpret_cast<tHeader*>(::new char[numBytesToAlloc]);
261
343
        if (memory == nullptr)
262
0
            return nullptr;
263
264
        // Use placement-new to initialize header
265
343
        new(memory) tHeader(inUseList, (numBytesToAlloc + pageSize - 1) / pageSize);
266
343
        inUseList = memory;
267
268
343
        currentPageOffset = pageSize;  // make next allocation come from a new page
269
270
        // No guard blocks for multi-page allocations (yet)
271
343
        return reinterpret_cast<void*>(reinterpret_cast<UINT_PTR>(memory) + headerSkip);
272
343
    }
273
274
    //
275
    // Need a simple page to allocate from.
276
    //
277
71.2k
    tHeader* memory;
278
71.2k
    if (freeList) {
279
0
        memory = freeList;
280
0
        freeList = freeList->nextPage;
281
71.2k
    } else {
282
71.2k
        memory = reinterpret_cast<tHeader*>(::new char[pageSize]);
283
71.2k
        if (memory == nullptr)
284
0
            return nullptr;
285
71.2k
    }
286
287
    // Use placement-new to initialize header
288
71.2k
    new(memory) tHeader(inUseList, 1);
289
71.2k
    inUseList = memory;
290
291
71.2k
    unsigned char* ret = reinterpret_cast<unsigned char*>(inUseList) + headerSkip;
292
71.2k
    currentPageOffset = (headerSkip + allocationSize + alignmentMask) & ~alignmentMask;
293
294
71.2k
    return initializeAllocation(inUseList, ret, numBytes);
295
71.2k
}
296
297
//
298
// Check all allocations in a list for damage by calling check on each.
299
//
300
void TAllocation::checkAllocList() const
301
0
{
302
0
    for (const TAllocation* alloc = this; alloc != nullptr; alloc = alloc->prevAlloc)
303
0
        alloc->check();
304
0
}
305
306
} // end namespace glslang