A platform for high-performance distributed tool and library development written in C++. It can be deployed in two different cluster modes: standalone or distributed. API for v0.5.0, released on June 13, 2018.
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros
Allocator.h
Go to the documentation of this file.
1 /*****************************************************************************
2  * *
3  * Copyright 2018 Rice University *
4  * *
5  * Licensed under the Apache License, Version 2.0 (the "License"); *
6  * you may not use this file except in compliance with the License. *
7  * You may obtain a copy of the License at *
8  * *
9  * http://www.apache.org/licenses/LICENSE-2.0 *
10  * *
11  * Unless required by applicable law or agreed to in writing, software *
12  * distributed under the License is distributed on an "AS IS" BASIS, *
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
14  * See the License for the specific language governing permissions and *
15  * limitations under the License. *
16  * *
17  *****************************************************************************/
18 
19 #ifndef ALLOCATOR_H
20 #define ALLOCATOR_H
21 
22 #include <cstddef>
23 #include <iostream>
24 #include <vector>
25 #include <algorithm>
26 #include <iterator>
27 #include <cstring>
28 #include <unordered_map>
29 
30 //#define DEBUG_OBJECT_MODEL
31 //#define DEBUG_DEEP_COPY
32 namespace pdb {
33 
34 // This file contains the Allocator class, as well as its helper classes. While
35 // these classes are used extenviely during Object management, most programmers
36 // (even PDB engineers) will not directly touch these classes. Instead, the
37 // Allocator is used indirectly, via the functions in InterfactFunctions.h.
38 #define CHAR_PTR(c) ((char*)c)
39 
40 
41 // this is a little container class for all of the Allocator-managed blocks of
42 // memory that still contain active objects.
44 
45 public:
46  // where the block begins
47  void* start;
48 
49  // where the block ends
50  void* end;
51 
52  // friend Allocator;
53 public:
54  // create a block
56 
57  // free the memory associated with the block
58  void freeBlock();
59 
60  // create a block... the two params are the location, and the size in bytes
61  InactiveAllocationBlock(void* startIn, size_t numBytes);
62 
63  // returns true if the block is *before* compToMe, and does not contain it
64  friend bool operator<(const InactiveAllocationBlock& in, const void* compToMe);
65 
66  // returns true if the ends of the lhs is before the end of the rhs
67  friend bool operator<(const InactiveAllocationBlock& lhs, const InactiveAllocationBlock& rhs);
68 
69  // returns true if the block is *after* compToMe, and does not contain it
70  friend bool operator>(const InactiveAllocationBlock& in, const void* compToMe);
71 
72  // returns true if the block contains compToMe
73  friend bool operator==(const InactiveAllocationBlock& in, const void* compToMe);
74 
75  // decrement the reference count of this block
76  void decReferenceCount();
77 
78  // return the reference count
79  unsigned getReferenceCount();
80 
81  // free it if there are no more references
82  bool areNoReferences();
83 
84  // return the size
85  size_t numBytes();
86 
87  // return the starting pointer
88  void* getStart();
89 
90  // return the ending pointr
91  void* getEnd();
92 };
93 
94 // this class holds the current state of an alloctor
96 
97  // the location of the block of RAM that this guy is allocating from
98  void* activeRAM;
99 
100  // the number of bytes available in all
101  size_t numBytes;
102 
103  // true if we are supposed to throw an exception on allocation failure
105 
106  // true if the current allocation block is user-supplied
108 
109  // Th Allocator implements a really simple memory manager. Every time there is a
110  // request, the allocator class services it by pre-pending an usigned int to the returned
111  // bytes that mark the length of the set of bytes.
112  //
113  // When the bytes are freed, a pointer to the region is added to the "chunks"
114  // vector, declared below.
115  //
116  // All of the free regions are organized into 32 lists. The first list has all
117  // chunks 2^0 bytes in size. The second all chunks 2^1 bytes in size. The third
118  // all 2^2 and up to 2^3 - 1. The fourth 2^3 and up to 2^4 - 1. The fifth all
119  // 2^4 and up to 2^5 - 1, and so on.
120  //
121  // When a request for RAM is serviced, the correct list is located (that is, the
122  // first list that could possibly service the request) depending upon the size of
123  // the request. It is searched linearly, and the first free region that can
124  // service the request is returned. If no region in the list can service it, the
125  // next list is checked. Then the next, and so on.
126  std::vector<std::vector<void*>> chunks;
127 };
128 
129 
131 
132 // the dummy policy
133 
134 class DummyPolicy {
135 
136 public:
137 // free some RAM
138 #ifdef DEBUG_OBJECT_MODEL
139  inline void freeRAM(bool isContained,
140  void* here,
141  std::vector<InactiveAllocationBlock>& allInactives,
142  AllocatorState& myState,
143  int16_t typeId) {
144 #else
145  inline void freeRAM(bool isContained,
146  void* here,
147  std::vector<InactiveAllocationBlock>& allInactives,
148  AllocatorState& myState) {
149 #endif
150 
151  std::cout << "Default policy!!\n";
152  }
153 
154 // returns some RAM... this can throw an exception if the request is too large
155 // to be handled because there is not enough RAM in the current allocation block
156 #ifdef DEBUG_OBJECT_MODEL
157  inline void* getRAM(size_t howMuch, AllocatorState& myState, int16_t typeId){
158 #else
159  inline void* getRAM(size_t howMuch, AllocatorState& myState) {
160 #endif
161 
162  return nullptr;
163 
164 }
165 
166 inline void
168 }
169 
170 inline void unsetPolicy() {}
171 };
172 
173 
174 // Policy that we use by default, with simple reclaiming of deallocated space
176 
177 public:
178 // free some RAM
179 #ifdef DEBUG_OBJECT_MODEL
180  inline void freeRAM(bool isContained,
181  void* here,
182  std::vector<InactiveAllocationBlock>& allInactives,
183  AllocatorState& myState,
184  int16_t typeId);
185 #else
186  inline void freeRAM(bool isContained,
187  void* here,
188  std::vector<InactiveAllocationBlock>& allInactives,
189  AllocatorState& myState);
190 #endif
191 
192 // returns some RAM... this can throw an exception if the request is too large
193 // to be handled because there is not enough RAM in the current allocation block
194 #ifdef DEBUG_OBJECT_MODEL
195  inline void* getRAM(size_t howMuch, AllocatorState& myState, int16_t typeId);
196 #else
197  inline void* getRAM(size_t howMuch, AllocatorState& myState);
198 #endif
199 
201 
203  }
204 };
205 
206 
207 // Policy that never try to reclaim deallocated spaces
209 
210 public:
211 // free some RAM
212 #ifdef DEBUG_OBJECT_MODEL
213  inline void freeRAM(bool isContained,
214  void* here,
215  std::vector<InactiveAllocationBlock>& allInactives,
216  AllocatorState& myState,
217  int16_t typeId);
218 #else
219  inline void freeRAM(bool isContained,
220  void* here,
221  std::vector<InactiveAllocationBlock>& allInactives,
222  AllocatorState& myState);
223 #endif
224 
225 // returns some RAM without reclaiming spaces...
226 // this can throw an exception if the request is too large
227 // to be handled because there is not enough RAM in the current allocation block
228 #ifdef DEBUG_OBJECT_MODEL
229  inline void* getRAM(size_t howMuch, AllocatorState& myState, int16_t typeId);
230 #else
231  inline void* getRAM(size_t howMuch, AllocatorState& myState);
232 #endif
233 
235 
237  }
238 };
239 
240 
241 // Policy that do not do reference counting
243 
244 public:
245 // do nothing
246 #ifdef DEBUG_OBJECT_MODEL
247  inline void freeRAM(bool isContained,
248  void* here,
249  std::vector<InactiveAllocationBlock>& allInactives,
250  AllocatorState& myState,
251  int16_t typeId);
252 #else
253  inline void freeRAM(bool isContained,
254  void* here,
255  std::vector<InactiveAllocationBlock>& allInactives,
256  AllocatorState& myState);
257 #endif
258 
259 // returns some RAM... this can throw an exception if the request is too large
260 // to be handled because there is not enough RAM in the current allocation block
261 #ifdef DEBUG_OBJECT_MODEL
262  inline void* getRAM(size_t howMuch, AllocatorState& myState, int16_t typeId);
263 #else
264  inline void* getRAM(size_t howMuch, AllocatorState& myState);
265 #endif
266 
268 
270  }
271 };
272 
273 
274 // forward declaration of the PolicyList class
275 
276 template <typename FirstPolicy, typename... OtherPolicies>
277 
279 
280 
281 // will use SINFAE to select type PolicyList <OtherPolicies...> if OtherPolicies is non-empty
282 
283 template <typename... OtherPolicies>
284 
285 typename std::enable_if<sizeof...(OtherPolicies) >= 1, PolicyList<OtherPolicies...>>::type first();
286 
287 
288 // will use SINFAE to select type DefaultPolicy if OtherPolicies is empty
289 
290 template <typename... OtherPolicies>
291 
292 typename std::enable_if<sizeof...(OtherPolicies) == 0, DummyPolicy>::type first();
293 
294 
295 // the actual policy list class
296 
297 template <typename FirstPolicy, typename... OtherPolicies>
298 
299 class PolicyList {
300 
301 private:
302  FirstPolicy firstPolicy;
303 
304  decltype(first<OtherPolicies...>()) theRest;
305 
306  bool useMe = false;
307 
308 public:
309  inline void setPolicy(AllocatorPolicy toMe) {
310 
311  if (firstPolicy.getPolicyName() == toMe) {
312  // std :: cout << "firstPolicy.getPolicyName()=" << firstPolicy.getPolicyName() << std
313  // :: endl;
314  useMe = true;
315  theRest.unsetPolicy();
316 
317  } else {
318  // std :: cout << "theRest.setPolicy(" << firstPolicy.getPolicyName() << ")" << std ::
319  // endl;
320  useMe = false;
321  theRest.setPolicy(toMe);
322  }
323  }
324 
325  inline void unsetPolicy() {
326 
327  useMe = false;
328  theRest.unsetPolicy();
329  }
330 
331 // free some RAM
332 #ifdef DEBUG_OBJECT_MODEL
333  inline void freeRAM(bool isContained,
334  void* here,
335  std::vector<InactiveAllocationBlock>& allInactives,
336  AllocatorState& myState,
337  int16_t typeId) {
338 #else
339  inline void freeRAM(bool isContained,
340  void* here,
341  std::vector<InactiveAllocationBlock>& allInactives,
342  AllocatorState& myState) {
343 #endif
344 
345  if (useMe) {
346 #ifdef DEBUG_OBJECT_MODEL
347  // std :: cout << "I am the policy: " << firstPolicy.getPolicyName() << std:: endl;
348  firstPolicy.freeRAM(isContained, here, allInactives, myState, typeId);
349 #else
350  firstPolicy.freeRAM(isContained, here, allInactives, myState);
351 #endif
352  } else {
353 #ifdef DEBUG_OBJECT_MODEL
354  // std :: cout << "I am not the policy: " << firstPolicy.getPolicyName() << std:: endl;
355  theRest.freeRAM(isContained, here, allInactives, myState, typeId);
356 #else
357  theRest.freeRAM(isContained, here, allInactives, myState);
358 #endif
359  }
360  }
361 
362 #ifdef DEBUG_OBJECT_MODEL
363  inline void* getRAM(size_t howMuch, AllocatorState& myState, int16_t typeId){
364 #else
365  inline void* getRAM(size_t howMuch, AllocatorState& myState) {
366 #endif
367 
368  if (useMe) {
369 #ifdef DEBUG_OBJECT_MODEL
370  // std :: cout << "I am the policy: " << firstPolicy.getPolicyName() << std:: endl;
371  return firstPolicy.getRAM(howMuch, myState, typeId);
372 #else
373  return firstPolicy.getRAM(howMuch, myState);
374 #endif
375 } else {
376 #ifdef DEBUG_OBJECT_MODEL
377  // std :: cout << "I am not the policy: " << firstPolicy.getPolicyName() << std :: endl;
378  return theRest.getRAM(howMuch, myState, typeId);
379 #else
380  return theRest.getRAM(howMuch, myState);
381 #endif
382 }
383 }
384 }
385 ;
386 // this class is an exception that is (optionally) thrown
387 // when we try to do an allocation to create an Object
388 // but there is not enough RAM
389 class NotEnoughSpace : public std::bad_alloc {
390  virtual const char* what() const throw() {
391  return "Not enough free memory in current allocation block.\n";
392  }
393 };
394 
395 extern NotEnoughSpace myException;
396 
397 
398 template <class ObjType>
399 class Handle;
400 
401 
402 // this is the Allocator class. There is one allocator per thread. The allocator
403 // is responsible for managing the RAM that is used to allocate everything that
404 // is descended from object. Most programmers (even PDB engineers) will never touch
405 // the allocator class directly.
406 template <typename FirstPolicy, typename... OtherPolicies>
408 
409 private:
410  PolicyList<FirstPolicy, OtherPolicies...> myPolicies;
411 
413 
414  // this is the list of all self-managed allocation blocks that are not active, but
415  // still contain some object...
416  std::vector<InactiveAllocationBlock> allInactives;
417 
418 public:
419  // return true if allocations should not fail due to not enough RAM...
420  // in this case, a null pointer is returned on a bad allocate, and NOT
421  // an exception
422  bool doNotFail();
423 
424  // destructor; if there is a self-allocated current allocation block, free it
426 
427 
428  // to set policy
429  inline void setPolicy(AllocatorPolicy policy);
430 
431 
432  // we have no active RAM
434 
435  // give this guy the specified active RAM
436  MultiPolicyAllocator(size_t numBytes);
437 
438  // this finds the block contianing the indicated pointer and sets the number of references to
439  // zero, freeing the block if applicable
440  inline void emptyOutBlock(void* here);
441 
442  // get the number of currently-reachable objects in this guy's block
443  template <class ObjType>
445 
446  // get the number of currently-reachable objects in the current allocation block
447  inline unsigned getNumObjectsInCurrentAllocatorBlock();
448 
449  // get the number of objects in the block containing the given memory location
450  inline unsigned getNumObjectsInAllocatorBlock(void* forMe);
451 
452  // get the number of bytes available in the current allocation block
454 
455  // returns true if and only if the RAM is in the current allocation block
456  inline bool contains(void* whereIn);
457 
458  // returns true if and only if the RAM is in the current allocation block
459  // or it is in some inactive allocation block that has not been deleted
460  // as of yet
461  inline bool isManaged(void* here);
462 
463 // returns some RAM... this can throw an exception if the request is too large
464 // to be handled because there is not enough RAM in the current allocation block
465 // JIA NOTE: enable DEBUG_OBJECT_MODEL will bring significant performance overhead, and should be
466 // used cautiously.
467 #ifdef DEBUG_OBJECT_MODEL
468  inline void* getRAM(size_t howMuch, int16_t typeId);
469 #else
470  inline void* getRAM(size_t howMuch);
471 #endif
472 
473 // free some RAM that was previous allocated via a call to getRAM
474 #ifdef DEBUG_OBJECT_MODEL
475  inline void freeRAM(void* here, int16_t typeId);
476 #else
477  inline void freeRAM(void* here);
478 #endif
479 
480  // make this RAM the current allocation block
481  inline void setupBlock(void* where, size_t numBytesIn, bool throwExceptionOnFail);
482 
483  // make this user-supplied RAM the current allocation block
484  inline void setupUserSuppliedBlock(void* where, size_t numBytesIn, bool throwExceptionOnFail);
485 
486  // returns a pointer to the allocation block housing the
487  // object pointed to by this handle. A nullptr is returned
488  // if it is point possible to find the allocation block
489  // pointed to by this handle. This happens if the object
490  // pointed to by forMe was not created in an allocation
491  // block managed by this thread's allocator.
492  //
493  // In the block of memory returned, the first few
494  // byes returned store the number of bytes used by the
495  // allocator, and the next few bytes store an offset to
496  // the location of forMe.
497  template <class ObjType>
498  void* getAllocationBlock(Handle<ObjType>& forMe);
499 
500  // uses a specified block of memory for all allocations, until
501  // restoreAllocationBlock () is called... SHOUD NOT BE USED DIRECTLY
502  AllocatorState temporarilyUseBlockForAllocations(void* putMeHere, size_t numBytesAvailable);
503  AllocatorState temporarilyUseBlockForAllocations(size_t numBytesAvailable);
504 
505  // goes back to the old allocation block.. this should only
506  // by alled after a call to temporarilyUseBlockForAllocations ()
507  inline void restoreAllocationBlock(AllocatorState& restoreMe);
508 
509  // Jia Note: for debugging object model memory management
510  inline std::string printCurrentBlock();
511  inline std::string printInactiveBlocks();
512 
513  // Jia Note: this function should only be used for debugging purposes.
514  inline void cleanInactiveBlocks();
515 
516  inline void cleanInactiveBlocks(size_t size);
517 
518 private:
519  friend void makeObjectAllocatorBlock(size_t numBytesIn);
520 };
521 
523 
524 
525 // returns a reference to the allocator that should be used. Each process has one default
526 // allocator.
527 // Each of the workers in a WorkerQueue are given their own allocator, which resides at the
528 // beginning
529 // of the worker's call stack. If this is called from within a worker, the worker's allocator is
530 // returned; otherwise, the process' default allocator is returned.
531 Allocator& getAllocator();
532 }
533 
534 // because all of the methods are inline, include the .cc file
535 #include "Allocator.cc"
536 
537 #endif
size_t getBytesAvailableInCurrentAllocatorBlock()
MultiPolicyAllocator< DefaultPolicy, NoReusePolicy, NoReferenceCountPolicy > Allocator
Definition: Allocator.h:522
friend bool operator>(const InactiveAllocationBlock &in, const void *compToMe)
Definition: Allocator.cc:64
FirstPolicy firstPolicy
Definition: Allocator.h:302
AllocatorPolicy
Definition: Allocator.h:130
void * getRAM(size_t howMuch, AllocatorState &myState)
Definition: Allocator.h:365
Allocator & getAllocator()
Definition: Allocator.cc:943
void * getRAM(size_t howMuch, AllocatorState &myState)
Definition: Allocator.h:159
NotEnoughSpace myException
std::vector< InactiveAllocationBlock > allInactives
Definition: Allocator.h:416
friend bool operator==(const InactiveAllocationBlock &in, const void *compToMe)
Definition: Allocator.cc:69
std::vector< std::vector< void * > > chunks
Definition: Allocator.h:126
void freeRAM(bool isContained, void *here, std::vector< InactiveAllocationBlock > &allInactives, AllocatorState &myState)
Definition: Allocator.h:339
PolicyList< OtherPolicies...>::type first()
unsigned getNumObjectsInHomeAllocatorBlock(Handle< ObjType > &forMe)
void freeRAM(bool isContained, void *here, std::vector< InactiveAllocationBlock > &allInactives, AllocatorState &myState)
Definition: Allocator.h:145
friend bool operator<(const InactiveAllocationBlock &in, const void *compToMe)
Definition: Allocator.cc:54
void setPolicy(AllocatorPolicy toMe)
Definition: Allocator.h:167
unsigned getNumObjectsInAllocatorBlock(void *forMe)
PolicyList< FirstPolicy, OtherPolicies...> myPolicies
Definition: Allocator.h:410
void unsetPolicy()
Definition: Allocator.h:170
virtual const char * what() const
Definition: Allocator.h:390
unsigned getNumObjectsInCurrentAllocatorBlock()
void unsetPolicy()
Definition: Allocator.h:325
AllocatorPolicy getPolicyName()
Definition: Allocator.h:200
AllocatorState myState
Definition: Allocator.h:412
void makeObjectAllocatorBlock(size_t numBytesIn, bool throwExceptionOnFail)
AllocatorPolicy getPolicyName()
Definition: Allocator.h:234
AllocatorPolicy getPolicyName()
Definition: Allocator.h:267