Stroika Library
3.0d16
Help-Home
Loading...
Searching...
No Matches
BlockAllocator.h
Go to the documentation of this file.
1
/*
2
* Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3
*/
4
#ifndef _Stroika_Foundation_Memory_BlockAllocator_h_
5
#define _Stroika_Foundation_Memory_BlockAllocator_h_ 1
6
7
#include "Stroika/Foundation/StroikaPreComp.h"
8
9
#include <cstddef>
10
11
#include "Stroika/Foundation/Common/Common.h"
12
#include "
Stroika/Foundation/Debug/Assertions.h
"
13
14
/**
15
* \file
16
*
17
* \note Code-Status: <a href="Code-Status.md#Beta">Beta</a>
18
*
19
*
20
* @todo Consider doing GetMem_Util_ code outside of the context of the lock-guard, and if
21
* we get multiple results, just patch them into the linked list. That way in case of
22
* multithreading (when we're paging in freepool) - we'll do less busy waiting. But, this would
23
* be at the cost of possibly allocating too many blocks when two threads both run out of memory
24
* in the same pool at the same time - though that could be undone).
25
*
26
* @todo Compact() method could be smart enough to move items in free pool that are from
27
* mostly used up pools to the head of the free list, and move mostly unused blocks
28
* to the end of the free pool, which over time should probably tend to defragment
29
* the pools.
30
*
31
* @todo BlockAllocator<T> could hugely benefit from some optimistic locking
32
* strategy - like in http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3341.pdf
33
*
34
* Make sure when we experiment with that - we include it here as one of our first
35
* optimization points!
36
*
37
* There is now a C++ semi-standard for this, and working implementations (I haven't tried). But my current
38
* lockfree approach maybe better.
39
*
40
* @todo Maybe use TRAITS on BlockAllocator to define some stuff
41
* about strategies (options) - like how to share pools?
42
*
43
* @todo BlockAllocator<>::Compact ()
44
* o not sure this is useful, or worth the effort, but since
45
* Sterl wrote it - leave it in for a while - til I get clearer experience.
46
*
47
* o BlockAllocationPool_<SIZE>::Compact () algorithm uses vector, which could fail if
48
* we are so fragmented we cannot allocate the large contiguous block needed for the vector<>.
49
* Catch-22. Maybe find a less costly strategy?
50
*
51
* o I make have broken something in Compact() routine when I transcribed it from Sterl's code.
52
* never tested (by me).
53
*/
54
55
namespace
Stroika::Foundation::Memory {
56
57
/**
58
* Low-level tool to allocate and free memory from a fixed size/element pool. Very high performance since
59
* no searching or coalescing ever needed, but at the cost of creating some amount of fragmentation.
60
*
61
* \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
62
*
63
* This API operates at the level of malloc/free - just allocating fixed sized blocks and freeing them.
64
*
65
* For easiser use, probably the best approach is to @see UseBlockAllocationIfAppropriate
66
*
67
* \note Design Note: alignas / alignemnt of allocated values
68
* http://stroika-bugs.sophists.com/browse/STK-511
69
* We use sizeof(). We always allocate large blocks which (I htink are always) aligned to the largest
70
* alignemnt required by the system, and we that as an array.
71
*
72
* But I think since sizeof(T) is the offset from one element of an array[T] - our allocations will always be aligned
73
* if the first big block is aligned.
74
*
75
* To double/triple check, we have an Ensure in BlockAllocator<T>::Allocate () to assure aligned allocations
76
*
77
* \note
78
* Assuming the compiler is able to inline the mapping from type T, to which sized pool, there is really no cost in adding more. The reason
79
* to have different type T objects share the same BlockAllocationPool_ is to reduce fragmentation.
80
*
81
* \note
82
* To make BlockAllocator more of a plug-replacement for the std-c++ free-pool allocator, neither Allocate nor Deallocate () are cancelation
83
* points.
84
*
85
* Making them cancelation points led to too many subtle bugs were very rarely we would have 'terminate' called due to a memory allocation
86
* from a no-except method.
87
*
88
* \note
89
* As of this release (v2.1b14) - we don't support use of BlockAllocator for arrays. We've never needed it
90
* or used it and it seems more likely a bug than a feature. And its easily worked around by wrapping
91
* your array in a struct. And it would have a slight performance overhead/cost. MAYBE revisit
92
* in the future.
93
*
94
* But also see:
95
* @see https://en.cppreference.com/w/cpp/named_req/Allocator
96
* @see ManuallyBlockAllocated
97
* @see UseBlockAllocationIfAppropriate
98
*/
99
template
<
typename
T>
100
class
BlockAllocator
{
101
public
:
102
using
value_type = T;
103
104
public
:
105
/**
106
*/
107
constexpr
BlockAllocator
() =
default
;
108
template
<
typename
U>
109
constexpr
BlockAllocator
(
const
BlockAllocator<U>
&)
noexcept
;
110
111
public
:
112
/**
113
* \pre (n == 1)
114
*
115
* \note - though this can throw due to memory exhaustion, it will not throw Thread::InterupptionException - it is not a cancelation point.
116
*/
117
nonvirtual [[nodiscard]] T*
allocate
(std::size_t n);
118
119
public
:
120
/**
121
* \pre (p allocated by BlockAllocator<T>::Allocate ());
122
* \pre (n == 1)
123
* p can be nullptr
124
*/
125
nonvirtual
void
deallocate
(T* p, std::size_t n)
noexcept
;
126
127
public
:
128
/**
129
* Return to the free store all deallocated blocks whcih can be returned.
130
*
131
* This takes O (N), where N is the total number of extant operator new allocations for this size.
132
*
133
* It also currently uses a large block of contiguous memory, which could fail.
134
*
135
* But it might sometimes return memory from a particular data structure (fixed element size pool) to
136
* the general free store.
137
*
138
* Also - beware - this locks out other threads (from allocation/deallocation) during execution (which can take a while).
139
*
140
* \note Similar to shrink_to_fit
141
*/
142
static
void
Compact
();
143
144
public
:
145
/**
146
*/
147
nonvirtual
bool
operator== (
const
BlockAllocator
& rhs)
const
;
148
149
private
:
150
// ensure return value is >= sizeof (T), and has legit alignment for T
151
static
constexpr
size_t
AdjustSizeForPool_ ();
152
};
153
154
}
155
156
/*
157
********************************************************************************
158
***************************** Implementation Details ***************************
159
********************************************************************************
160
*/
161
#include "BlockAllocator.inl"
162
163
#endif
/*_Stroika_Foundation_Memory_BlockAllocator_h_*/
Assertions.h
Stroika::Foundation::Memory::BlockAllocator
Definition
BlockAllocator.h:100
Stroika::Foundation::Memory::BlockAllocator::Compact
static void Compact()
Definition
BlockAllocator.inl:393
Stroika::Foundation::Memory::BlockAllocator::deallocate
nonvirtual void deallocate(T *p, std::size_t n) noexcept
Definition
BlockAllocator.inl:380
Stroika::Foundation::Memory::BlockAllocator::allocate
nonvirtual T * allocate(std::size_t n)
Definition
BlockAllocator.inl:366
Library
Sources
Stroika
Foundation
Memory
BlockAllocator.h
Generated by
1.9.8