1 | //===----------------------------------------------------------------------===// |
2 | // DuckDB |
3 | // |
4 | // duckdb/common/allocator.hpp |
5 | // |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #pragma once |
10 | |
11 | #include "duckdb/common/common.hpp" |
12 | #include "duckdb/common/optional_ptr.hpp" |
13 | |
14 | namespace duckdb { |
15 | class Allocator; |
16 | class AttachedDatabase; |
17 | class ClientContext; |
18 | class DatabaseInstance; |
19 | class ExecutionContext; |
20 | class ThreadContext; |
21 | |
22 | struct AllocatorDebugInfo; |
23 | |
24 | struct PrivateAllocatorData { |
25 | PrivateAllocatorData(); |
26 | virtual ~PrivateAllocatorData(); |
27 | |
28 | unique_ptr<AllocatorDebugInfo> debug_info; |
29 | |
30 | template <class TARGET> |
31 | TARGET &Cast() { |
32 | D_ASSERT(dynamic_cast<TARGET *>(this)); |
33 | return reinterpret_cast<TARGET &>(*this); |
34 | } |
35 | template <class TARGET> |
36 | const TARGET &Cast() const { |
37 | D_ASSERT(dynamic_cast<const TARGET *>(this)); |
38 | return reinterpret_cast<const TARGET &>(*this); |
39 | } |
40 | }; |
41 | |
42 | typedef data_ptr_t (*allocate_function_ptr_t)(PrivateAllocatorData *private_data, idx_t size); |
43 | typedef void (*free_function_ptr_t)(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size); |
44 | typedef data_ptr_t (*reallocate_function_ptr_t)(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size, |
45 | idx_t size); |
46 | |
47 | class AllocatedData { |
48 | public: |
49 | DUCKDB_API AllocatedData(); |
50 | DUCKDB_API AllocatedData(Allocator &allocator, data_ptr_t pointer, idx_t allocated_size); |
51 | DUCKDB_API ~AllocatedData(); |
52 | // disable copy constructors |
53 | AllocatedData(const AllocatedData &other) = delete; |
54 | AllocatedData &operator=(const AllocatedData &) = delete; |
55 | //! enable move constructors |
56 | DUCKDB_API AllocatedData(AllocatedData &&other) noexcept; |
57 | DUCKDB_API AllocatedData &operator=(AllocatedData &&) noexcept; |
58 | |
59 | data_ptr_t get() { |
60 | return pointer; |
61 | } |
62 | const_data_ptr_t get() const { |
63 | return pointer; |
64 | } |
65 | idx_t GetSize() const { |
66 | return allocated_size; |
67 | } |
68 | bool IsSet() { |
69 | return pointer; |
70 | } |
71 | void Reset(); |
72 | |
73 | private: |
74 | optional_ptr<Allocator> allocator; |
75 | data_ptr_t pointer; |
76 | idx_t allocated_size; |
77 | }; |
78 | |
79 | class Allocator { |
80 | // 281TB ought to be enough for anybody |
81 | static constexpr const idx_t MAXIMUM_ALLOC_SIZE = 281474976710656ULL; |
82 | |
83 | public: |
84 | DUCKDB_API Allocator(); |
85 | DUCKDB_API Allocator(allocate_function_ptr_t allocate_function_p, free_function_ptr_t free_function_p, |
86 | reallocate_function_ptr_t reallocate_function_p, |
87 | unique_ptr<PrivateAllocatorData> private_data); |
88 | Allocator &operator=(Allocator &&allocator) noexcept = delete; |
89 | DUCKDB_API ~Allocator(); |
90 | |
91 | DUCKDB_API data_ptr_t AllocateData(idx_t size); |
92 | DUCKDB_API void FreeData(data_ptr_t pointer, idx_t size); |
93 | DUCKDB_API data_ptr_t ReallocateData(data_ptr_t pointer, idx_t old_size, idx_t new_size); |
94 | |
95 | AllocatedData Allocate(idx_t size) { |
96 | return AllocatedData(*this, AllocateData(size), size); |
97 | } |
98 | static data_ptr_t DefaultAllocate(PrivateAllocatorData *private_data, idx_t size) { |
99 | return data_ptr_cast(src: malloc(size: size)); |
100 | } |
101 | static void DefaultFree(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t size) { |
102 | free(ptr: pointer); |
103 | } |
104 | static data_ptr_t DefaultReallocate(PrivateAllocatorData *private_data, data_ptr_t pointer, idx_t old_size, |
105 | idx_t size) { |
106 | return data_ptr_cast(src: realloc(ptr: pointer, size: size)); |
107 | } |
108 | static Allocator &Get(ClientContext &context); |
109 | static Allocator &Get(DatabaseInstance &db); |
110 | static Allocator &Get(AttachedDatabase &db); |
111 | |
112 | PrivateAllocatorData *GetPrivateData() { |
113 | return private_data.get(); |
114 | } |
115 | |
116 | DUCKDB_API static Allocator &DefaultAllocator(); |
117 | DUCKDB_API static shared_ptr<Allocator> &DefaultAllocatorReference(); |
118 | |
119 | private: |
120 | allocate_function_ptr_t allocate_function; |
121 | free_function_ptr_t free_function; |
122 | reallocate_function_ptr_t reallocate_function; |
123 | |
124 | unique_ptr<PrivateAllocatorData> private_data; |
125 | }; |
126 | |
127 | template <class T> |
128 | T *AllocateArray(idx_t size) { |
129 | return (T *)Allocator::DefaultAllocator().AllocateData(size: size * sizeof(T)); |
130 | } |
131 | |
132 | template <class T> |
133 | void DeleteArray(T *ptr, idx_t size) { |
134 | Allocator::DefaultAllocator().FreeData(pointer: data_ptr_cast(ptr), size: size * sizeof(T)); |
135 | } |
136 | |
137 | template <typename T, typename... ARGS> |
138 | T *AllocateObject(ARGS &&... args) { |
139 | auto data = Allocator::DefaultAllocator().AllocateData(size: sizeof(T)); |
140 | return new (data) T(std::forward<ARGS>(args)...); |
141 | } |
142 | |
143 | template <typename T> |
144 | void DestroyObject(T *ptr) { |
145 | ptr->~T(); |
146 | Allocator::DefaultAllocator().FreeData(pointer: data_ptr_cast(ptr), size: sizeof(T)); |
147 | } |
148 | |
149 | //! The BufferAllocator is a wrapper around the global allocator class that sends any allocations made through the |
150 | //! buffer manager. This makes the buffer manager aware of the memory usage, allowing it to potentially free |
151 | //! other blocks to make space in memory. |
152 | //! Note that there is a cost to doing so (several atomic operations will be performed on allocation/free). |
153 | //! As such this class should be used primarily for larger allocations. |
154 | struct BufferAllocator { |
155 | DUCKDB_API static Allocator &Get(ClientContext &context); |
156 | DUCKDB_API static Allocator &Get(DatabaseInstance &db); |
157 | DUCKDB_API static Allocator &Get(AttachedDatabase &db); |
158 | }; |
159 | |
160 | } // namespace duckdb |
161 | |