diff --git a/storage/ndb/src/kernel/vm/LinearPool.hpp b/storage/ndb/src/kernel/vm/LinearPool.hpp new file mode 100644 index 00000000000..188185701f5 --- /dev/null +++ b/storage/ndb/src/kernel/vm/LinearPool.hpp @@ -0,0 +1,480 @@ +/* Copyright (C) 2003 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ + +#ifndef LINEAR_POOL_HPP +#define LINEAR_POOL_HPP + +#include + +/* + * LinearPool - indexed record pool + * + * LinearPool implements a pool where each record has a 0-based index. + * Any index value (up to 2^32-1) is allowed. Normal efficient usage is + * to assign index values in sequence and to re-use any values which + * have become free. This is default seize/release behaviour. + * + * LinearPool has 2 internal RecordPool instances: + * + * (a) record pool of T (the template argument class) + * (b) record pool of "maps" (array of Uint32) + * + * The maps translate an index into an i-value in (a). Each map has + * a level. Level 0 maps point to i-values. Level N+1 maps point to + * level N maps. There is a unique "root map" at top. + * + * This works exactly like numbers in a given base. Each map has base + * size entries. For implementation convenience the base must be power + * of 2 and is given as its log2 value. + * + * There is a doubly linked list of available maps (some free entries) + * on each level. There is a singly linked free list within each map. + * + * Level 0 free entry has space for one record. Level N free entry + * implies space for base^N records. The implied levels are created and + * removed on demand. Completely empty maps are removed. + * + * Default base is 256 (log2 = 8) which requires maximum 4 levels or + * "digits" (similar to ip address). + * + * TODO + * + * - move most of the inline code to LinearPool.cpp + * - add methods to check / seize user-specified index + */ + +#include "SuperPool.hpp" + +template +class LinearPool { + typedef SuperPool::PtrI PtrI; + + // Base. + STATIC_CONST( Base = 1 << LogBase ); + + // Digit mask. + STATIC_CONST( DigitMask = Base - 1 ); + + // Max possible levels (0 to max root level). + STATIC_CONST( MaxLevels = (32 + LogBase - 1) / LogBase ); + + // Map. + struct Map { + Uint32 m_level; + Uint32 m_occup; // number of used entries + Uint32 m_firstfree; // position of first free entry + PtrI m_parent; // parent map + Uint32 m_index; // from root to here + PtrI m_nextavail; + PtrI m_prevavail; + PtrI m_entry[Base]; + }; + +public: + + // Constructor. + LinearPool(GroupPool& gp); + + // Destructor. + ~LinearPool(); + + // Update pointer ptr.p according to index value ptr.i. + void getPtr(Ptr& ptr); + + // Allocate record from the pool. Reuses free index if possible. + bool seize(Ptr& ptr); + + // Return record to the pool. + void release(Ptr& ptr); + + // Verify (debugging). + void verify(); + +private: + + // Given index find the bottom map. + void get_map(Ptr& map_ptr, Uint32 index); + + // Add new root map and increase level + bool add_root(); + + // Add new non-root map. + bool add_map(Ptr& map_ptr, Ptr parent_ptr, Uint32 digit); + + // Remove entry and map if it becomes empty. + void remove_entry(Ptr map_ptr, Uint32 digit); + + // Remove map and all parents which become empty. + void remove_map(Ptr map_ptr); + + // Add map to available list. + void add_avail(Ptr map_ptr); + + // Remove map from available list. + void remove_avail(Ptr map_ptr); + + // Verify map (recursive). + void verify(Ptr map_ptr, Uint32 level); + + RecordPool m_records; + RecordPool m_maps; + Uint32 m_levels; // 0 means empty pool + PtrI m_root; + PtrI m_avail[MaxLevels]; +}; + +template +inline +LinearPool::LinearPool(GroupPool& gp) : + m_records(gp), + m_maps(gp), + m_levels(0), + m_root(RNIL) +{ + Uint32 n; + for (n = 0; n < MaxLevels; n++) + m_avail[n] = RNIL; +} + +template +inline +LinearPool::~LinearPool() +{ +} + +template +inline void +LinearPool::getPtr(Ptr& ptr) +{ + Uint32 index = ptr.i; + // get level 0 map + Ptr map_ptr; + get_map(map_ptr, index); + // get record + Ptr rec_ptr; + Uint32 digit = index & DigitMask; + rec_ptr.i = map_ptr.p->m_entry[digit]; + m_records.getPtr(rec_ptr); + ptr.p = rec_ptr.p; +} + +template +inline bool +LinearPool::seize(Ptr& ptr) +{ + // look for free list on some level + Ptr map_ptr; + map_ptr.i = RNIL; + Uint32 n = 0; + while (n < m_levels) { + if ((map_ptr.i = m_avail[n]) != RNIL) + break; + n++; + } + if (map_ptr.i == RNIL) { + // add new level with available maps + if (! add_root()) + return false; + assert(n < m_levels); + map_ptr.i = m_avail[n]; + } + m_maps.getPtr(map_ptr); + // walk down creating missing levels and using an entry on each + Uint32 firstfree; + while (true) { + assert(map_ptr.p->m_occup < Base); + map_ptr.p->m_occup++; + firstfree = map_ptr.p->m_firstfree; + assert(firstfree < Base); + map_ptr.p->m_firstfree = map_ptr.p->m_entry[firstfree]; + if (map_ptr.p->m_occup == Base) { + assert(map_ptr.p->m_firstfree == Base); + // remove from available list + remove_avail(map_ptr); + } + if (n == 0) + break; + Ptr child_ptr; + if (! add_map(child_ptr, map_ptr, firstfree)) { + remove_entry(map_ptr, firstfree); + return false; + } + map_ptr.p->m_entry[firstfree] = child_ptr.i; + map_ptr = child_ptr; + n--; + } + // now on level 0 + assert(map_ptr.p->m_level == 0); + Ptr rec_ptr; + if (! m_records.seize(rec_ptr)) { + remove_entry(map_ptr, firstfree); + return false; + } + map_ptr.p->m_entry[firstfree] = rec_ptr.i; + ptr.i = firstfree + (map_ptr.p->m_index << LogBase); + ptr.p = rec_ptr.p; + return true; +} + +template +inline void +LinearPool::release(Ptr& ptr) +{ + Uint32 index = ptr.i; + // get level 0 map + Ptr map_ptr; + get_map(map_ptr, index); + // release record + Ptr rec_ptr; + Uint32 digit = index & DigitMask; + rec_ptr.i = map_ptr.p->m_entry[digit]; + m_records.release(rec_ptr); + // remove entry + remove_entry(map_ptr, digit); + // null pointer + ptr.i = RNIL; + ptr.p = 0; +} + +template +inline void +LinearPool::verify() +{ + if (m_root == RNIL) + return; + assert(m_levels != 0); + Ptr map_ptr; + map_ptr.i = m_root; + m_maps.getPtr(map_ptr); + verify(map_ptr, m_levels - 1); +} + +// private methods + +template +inline void +LinearPool::get_map(Ptr& map_ptr, Uint32 index) +{ + // root map must exist + Ptr tmp_ptr; + tmp_ptr.i = m_root; + m_maps.getPtr(tmp_ptr); + assert(tmp_ptr.p->m_level + 1 == m_levels); + // extract index digits up to current root level + Uint32 digits[MaxLevels]; + Uint32 n = 0; + do { + digits[n] = index & DigitMask; + index >>= LogBase; + } while (++n < m_levels); + assert(index == 0); + // walk down indirect levels + while (--n > 0) { + tmp_ptr.i = tmp_ptr.p->m_entry[digits[n]]; + m_maps.getPtr(tmp_ptr); + } + // level 0 map + assert(tmp_ptr.p->m_level == 0); + map_ptr = tmp_ptr; +} + +template +inline bool +LinearPool::add_root() +{ + // new root + Ptr map_ptr; + if (! m_maps.seize(map_ptr)) + return false; + Uint32 n = m_levels++; + assert(n < MaxLevels); + // set up + map_ptr.p->m_level = n; + if (n == 0) { + map_ptr.p->m_occup = 0; + map_ptr.p->m_firstfree = 0; + } else { + // on level > 0 digit 0 points to old root + map_ptr.p->m_occup = 1; + map_ptr.p->m_firstfree = 1; + Ptr old_ptr; + old_ptr.i = m_root; + m_maps.getPtr(old_ptr); + assert(old_ptr.p->m_parent == RNIL); + old_ptr.p->m_parent = map_ptr.i; + map_ptr.p->m_entry[0] = old_ptr.i; + } + // set up free list with Base as terminator + for (Uint32 j = map_ptr.p->m_firstfree; j < Base; j++) + map_ptr.p->m_entry[j] = j + 1; + map_ptr.p->m_parent = RNIL; + map_ptr.p->m_index = 0; + add_avail(map_ptr); + // set new root + m_root = map_ptr.i; + return true; +} + +template +inline bool +LinearPool::add_map(Ptr& map_ptr, Ptr parent_ptr, Uint32 digit) +{ + if (! m_maps.seize(map_ptr)) + return false; + assert(parent_ptr.p->m_level != 0); + // set up + map_ptr.p->m_level = parent_ptr.p->m_level - 1; + map_ptr.p->m_occup = 0; + map_ptr.p->m_firstfree = 0; + // set up free list with Base as terminator + for (Uint32 j = map_ptr.p->m_firstfree; j < Base; j++) + map_ptr.p->m_entry[j] = j + 1; + map_ptr.p->m_parent = parent_ptr.i; + map_ptr.p->m_index = digit + (parent_ptr.p->m_index << LogBase); + add_avail(map_ptr); + return true; +} + +template +inline void +LinearPool::remove_entry(Ptr map_ptr, Uint32 digit) +{ + assert(map_ptr.p->m_occup != 0 && digit < Base); + map_ptr.p->m_occup--; + map_ptr.p->m_entry[digit] = map_ptr.p->m_firstfree; + map_ptr.p->m_firstfree = digit; + if (map_ptr.p->m_occup + 1 == Base) + add_avail(map_ptr); + else if (map_ptr.p->m_occup == 0) + remove_map(map_ptr); +} + +template +inline void +LinearPool::remove_map(Ptr map_ptr) +{ + assert(map_ptr.p->m_occup == 0); + remove_avail(map_ptr); + Ptr parent_ptr; + parent_ptr.i = map_ptr.p->m_parent; + Uint32 digit = map_ptr.p->m_index & DigitMask; + PtrI map_ptr_i = map_ptr.i; + m_maps.release(map_ptr); + if (m_root == map_ptr_i) { + assert(parent_ptr.i == RNIL); + Uint32 used = m_maps.m_superPool.getRecUseCount(m_maps.m_recInfo); + assert(used == 0); + m_root = RNIL; + m_levels = 0; + } + if (parent_ptr.i != RNIL) { + m_maps.getPtr(parent_ptr); + // remove child entry (recursive) + remove_entry(parent_ptr, digit); + } +} + +template +inline void +LinearPool::add_avail(Ptr map_ptr) +{ + Uint32 n = map_ptr.p->m_level; + assert(n < m_levels); + map_ptr.p->m_nextavail = m_avail[n]; + if (map_ptr.p->m_nextavail != RNIL) { + Ptr next_ptr; + next_ptr.i = map_ptr.p->m_nextavail; + m_maps.getPtr(next_ptr); + next_ptr.p->m_prevavail = map_ptr.i; + } + map_ptr.p->m_prevavail = RNIL; + m_avail[n] = map_ptr.i; +} + +template +inline void +LinearPool::remove_avail(Ptr map_ptr) +{ + Uint32 n = map_ptr.p->m_level; + assert(n < m_levels); + if (map_ptr.p->m_nextavail != RNIL) { + Ptr next_ptr; + next_ptr.i = map_ptr.p->m_nextavail; + m_maps.getPtr(next_ptr); + next_ptr.p->m_prevavail = map_ptr.p->m_prevavail; + } + if (map_ptr.p->m_prevavail != RNIL) { + Ptr prev_ptr; + prev_ptr.i = map_ptr.p->m_prevavail; + m_maps.getPtr(prev_ptr); + prev_ptr.p->m_nextavail = map_ptr.p->m_nextavail; + } + if (map_ptr.p->m_prevavail == RNIL) { + m_avail[n] = map_ptr.p->m_nextavail; + } + map_ptr.p->m_nextavail = RNIL; + map_ptr.p->m_prevavail = RNIL; +} + +template +inline void +LinearPool::verify(Ptr map_ptr, Uint32 level) +{ + assert(level < MaxLevels); + assert(map_ptr.p->m_level == level); + Uint32 j = 0; + while (j < Base) { + bool free = false; + Uint32 j2 = map_ptr.p->m_firstfree; + while (j2 != Base) { + if (j2 == j) { + free = true; + break; + } + assert(j2 < Base); + j2 = map_ptr.p->m_entry[j2]; + } + if (! free) { + if (level != 0) { + Ptr child_ptr; + child_ptr.i = map_ptr.p->m_entry[j]; + m_maps.getPtr(child_ptr); + assert(child_ptr.p->m_parent == map_ptr.i); + assert(child_ptr.p->m_index == j + (map_ptr.p->m_index << LogBase)); + verify(child_ptr, level - 1); + } else { + Ptr rec_ptr; + rec_ptr.i = map_ptr.p->m_entry[j]; + m_records.getPtr(rec_ptr); + } + Ptr avail_ptr; + avail_ptr.i = m_avail[map_ptr.p->m_level]; + bool found = false; + while (avail_ptr.i != RNIL) { + if (avail_ptr.i == map_ptr.i) { + found = true; + break; + } + m_maps.getPtr(avail_ptr); + avail_ptr.i = avail_ptr.p->m_nextavail; + } + assert(found == (map_ptr.p->m_occup < Base)); + } + j++; + } +} + +#endif diff --git a/storage/ndb/src/kernel/vm/testSuperPool.cpp b/storage/ndb/src/kernel/vm/testSuperPool.cpp index bb2c2f2be42..78a1a29c883 100644 --- a/storage/ndb/src/kernel/vm/testSuperPool.cpp +++ b/storage/ndb/src/kernel/vm/testSuperPool.cpp @@ -23,6 +23,7 @@ exit $? Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "SuperPool.hpp" +#include "LinearPool.hpp" #include template @@ -198,6 +199,74 @@ sp_test(GroupPool& gp) delete [] ptrList; } +template +static void +lp_test(GroupPool& gp) +{ + SuperPool& sp = gp.m_superPool; + LinearPool lp(gp); + ndbout << "linear pool test" << endl; + Ptr ptr; + Uint32 loop; + for (loop = 0; loop < 3 * loopcount; loop++) { + int count = 0; + while (1) { + bool ret = lp.seize(ptr); + lp.verify(); + if (! ret) + break; + assert(ptr.i == count); + Ptr ptr2; + ptr2.i = ptr.i; + ptr2.p = 0; + lp.getPtr(ptr2); + assert(ptr.p == ptr2.p); + count++; + } + ndbout << "seized " << count << endl; + switch (loop % 3) { + case 0: + { + int n = 0; + while (n < count) { + ptr.i = n; + lp.release(ptr); + lp.verify(); + n++; + } + ndbout << "released in order" << endl; + } + break; + case 1: + { + int n = count; + while (n > 0) { + n--; + ptr.i = n; + lp.release(ptr); + lp.verify(); + } + ndbout << "released in reverse" << endl; + } + break; + default: + { + int coprime = random_coprime(count); + int n = 0; + while (n < count) { + int m = (coprime * n) % count; + ptr.i = m; + lp.release(ptr); + lp.verify(); + n++; + } + ndbout << "released at random" << endl; + } + break; + } + } +} + static Uint32 pageSize = 32768; static Uint32 pageBits = 17; @@ -218,6 +287,8 @@ template static void sp_test(GroupPool& sp); template static void sp_test(GroupPool& sp); template static void sp_test(GroupPool& sp); template static void sp_test(GroupPool& sp); +// +template static void lp_test(GroupPool& sp); int main() @@ -231,13 +302,18 @@ main() Uint16 s = (Uint16)getpid(); srandom(s); ndbout << "rand " << s << endl; - int count = 0; - while (++count <= 1) { + int count; + count = 0; + while (++count <= 0) { sp_test(gp); sp_test(gp); sp_test(gp); sp_test(gp); sp_test(gp); } + count = 0; + while (++count <= 1) { + lp_test(gp); + } return 0; }