-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathChunkPool.cpp
More file actions
190 lines (168 loc) · 4.31 KB
/
Copy pathChunkPool.cpp
File metadata and controls
190 lines (168 loc) · 4.31 KB
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
/* *************************************************************************/
/*
* @file MemoryManager.cpp
* @author sean shih
* @date 09/12/2014 Fri 05:48 PM
* @brief
*
* @copyright
* All content (c) 2014-2015 DigiPen (USA) Corporation, all rights reserved.
*/
/* *************************************************************************/
#include <cassert>
#include "ChunkPool.h"
#include "mem_config.h"
using namespace Memory;
Memory::ChunkPool::ChunkPool(unsigned chunk_size, unsigned chunk_num)
: _chunk_size(chunk_size),
_page_list(nullptr),
_free_list(nullptr),
_chunk_num(chunk_num),
_page_size(k_header_size + chunk_num * (_chunk_size + k_info_size)),
_automatic_recycle(false)
{
}
// during destruction, free all pages if page empty
// if page not empty, keep them for automatic recycle
Memory::ChunkPool::~ChunkPool()
{
Header* next_page = 0;
for (Header* page = _page_list; page != nullptr; page = next_page)
{
next_page = page->next;
if (page->chunk_count == _chunk_num)
FreePage(page);
else
{
#ifdef PRINT_REPORT
volatile int i = 0;
printf("one remaining page in size %d chunk pool: %d\n", _chunk_size, i);
#endif
}
}
TriggerAutomaticRecycle();
}
// allocate a new page and insert into free list
void* Memory::ChunkPool::Alloc()
{
assert(!_automatic_recycle && "Cannot allocate when automatic recycle is triggered!");
#ifdef THREAD_SAFE
std::lock_guard<std::mutex> lock(_locker);
#endif
if (!_free_list)
{
#ifdef PRINT_REPORT
std::printf("%d chunk page used up, cache missed\n", _chunk_size);
#endif
Header* newpage = GetNewPage();
InsertToFreeList(newpage);
}
return Pop(_free_list);
}
// deallocate a page
void Memory::ChunkPool::Dealloc(void* data)
{
if (data)
{
#ifdef THREAD_SAFE
std::lock_guard<std::mutex> lock(_locker);
#endif
Link(_free_list, (char*)data);
const auto pagehead = GetPageHead((char*)data);
auto chunk_num = ++pagehead->chunk_count;
if (_automatic_recycle && chunk_num == _chunk_num)
{
FreePage(pagehead);
#ifdef PRINT_REPORT
volatile int i = 0;
printf("automatically freed page with %d chunk size %d\n", _chunk_size, i);
#endif
}
}
}
// low level page allocation function for page initialization
ChunkPool::Header* Memory::ChunkPool::GetNewPage()
{
if (_page_size > 0)
{
auto page = (Header*)_aligned_malloc(_page_size, 16);
page->chunk_count = _chunk_num;
page->self = this;
page->prev = _page_list;
page->next = nullptr;
if (_page_list)
_page_list->next = page;
else
_page_list = page;
return page;
}
else
return nullptr;
}
// link two pages together, child becomes the new list head
void Memory::ChunkPool::Link(char*& list, char* child)
{
Next(child) = list;
list = child;
}
// low level page free function for linking old pages together
void Memory::ChunkPool::FreePage(Header* page)
{
if (page->prev)
page->prev = page->next;
else
_page_list = page->next;
_aligned_free(page);
}
// pop page list head
char* Memory::ChunkPool::Pop(char*& list)
{
if (!list)
return nullptr;
--GetPageHead(list)->chunk_count;
char* popped = list;
list = Next(list);
return popped;
}
// get next page
char*& Memory::ChunkPool::Next(char* node)
{
return *(char**)node;
}
// insert page into free list
// the list is instrusive, which means it use part of the header as the pointer to next page in the list
void Memory::ChunkPool::InsertToFreeList(Header* page)
{
char* node = ((char*)page + k_header_size + k_info_size);
for (size_t i = 0; i < _chunk_num; ++i, node += (_chunk_size + k_info_size))
{
Info& info = *(Info*)(node - k_info_size);
info.pattern = k_pattern;
info.head = page;
Link(_free_list, node);
}
}
// check chunk validity and get page head from a chunk
ChunkPool* Memory::ChunkPool::GetPageHeadCheck(void* ptr)
{
if (!ptr)
return nullptr;
else
{
Info& info = *(Info*)((char*)ptr - k_info_size);
if (info.pattern != k_pattern)
return nullptr;
else
return info.head->self;
}
}
void Memory::ChunkPool::TriggerAutomaticRecycle()
{
_automatic_recycle = true;
}
// get page head from a chunk directly without checking
ChunkPool::Header* Memory::ChunkPool::GetPageHead(char* chunk)
{
const Info& info = *(Info*)(chunk - k_info_size);
return info.head;
}