File size: 10,423 Bytes
2fbb123
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
#pragma once

#include <atomic>      // Atomic operations library for lock-free programming
#include <cstddef>     // Standard definitions, including size_t
#include <cstdint>     // Standard integer types
#include <memory>      // Memory management
#include <type_traits> // Type traits
#include <iostream>    // Input/output stream
#include <vector>
#include <stdexcept>

#ifdef _WIN32
#include <Windows.h>   // Windows system calls
#else
#include <sys/mman.h>  // Linux memory mapping
#include <unistd.h>    // Linux system calls
#endif

namespace hhb {
namespace core {

// Object pool configuration
template <typename T>
struct ObjectPoolConfig {
    static constexpr size_t DEFAULT_BLOCK_SIZE = 1024 * 1024; // 1MB block size
    static constexpr size_t DEFAULT_OBJECTS_PER_BLOCK = 1024;  // Default objects per block
};

// Memory block header structure
// alignas(64): Force alignment to 64 bytes, the size of a cache line
struct alignas(64) BlockHeader {
    std::atomic<BlockHeader*> next; // Atomic pointer to next block
    std::atomic<size_t> used;       // Number of used objects
    size_t capacity;                // Block capacity
    char data[];                    // Flexible array for actual objects
};

// Free object node
struct FreeNode {
    std::atomic<FreeNode*> next; // Atomic pointer to next free node
};

// High-performance object pool
template <typename T>
class ObjectPool {
public:
    // Constructor: Initialize object pool
    // objects_per_block: Number of objects per block
    ObjectPool(size_t objects_per_block = ObjectPoolConfig<T>::DEFAULT_OBJECTS_PER_BLOCK)
        : objects_per_block_(objects_per_block),
          // Calculate block size: block header size + object size * object count
          block_size_(sizeof(BlockHeader) + sizeof(T) * objects_per_block),
          head_block_(nullptr),
          free_list_(nullptr) {
        try {
            // Pre-allocate first block
            allocate_block();
        } catch (const std::exception& e) {
            std::cerr << "Exception in ObjectPool constructor: " << e.what() << std::endl;
            throw;
        }
    }

    // Destructor: Release all blocks
    ~ObjectPool() {
        // Release all blocks
        BlockHeader* block = head_block_;
        while (block) {
            BlockHeader* next = block->next;
            deallocate_block(block);
            block = next;
        }
    }

    // Disable copy and move
    ObjectPool(const ObjectPool&) = delete;
    ObjectPool& operator=(const ObjectPool&) = delete;
    ObjectPool(ObjectPool&&) = delete;
    ObjectPool& operator=(ObjectPool&&) = delete;

    // Allocate object
    T* allocate() {
        // Try to get from free list
        FreeNode* node = free_list_.load(std::memory_order_acquire);
        while (node) {
            // Load next node with acquire memory order
            FreeNode* next = node->next.load(std::memory_order_acquire);
            // Compare-exchange weak: lock-free CAS operation
            if (free_list_.compare_exchange_weak(node, next, std::memory_order_acq_rel, std::memory_order_acquire)) {
                // Reinterpret cast: convert FreeNode* to T*
                return reinterpret_cast<T*>(node);
            }
        }

        // Free list is empty, try to allocate in existing blocks
        BlockHeader* block = head_block_.load(std::memory_order_acquire);
        while (block) {
            size_t used = block->used.load(std::memory_order_acquire);
            while (used < block->capacity) {
                if (block->used.compare_exchange_weak(
                        used, used + 1,
                        std::memory_order_acq_rel,
                        std::memory_order_acquire)) {
                    return reinterpret_cast<T*>(
                        reinterpret_cast<char*>(block) + sizeof(BlockHeader) + sizeof(T) * used);
                }
            }
            block = block->next.load(std::memory_order_acquire);
        }

        // All blocks are full, allocate new block and retry on head
        allocate_block();
        BlockHeader* head = head_block_.load(std::memory_order_acquire);
        while (true) {
            size_t used = head->used.load(std::memory_order_acquire);
            if (used >= head->capacity) {
                allocate_block();
                head = head_block_.load(std::memory_order_acquire);
                continue;
            }
            if (head->used.compare_exchange_weak(
                    used, used + 1,
                    std::memory_order_acq_rel,
                    std::memory_order_acquire)) {
                return reinterpret_cast<T*>(
                    reinterpret_cast<char*>(head) + sizeof(BlockHeader) + sizeof(T) * used);
            }
        }
    }

    // Deallocate object
    void deallocate(T* ptr) {
        // Convert object to free node
        FreeNode* node = reinterpret_cast<FreeNode*>(ptr);
        // Load free list head with acquire memory order
        FreeNode* old_head = free_list_.load(std::memory_order_acquire);
        do {
            // Set new node's next pointer to old head
            node->next.store(old_head, std::memory_order_release);
            // CAS operation: try to set new node as list head
        } while (!free_list_.compare_exchange_weak(old_head, node, std::memory_order_acq_rel, std::memory_order_acquire));
    }

    // Get number of allocated objects
    size_t size() const {
        size_t total = 0;
        BlockHeader* block = head_block_.load(std::memory_order_acquire);
        while (block) {
            total += block->used.load(std::memory_order_acquire);
            block = block->next.load(std::memory_order_acquire);
        }
        return total;
    }

    // Iterate all objects and execute callback
    template <typename Func>
    void for_each(Func func) {
        BlockHeader* block = head_block_.load(std::memory_order_acquire);
        while (block) {
            size_t used = block->used.load(std::memory_order_acquire);
            for (size_t i = 0; i < used; ++i) {
                T* obj = reinterpret_cast<T*>(
                    reinterpret_cast<char*>(block) + sizeof(BlockHeader) + sizeof(T) * i);
                func(obj);
            }
            block = block->next.load(std::memory_order_acquire);
        }
    }

    // Access object by index
    T& operator[](size_t index) {
        size_t current_index = 0;
        BlockHeader* block = head_block_.load(std::memory_order_acquire);
        
        // Reverse traversal since blocks are linked in reverse order
        std::vector<BlockHeader*> blocks;
        while (block) {
            blocks.push_back(block);
            block = block->next.load(std::memory_order_acquire);
        }
        
        // Traverse in reverse order (oldest first)
        for (auto it = blocks.rbegin(); it != blocks.rend(); ++it) {
            BlockHeader* b = *it;
            size_t used = b->used.load(std::memory_order_acquire);
            if (index < current_index + used) {
                size_t offset = index - current_index;
                return *reinterpret_cast<T*>(
                    reinterpret_cast<char*>(b) + sizeof(BlockHeader) + sizeof(T) * offset);
            }
            current_index += used;
        }
        
        throw std::out_of_range("Index out of range");
    }

    // Const access
    const T& operator[](size_t index) const {
        return const_cast<ObjectPool*>(this)->operator[](index);
    }

private:
    // Allocate new memory block
    void allocate_block() {
        try {
            // Calculate block size
            size_t actual_block_size = block_size_;
            std::cout << "Allocating block with size: " << actual_block_size << " bytes" << std::endl;
            
            // Allocate memory
            void* memory = nullptr;
#ifdef _WIN32
            // Windows: Use VirtualAlloc to allocate virtual memory
            // MEM_COMMIT | MEM_RESERVE: Reserve and commit memory
            // PAGE_READWRITE: Readable and writable
            std::cout << "Calling VirtualAlloc" << std::endl;
            memory = VirtualAlloc(nullptr, actual_block_size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
            std::cout << "VirtualAlloc returned: " << memory << std::endl;
#else
            // Linux: Use mmap to allocate memory
            // MAP_PRIVATE | MAP_ANONYMOUS: Private, anonymous mapping
            // PROT_READ | PROT_WRITE: Readable and writable
            memory = mmap(nullptr, actual_block_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
#endif

            if (!memory) {
                std::cerr << "Failed to allocate memory" << std::endl;
                throw std::bad_alloc();
            }

            // Initialize block header
            BlockHeader* block = reinterpret_cast<BlockHeader*>(memory);
            block->used.store(0, std::memory_order_release);
            block->capacity = objects_per_block_;

            BlockHeader* old_head = head_block_.load(std::memory_order_acquire);
            do {
                block->next.store(old_head, std::memory_order_release);
            } while (!head_block_.compare_exchange_weak(old_head, block, std::memory_order_acq_rel, std::memory_order_acquire));
            std::cout << "Block allocated successfully" << std::endl;
        } catch (const std::exception& e) {
            std::cerr << "Exception in allocate_block: " << e.what() << std::endl;
            throw;
        }
    }

    // Release memory block
    void deallocate_block(BlockHeader* block) {
#ifdef _WIN32
        // Windows: Use VirtualFree to release memory
        // MEM_RELEASE: Release entire memory block
        VirtualFree(block, 0, MEM_RELEASE);
#else
        // Linux: Use munmap to release memory
        munmap(block, block_size_);
#endif
    }

    size_t objects_per_block_; // Number of objects per block
    size_t block_size_;        // Size of each block
    std::atomic<BlockHeader*> head_block_;  // Head block pointer
    std::atomic<FreeNode*> free_list_; // Free list (atomic pointer)
};

} // namespace core
} // namespace hhb