CernVM-FS  2.13.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
malloc_arena.cc
Go to the documentation of this file.
1 
5 #define __STDC_FORMAT_MACROS
6 
7 
8 #include "malloc_arena.h"
9 
10 #include <cassert>
11 #include <cstddef>
12 #include <cstring>
13 #include <new>
14 
15 #include "util/smalloc.h"
16 
17 using namespace std; // NOLINT
18 
19 
25  const int32_t block_size) {
26  bool wrapped = false;
27  // Generally: p = LINK(q)
28  AvailBlockCtl *q = rover_;
29  AvailBlockCtl *p;
30  do {
31  p = q->GetNextPtr(arena_);
32  if (p->size >= block_size) {
33  rover_ = p->GetNextPtr(arena_);
34  return p;
35  }
36  if (p == head_avail_) {
37  if (wrapped)
38  return NULL;
39  wrapped = true;
40  }
41  q = p;
42  } while (true);
43 }
44 
45 
54 void MallocArena::Free(void *ptr) {
55  assert(Contains(ptr));
56 
57  no_reserved_--;
58 
59  ReservedBlockCtl *block_ctl = reinterpret_cast<ReservedBlockCtl *>(
60  reinterpret_cast<char *>(ptr) - sizeof(ReservedBlockCtl));
61  char prior_tag = *(reinterpret_cast<char *>(block_ctl) - 1);
62  assert((prior_tag == kTagAvail) || (prior_tag == kTagReserved));
63 
64  int32_t new_size = block_ctl->size();
65  assert(new_size > 0);
66  AvailBlockCtl *new_avail = reinterpret_cast<AvailBlockCtl *>(block_ctl);
67 
68  if (prior_tag == kTagAvail) {
69  // Merge with block before and remove the block from the list
70  int32_t prior_size = reinterpret_cast<AvailBlockTag *>(
71  reinterpret_cast<char *>(block_ctl)
72  - sizeof(AvailBlockTag))
73  ->size;
74  assert(prior_size > 0);
75  new_size += prior_size;
76  new_avail = reinterpret_cast<AvailBlockCtl *>(
77  reinterpret_cast<char *>(block_ctl) - prior_size);
78  // new_avail points now to the prior block
79  UnlinkAvailBlock(new_avail);
80  if (rover_ == new_avail)
81  rover_ = head_avail_;
82  }
83 
84  int32_t succ_size = *reinterpret_cast<int32_t *>(
85  reinterpret_cast<char *>(new_avail) + new_size);
86  if (succ_size >= 0) {
87  // Merge with succeeding block and remove the block from the list
88  AvailBlockCtl *succ_avail = reinterpret_cast<AvailBlockCtl *>(
89  reinterpret_cast<char *>(new_avail) + new_size);
90  UnlinkAvailBlock(succ_avail);
91  new_size += succ_size;
92  if (rover_ == succ_avail)
93  rover_ = head_avail_;
94  }
95 
96  // Set new free block's boundaries
97  new_avail->size = new_size;
98  new (AvailBlockTag::GetTagLocation(new_avail)) AvailBlockTag(new_size);
99 
100  EnqueueAvailBlock(new_avail);
101 }
102 
103 
108  AvailBlockCtl *next = head_avail_;
109  AvailBlockCtl *prev = head_avail_->GetPrevPtr(arena_);
110  next->link_prev = block->ConvertToLink(arena_);
111  prev->link_next = block->ConvertToLink(arena_);
112  block->link_next = head_avail_->ConvertToLink(arena_);
113  block->link_prev = prev->ConvertToLink(arena_);
114 }
115 
116 
121 uint32_t MallocArena::GetSize(void *ptr) const {
122  assert(Contains(ptr));
123 
124  ReservedBlockCtl *block_ctl = reinterpret_cast<ReservedBlockCtl *>(
125  reinterpret_cast<char *>(ptr) - sizeof(ReservedBlockCtl));
126  int32_t size = block_ctl->size();
127  assert(size > 1);
128  return size - sizeof(ReservedBlockCtl) - 1;
129 }
130 
131 
138 void *MallocArena::Malloc(const uint32_t size) {
139  assert(size > 0);
140 
141  // Control word first, block type tag last
142  int32_t total_size = sizeof(ReservedBlockCtl) + size + 1;
143  total_size = RoundUp8(total_size);
144  if (total_size < kMinBlockSize)
145  total_size = kMinBlockSize;
146 
147  AvailBlockCtl *p = FindAvailBlock(total_size);
148  if (p == NULL)
149  return NULL;
150 
151  no_reserved_++;
152  return ReserveBlock(p, total_size);
153 }
154 
155 
162 MallocArena::MallocArena(unsigned arena_size)
163  : arena_(reinterpret_cast<char *>(sxmmap_align(arena_size)))
164  , head_avail_(reinterpret_cast<AvailBlockCtl *>(arena_ + sizeof(uint64_t)))
165  , rover_(head_avail_)
166  , no_reserved_(0)
167  , arena_size_(arena_size) {
168  assert(arena_size_ > 0);
169  assert((arena_size_ % (2 * 1024 * 1024)) == 0); // Multiple of 2MB
170  assert(arena_size_ <= (512 * 1024 * 1024)); // <= 512MB
171 
172  const unsigned char padding = 7;
173  // Size of the initial free block: everything minus arena boundaries
174  int32_t usable_size = arena_size_
175  - (sizeof(uint64_t) + sizeof(AvailBlockCtl) + padding
176  + 1 + sizeof(int32_t));
177  assert((usable_size % 8) == 0);
178 
179  // First 8 bytes of arena: this pointer (occupies only 4 bytes on 32bit
180  // architectures, in which case the second 4 bytes are unused.)
181  *reinterpret_cast<MallocArena **>(arena_) = this;
182 
183  // The initial large free block
184  AvailBlockCtl *free_block = new (arena_ + sizeof(uint64_t)
185  + sizeof(AvailBlockCtl) + padding + 1)
186  AvailBlockCtl();
187  free_block->size = usable_size;
188  free_block->link_next = free_block->link_prev = head_avail_->ConvertToLink(
189  arena_);
190  new (AvailBlockTag::GetTagLocation(free_block)) AvailBlockTag(usable_size);
191 
192  head_avail_->size = 0;
194  arena_);
195 
196  // Prevent succeeding blocks from merging
197  *(reinterpret_cast<char *>(free_block) - 1) = kTagReserved;
198  // Final tag: reserved block marker
199  *reinterpret_cast<int32_t *>(arena_ + arena_size_ - sizeof(int32_t)) = -1;
200 }
201 
202 
208  unsigned char pattern) {
209  MallocArena *result = new MallocArena(arena_size);
210  // At this point, there is one big free block linked to by head_avail_
211  AvailBlockCtl *free_block = result->head_avail_->GetNextPtr(result->arena_);
212  assert(free_block != result->head_avail_);
213  assert(free_block->size > 0);
214  // Strip control information at both ends of the block
215  int usable_size = free_block->size
216  - (sizeof(AvailBlockCtl) + sizeof(AvailBlockTag));
217  assert(usable_size > 0);
218  memset(free_block + 1, pattern, usable_size);
219  return result;
220 }
221 
222 
224 
225 
231 void *MallocArena::ReserveBlock(AvailBlockCtl *block, int32_t block_size) {
232  assert(block->size >= block_size);
233 
234  int32_t remaining_size = block->size - block_size;
235  // Avoid creation of very small blocks
236  if (remaining_size < kMinBlockSize) {
237  block_size += remaining_size;
238  remaining_size = 0;
239  }
240 
241  // Update the list of available blocks
242  if (remaining_size == 0) {
243  // Remove free block p from the list of available blocks
244  UnlinkAvailBlock(block);
245  } else {
246  block->ShrinkTo(remaining_size);
247  }
248 
249  // Place the new allocation, which also sets the block type tag at the end
250  char *new_block = reinterpret_cast<char *>(block) + remaining_size;
251  new (new_block) ReservedBlockCtl(block_size);
252  return new_block + sizeof(ReservedBlockCtl);
253 }
254 
255 
262  AvailBlockCtl *next = block->GetNextPtr(arena_);
263  AvailBlockCtl *prev = block->GetPrevPtr(arena_);
264  prev->link_next = block->link_next;
265  next->link_prev = block->link_prev;
266 }
MallocArena(unsigned arena_size)
AvailBlockCtl * FindAvailBlock(const int32_t block_size)
Definition: malloc_arena.cc:24
void ShrinkTo(int32_t smaller_size)
Definition: malloc_arena.h:115
static void * GetTagLocation(AvailBlockCtl *block)
Definition: malloc_arena.h:129
assert((mem||(size==0))&&"Out Of Memory")
void UnlinkAvailBlock(AvailBlockCtl *block)
int32_t ConvertToLink(char *base)
Definition: malloc_arena.h:112
static const int kMinBlockSize
Definition: malloc_arena.h:72
void * ReserveBlock(AvailBlockCtl *block, int32_t block_size)
AvailBlockCtl * GetNextPtr(char *base)
Definition: malloc_arena.h:106
void EnqueueAvailBlock(AvailBlockCtl *block)
static const char kTagReserved
Definition: malloc_arena.h:99
static MallocArena * CreateInitialized(unsigned arena_size, unsigned char pattern)
AvailBlockCtl * head_avail_
Definition: malloc_arena.h:176
unsigned arena_size_
Definition: malloc_arena.h:179
static uint64_t RoundUp8(const uint64_t size)
Definition: smalloc.h:37
AvailBlockCtl * GetPrevPtr(char *base)
Definition: malloc_arena.h:109
void Free(void *ptr)
Definition: malloc_arena.cc:54
void * Malloc(const uint32_t size)
char * arena_
The memory block.
Definition: malloc_arena.h:169
uint32_t GetSize(void *ptr) const
static void size_t size
Definition: smalloc.h:54