/sparrowhawk/foundation/ESFBuddyAllocator.cpp

http://github.com/jtblatt/duderino · C++ · 415 lines · 238 code · 110 blank · 67 comment · 65 complexity · 6627a5cc6e53ab17492e7a6157c48384 MD5 · raw file

  1. /** @file ESFBuddyAllocator.cpp
  2. * @brief A ESFAllocator implementation good for variable-length allocations
  3. *
  4. * Copyright (c) 2009 Yahoo! Inc.
  5. * The copyrights embodied in the content of this file are licensed by Yahoo! Inc.
  6. * under the BSD (revised) open source license.
  7. *
  8. * Derived from code that is Copyright (c) 2009 Joshua Blatt and offered under both
  9. * BSD and Apache 2.0 licenses (http://sourceforge.net/projects/sparrowhawk/).
  10. *
  11. * $Author: blattj $
  12. * $Date: 2009/05/25 21:51:08 $
  13. * $Name: $
  14. * $Revision: 1.3 $
  15. */
  16. #ifndef ESF_BUDDY_ALLOCATOR_H
  17. #include <ESFBuddyAllocator.h>
  18. #endif
  19. #ifndef ESF_ASSERT_H
  20. #include <ESFAssert.h>
  21. #endif
  22. ESFBuddyAllocator::KVal ESFBuddyAllocator::GetKVal(ESFUWord requestedSize) {
  23. ESFUWord adjustedSize = requestedSize + sizeof(AvailListElem);
  24. KVal kVal = 1;
  25. while (adjustedSize > (ESF_UWORD_C(1) << kVal)) {
  26. ++kVal;
  27. }
  28. return kVal;
  29. }
  30. ESFBuddyAllocator::AvailListElem *ESFBuddyAllocator::popAvailList(ESFBuddyAllocator::KVal kVal) {
  31. ESF_ASSERT( kVal < ESF_AVAIL_LIST_LENGTH );
  32. AvailListElem *elem = _availList[kVal];
  33. if (!elem)
  34. return 0;
  35. AvailListElem *next = elem->_linkF;
  36. ESF_ASSERT( 0 == elem->_linkB );
  37. ESF_ASSERT( kVal == elem->_kVal );
  38. ESF_ASSERT( 1 == elem->_tag );
  39. elem->_linkB = 0;
  40. elem->_linkF = 0;
  41. elem->_tag = 0;
  42. if (next) {
  43. next->_linkB = 0;
  44. _availList[kVal] = next;
  45. } else {
  46. _availList[kVal] = 0;
  47. }
  48. return elem;
  49. }
  50. void ESFBuddyAllocator::removeFromAvailList(ESFBuddyAllocator::AvailListElem *elem) {
  51. ESF_ASSERT( elem );
  52. ESF_ASSERT( elem->_kVal < ESF_AVAIL_LIST_LENGTH );
  53. if (elem->_linkB) {
  54. elem->_linkB->_linkF = elem->_linkF;
  55. }
  56. if (elem->_linkF) {
  57. elem->_linkF->_linkB = elem->_linkB;
  58. }
  59. if (!elem->_linkB) {
  60. _availList[elem->_kVal] = elem->_linkF;
  61. }
  62. elem->_linkB = 0;
  63. elem->_linkF = 0;
  64. elem->_tag = 0;
  65. }
  66. void ESFBuddyAllocator::pushAvailList(AvailListElem *elem) {
  67. ESF_ASSERT( elem );
  68. ESF_ASSERT( elem->_kVal < ESF_AVAIL_LIST_LENGTH );
  69. AvailListElem *next = _availList[elem->_kVal];
  70. elem->_linkB = 0;
  71. elem->_linkF = next;
  72. elem->_tag = 1;
  73. if (next) {
  74. next->_linkB = elem;
  75. }
  76. _availList[elem->_kVal] = elem;
  77. }
  78. ESFBuddyAllocator::ESFBuddyAllocator(int size, ESFAllocator *source) :
  79. _failoverAllocator(0), _sourceAllocator(source), _pool(0), _poolKVal(0) {
  80. if (1 > size || ESF_AVAIL_LIST_LENGTH < size)
  81. return;
  82. if (!_sourceAllocator)
  83. return;
  84. for (int i = 0; i < ESF_AVAIL_LIST_LENGTH; ++i) {
  85. _availList[i] = 0;
  86. }
  87. _poolKVal = size;
  88. _sourceAllocator = source;
  89. }
  90. ESFBuddyAllocator::~ESFBuddyAllocator() {
  91. destroy();
  92. }
  93. void *
  94. ESFBuddyAllocator::allocate(ESFUWord size) {
  95. if (0 == size) {
  96. return 0;
  97. }
  98. if (!_pool) {
  99. if (ESF_SUCCESS != initialize()) {
  100. return 0;
  101. }
  102. }
  103. //
  104. // Find an element large enough to fulfill the request. If found, remove
  105. // it from the available list. If not found, try the failover allocator.
  106. //
  107. // Knuth R1 and R2
  108. //
  109. KVal minimumKVal = GetKVal(size);
  110. KVal actualKVal = minimumKVal;
  111. char *elem = 0;
  112. for (; actualKVal < ESF_AVAIL_LIST_LENGTH; ++actualKVal) {
  113. if (0 != _availList[actualKVal]) {
  114. elem = (char *) popAvailList(actualKVal);
  115. break;
  116. }
  117. }
  118. if (!elem) {
  119. if (!_failoverAllocator)
  120. return 0;
  121. return _failoverAllocator->allocate(size);
  122. }
  123. //
  124. // Keep splitting the elem until we are left with a minimally sized elem.
  125. //
  126. // Knuth R3 and R4
  127. //
  128. ESF_ASSERT( actualKVal >= minimumKVal );
  129. AvailListElem *right = (AvailListElem *) elem;
  130. AvailListElem *left = 0;
  131. while (minimumKVal < actualKVal) {
  132. --actualKVal;
  133. left = (AvailListElem *) (elem + (ESF_UWORD_C(1) << actualKVal));
  134. right->_kVal = actualKVal;
  135. left->_linkB = 0;
  136. left->_linkF = 0;
  137. left->_kVal = actualKVal;
  138. left->_tag = 0;
  139. pushAvailList(left);
  140. }
  141. ESF_ASSERT( right );
  142. return (void *) (((char *) right) + sizeof(AvailListElem));
  143. }
  144. ESFError ESFBuddyAllocator::allocate(void **block, ESFUWord size) {
  145. if (!block) {
  146. return ESF_NULL_POINTER;
  147. }
  148. if (0 == size) {
  149. return ESF_INVALID_ARGUMENT;
  150. }
  151. if (!_pool) {
  152. ESFError error = initialize();
  153. if (ESF_SUCCESS != error) {
  154. return error;
  155. }
  156. }
  157. void *tmp = allocate(size);
  158. if (!tmp) {
  159. return ESF_OUT_OF_MEMORY;
  160. }
  161. *block = tmp;
  162. return ESF_SUCCESS;
  163. }
  164. ESFError ESFBuddyAllocator::deallocate(void *block) {
  165. if (!block) {
  166. return ESF_NULL_POINTER;
  167. }
  168. if (!_pool) {
  169. return ESF_INVALID_STATE;
  170. }
  171. char *trueAddress = ((char *) block) - sizeof(AvailListElem);
  172. if (trueAddress < (char *) _pool || trueAddress >= (char *) _pool + (ESF_UWORD_C(1) << _poolKVal)) {
  173. if (_failoverAllocator) {
  174. return _failoverAllocator->deallocate(block);
  175. }
  176. return ESF_NOT_OWNER;
  177. }
  178. //
  179. // Find this block's buddy and check its availability.
  180. //
  181. // Knuth S1
  182. //
  183. AvailListElem *elem = (AvailListElem *) trueAddress;
  184. AvailListElem *buddy = 0;
  185. ESF_ASSERT( 0 == elem->_linkB );
  186. ESF_ASSERT( 0 == elem->_linkF );
  187. ESF_ASSERT( 0 == elem->_tag );
  188. //
  189. // For as long as we can, start coalescing buddies. Two buddies can be
  190. // coalesced iff both are available and both are the same size (kVal).
  191. // If they have different kVals, one of the buddies has been broken up
  192. // into smaller nodes and cannot be coalesced until it is whole again.
  193. //
  194. // Knuth S2 and S3
  195. //
  196. while (true) {
  197. //
  198. // Base Case: We've coalesced everything back into the original block.
  199. //
  200. if (elem->_kVal == _poolKVal) {
  201. ESF_ASSERT( ( char * ) elem == _pool );
  202. pushAvailList(elem);
  203. break;
  204. }
  205. ESFUWord elemAddress = (ESFUWord) (((char *) elem) - (char *) _pool);
  206. if (0 == elemAddress % (ESF_UWORD_C(1) << (elem->_kVal + 1))) {
  207. //
  208. // The buddy is to the right of this node.
  209. //
  210. buddy = (AvailListElem *) (((char *) elem) + (ESF_UWORD_C(1) << elem->_kVal));
  211. ESF_ASSERT( buddy->_kVal <= elem->_kVal );
  212. if (buddy->_tag && buddy->_kVal == elem->_kVal) {
  213. removeFromAvailList(buddy);
  214. //
  215. // Coalesce two buddies. When coalescing two buddies, the
  216. // address of the left buddy becomes the address of the new
  217. // buddy. Also, the size of the new buddy doubles.
  218. //
  219. // elem = elem;
  220. ++elem->_kVal;
  221. continue;
  222. }
  223. //
  224. // The buddy node isn't ready to be coalesced so just add
  225. // the current node to the avail list.
  226. //
  227. pushAvailList(elem);
  228. break;
  229. }
  230. //
  231. // The buddy is to the left of this node.
  232. //
  233. buddy = (AvailListElem *) (((char *) elem) - (ESF_UWORD_C(1) << elem->_kVal));
  234. ESF_ASSERT( buddy->_kVal <= elem->_kVal );
  235. if (buddy->_tag && buddy->_kVal == elem->_kVal) {
  236. removeFromAvailList(buddy);
  237. //
  238. // Coalesce two buddies. When coalescing two buddies, the
  239. // address of the left buddy becomes the address of the new
  240. // buddy. Also, the size of the new buddy doubles.
  241. //
  242. elem = buddy;
  243. ++elem->_kVal;
  244. continue;
  245. }
  246. //
  247. // The buddy node isn't ready to be coalesced so just add
  248. // the current node to the avail list.
  249. //
  250. pushAvailList(elem);
  251. break;
  252. }
  253. return ESF_SUCCESS;
  254. }
  255. ESFUWord ESFBuddyAllocator::getOverhead() {
  256. return sizeof(AvailListElem);
  257. }
  258. ESFError ESFBuddyAllocator::initialize() {
  259. if (_pool || 1 > _poolKVal || ESF_AVAIL_LIST_LENGTH <= _poolKVal) {
  260. return ESF_INVALID_STATE;
  261. }
  262. _pool = _sourceAllocator->allocate(ESF_UWORD_C(1) << _poolKVal);
  263. if (0 == _pool) {
  264. return ESF_OUT_OF_MEMORY;
  265. }
  266. ESF_ASSERT( _pool );
  267. AvailListElem *elem = (AvailListElem *) _pool;
  268. elem->_linkB = 0;
  269. elem->_linkF = 0;
  270. elem->_tag = 1;
  271. elem->_kVal = _poolKVal;
  272. _availList[_poolKVal] = elem;
  273. return ESF_SUCCESS;
  274. }
  275. ESFError ESFBuddyAllocator::destroy() {
  276. if (!_pool) {
  277. return ESF_INVALID_STATE;
  278. }
  279. if (0 == _availList[_poolKVal]) {
  280. return ESF_IN_USE;
  281. }
  282. if (_failoverAllocator) {
  283. ESFError error = _failoverAllocator->destroy();
  284. if (ESF_SUCCESS != error) {
  285. return error;
  286. }
  287. }
  288. _sourceAllocator->deallocate((void *) _pool);
  289. _pool = 0;
  290. return ESF_SUCCESS;
  291. }
  292. ESFError ESFBuddyAllocator::isInitialized() {
  293. return _pool ? ESF_SUCCESS : ESF_NOT_INITIALIZED;
  294. }
  295. ESFError ESFBuddyAllocator::setFailoverAllocator(ESFAllocator *allocator) {
  296. _failoverAllocator = allocator;
  297. return ESF_SUCCESS;
  298. }
  299. ESFError ESFBuddyAllocator::getFailoverAllocator(ESFAllocator **allocator) {
  300. if (!allocator) {
  301. return ESF_NULL_POINTER;
  302. }
  303. *allocator = _failoverAllocator;
  304. return ESF_SUCCESS;
  305. }