PageRenderTime 46ms CodeModel.GetById 25ms app.highlight 19ms RepoModel.GetById 0ms app.codeStats 0ms

/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
 17#ifndef ESF_BUDDY_ALLOCATOR_H
 18#include <ESFBuddyAllocator.h>
 19#endif
 20
 21#ifndef ESF_ASSERT_H
 22#include <ESFAssert.h>
 23#endif
 24
 25ESFBuddyAllocator::KVal ESFBuddyAllocator::GetKVal(ESFUWord requestedSize) {
 26    ESFUWord adjustedSize = requestedSize + sizeof(AvailListElem);
 27    KVal kVal = 1;
 28
 29    while (adjustedSize > (ESF_UWORD_C(1) << kVal)) {
 30        ++kVal;
 31    }
 32
 33    return kVal;
 34}
 35
 36ESFBuddyAllocator::AvailListElem *ESFBuddyAllocator::popAvailList(ESFBuddyAllocator::KVal kVal) {
 37    ESF_ASSERT( kVal < ESF_AVAIL_LIST_LENGTH );
 38
 39    AvailListElem *elem = _availList[kVal];
 40
 41    if (!elem)
 42        return 0;
 43
 44    AvailListElem *next = elem->_linkF;
 45
 46    ESF_ASSERT( 0 == elem->_linkB );
 47    ESF_ASSERT( kVal == elem->_kVal );
 48    ESF_ASSERT( 1 == elem->_tag );
 49
 50    elem->_linkB = 0;
 51    elem->_linkF = 0;
 52    elem->_tag = 0;
 53
 54    if (next) {
 55        next->_linkB = 0;
 56        _availList[kVal] = next;
 57    } else {
 58        _availList[kVal] = 0;
 59    }
 60
 61    return elem;
 62}
 63
 64void ESFBuddyAllocator::removeFromAvailList(ESFBuddyAllocator::AvailListElem *elem) {
 65    ESF_ASSERT( elem );
 66    ESF_ASSERT( elem->_kVal < ESF_AVAIL_LIST_LENGTH );
 67
 68    if (elem->_linkB) {
 69        elem->_linkB->_linkF = elem->_linkF;
 70    }
 71
 72    if (elem->_linkF) {
 73        elem->_linkF->_linkB = elem->_linkB;
 74    }
 75
 76    if (!elem->_linkB) {
 77        _availList[elem->_kVal] = elem->_linkF;
 78    }
 79
 80    elem->_linkB = 0;
 81    elem->_linkF = 0;
 82    elem->_tag = 0;
 83}
 84
 85void ESFBuddyAllocator::pushAvailList(AvailListElem *elem) {
 86    ESF_ASSERT( elem );
 87    ESF_ASSERT( elem->_kVal < ESF_AVAIL_LIST_LENGTH );
 88
 89    AvailListElem *next = _availList[elem->_kVal];
 90
 91    elem->_linkB = 0;
 92    elem->_linkF = next;
 93    elem->_tag = 1;
 94
 95    if (next) {
 96        next->_linkB = elem;
 97    }
 98
 99    _availList[elem->_kVal] = elem;
100}
101
102ESFBuddyAllocator::ESFBuddyAllocator(int size, ESFAllocator *source) :
103    _failoverAllocator(0), _sourceAllocator(source), _pool(0), _poolKVal(0) {
104    if (1 > size || ESF_AVAIL_LIST_LENGTH < size)
105        return;
106
107    if (!_sourceAllocator)
108        return;
109
110    for (int i = 0; i < ESF_AVAIL_LIST_LENGTH; ++i) {
111        _availList[i] = 0;
112    }
113
114    _poolKVal = size;
115    _sourceAllocator = source;
116}
117
118ESFBuddyAllocator::~ESFBuddyAllocator() {
119    destroy();
120}
121
122void *
123ESFBuddyAllocator::allocate(ESFUWord size) {
124    if (0 == size) {
125        return 0;
126    }
127
128    if (!_pool) {
129        if (ESF_SUCCESS != initialize()) {
130            return 0;
131        }
132    }
133
134    //
135    //    Find an element large enough to fulfill the request.  If found, remove
136    //    it from the available list.  If not found, try the failover allocator.
137    //
138    //    Knuth R1 and R2
139    //
140
141    KVal minimumKVal = GetKVal(size);
142    KVal actualKVal = minimumKVal;
143    char *elem = 0;
144
145    for (; actualKVal < ESF_AVAIL_LIST_LENGTH; ++actualKVal) {
146        if (0 != _availList[actualKVal]) {
147            elem = (char *) popAvailList(actualKVal);
148            break;
149        }
150    }
151
152    if (!elem) {
153        if (!_failoverAllocator)
154            return 0;
155
156        return _failoverAllocator->allocate(size);
157    }
158
159    //
160    //    Keep splitting the elem until we are left with a minimally sized elem.
161    //
162    //    Knuth R3 and R4
163    //
164
165    ESF_ASSERT( actualKVal >= minimumKVal );
166
167    AvailListElem *right = (AvailListElem *) elem;
168    AvailListElem *left = 0;
169
170    while (minimumKVal < actualKVal) {
171        --actualKVal;
172
173        left = (AvailListElem *) (elem + (ESF_UWORD_C(1) << actualKVal));
174
175        right->_kVal = actualKVal;
176
177        left->_linkB = 0;
178        left->_linkF = 0;
179        left->_kVal = actualKVal;
180        left->_tag = 0;
181
182        pushAvailList(left);
183    }
184
185    ESF_ASSERT( right );
186
187    return (void *) (((char *) right) + sizeof(AvailListElem));
188}
189
190ESFError ESFBuddyAllocator::allocate(void **block, ESFUWord size) {
191    if (!block) {
192        return ESF_NULL_POINTER;
193    }
194
195    if (0 == size) {
196        return ESF_INVALID_ARGUMENT;
197    }
198
199    if (!_pool) {
200        ESFError error = initialize();
201
202        if (ESF_SUCCESS != error) {
203            return error;
204        }
205    }
206
207    void *tmp = allocate(size);
208
209    if (!tmp) {
210        return ESF_OUT_OF_MEMORY;
211    }
212
213    *block = tmp;
214
215    return ESF_SUCCESS;
216}
217
218ESFError ESFBuddyAllocator::deallocate(void *block) {
219    if (!block) {
220        return ESF_NULL_POINTER;
221    }
222
223    if (!_pool) {
224        return ESF_INVALID_STATE;
225    }
226
227    char *trueAddress = ((char *) block) - sizeof(AvailListElem);
228
229    if (trueAddress < (char *) _pool || trueAddress >= (char *) _pool + (ESF_UWORD_C(1) << _poolKVal)) {
230        if (_failoverAllocator) {
231            return _failoverAllocator->deallocate(block);
232        }
233
234        return ESF_NOT_OWNER;
235    }
236
237    //
238    //    Find this block's buddy and check its availability.
239    //
240    //    Knuth S1
241    //
242
243    AvailListElem *elem = (AvailListElem *) trueAddress;
244    AvailListElem *buddy = 0;
245
246    ESF_ASSERT( 0 == elem->_linkB );
247    ESF_ASSERT( 0 == elem->_linkF );
248    ESF_ASSERT( 0 == elem->_tag );
249
250    //
251    //  For as long as we can, start coalescing buddies.  Two buddies can be
252    //  coalesced iff both are available and both are the same size (kVal).
253    //  If they have different kVals, one of the buddies has been broken up
254    //  into smaller nodes and cannot be coalesced until it is whole again.
255    //
256    //  Knuth S2 and S3
257    //
258
259    while (true) {
260        //
261        //    Base Case:  We've coalesced everything back into the original block.
262        //
263        if (elem->_kVal == _poolKVal) {
264            ESF_ASSERT( ( char * ) elem == _pool );
265
266            pushAvailList(elem);
267
268            break;
269        }
270
271        ESFUWord elemAddress = (ESFUWord) (((char *) elem) - (char *) _pool);
272
273        if (0 == elemAddress % (ESF_UWORD_C(1) << (elem->_kVal + 1))) {
274            //
275            // The buddy is to the right of this node.
276            //
277
278            buddy = (AvailListElem *) (((char *) elem) + (ESF_UWORD_C(1) << elem->_kVal));
279
280            ESF_ASSERT( buddy->_kVal <= elem->_kVal );
281
282            if (buddy->_tag && buddy->_kVal == elem->_kVal) {
283                removeFromAvailList(buddy);
284
285                //
286                //  Coalesce two buddies.  When coalescing two buddies, the
287                //  address of the left buddy becomes the address of the new
288                //  buddy.  Also, the size of the new buddy doubles.
289                //
290
291                // elem = elem;
292
293                ++elem->_kVal;
294
295                continue;
296            }
297
298            //
299            // The buddy node isn't ready to be coalesced so just add
300            // the current node to the avail list.
301            //
302
303            pushAvailList(elem);
304
305            break;
306        }
307
308        //
309        // The buddy is to the left of this node.
310        //
311
312        buddy = (AvailListElem *) (((char *) elem) - (ESF_UWORD_C(1) << elem->_kVal));
313
314        ESF_ASSERT( buddy->_kVal <= elem->_kVal );
315
316        if (buddy->_tag && buddy->_kVal == elem->_kVal) {
317            removeFromAvailList(buddy);
318
319            //
320            //    Coalesce two buddies.  When coalescing two buddies, the
321            //    address of the left buddy becomes the address of the new
322            //    buddy.  Also, the size of the new buddy doubles.
323            //
324
325            elem = buddy;
326
327            ++elem->_kVal;
328
329            continue;
330        }
331
332        //
333        // The buddy node isn't ready to be coalesced so just add
334        // the current node to the avail list.
335        //
336
337        pushAvailList(elem);
338
339        break;
340    }
341
342    return ESF_SUCCESS;
343}
344
345ESFUWord ESFBuddyAllocator::getOverhead() {
346    return sizeof(AvailListElem);
347}
348
349ESFError ESFBuddyAllocator::initialize() {
350    if (_pool || 1 > _poolKVal || ESF_AVAIL_LIST_LENGTH <= _poolKVal) {
351        return ESF_INVALID_STATE;
352    }
353
354    _pool = _sourceAllocator->allocate(ESF_UWORD_C(1) << _poolKVal);
355
356    if (0 == _pool) {
357        return ESF_OUT_OF_MEMORY;
358    }
359
360    ESF_ASSERT( _pool );
361
362    AvailListElem *elem = (AvailListElem *) _pool;
363
364    elem->_linkB = 0;
365    elem->_linkF = 0;
366    elem->_tag = 1;
367    elem->_kVal = _poolKVal;
368
369    _availList[_poolKVal] = elem;
370
371    return ESF_SUCCESS;
372}
373
374ESFError ESFBuddyAllocator::destroy() {
375    if (!_pool) {
376        return ESF_INVALID_STATE;
377    }
378
379    if (0 == _availList[_poolKVal]) {
380        return ESF_IN_USE;
381    }
382
383    if (_failoverAllocator) {
384        ESFError error = _failoverAllocator->destroy();
385
386        if (ESF_SUCCESS != error) {
387            return error;
388        }
389    }
390
391    _sourceAllocator->deallocate((void *) _pool);
392    _pool = 0;
393
394    return ESF_SUCCESS;
395}
396
397ESFError ESFBuddyAllocator::isInitialized() {
398    return _pool ? ESF_SUCCESS : ESF_NOT_INITIALIZED;
399}
400
401ESFError ESFBuddyAllocator::setFailoverAllocator(ESFAllocator *allocator) {
402    _failoverAllocator = allocator;
403
404    return ESF_SUCCESS;
405}
406
407ESFError ESFBuddyAllocator::getFailoverAllocator(ESFAllocator **allocator) {
408    if (!allocator) {
409        return ESF_NULL_POINTER;
410    }
411
412    *allocator = _failoverAllocator;
413
414    return ESF_SUCCESS;
415}