![]() |
Shadowrun: Awakened 29 September 2011 - Build 871
|
00001 /* 00002 Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved. 00003 00004 Redistribution and use in source and binary forms, with or without 00005 modification, are permitted provided that the following conditions are met: 00006 00007 * Redistributions of source code must retain the above copyright notice, 00008 this list of conditions and the following disclaimer. 00009 * Redistributions in binary form must reproduce the above copyright notice, 00010 this list of conditions and the following disclaimer in the documentation 00011 and/or other materials provided with the distribution. 00012 * Neither the name of LibCat nor the names of its contributors may be used 00013 to endorse or promote products derived from this software without 00014 specific prior written permission. 00015 00016 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 00017 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00018 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 00019 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 00020 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 00021 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 00022 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 00023 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 00024 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 00025 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00026 POSSIBILITY OF SUCH DAMAGE. 00027 */ 00028 00029 #ifndef CAT_SPHYNX_COLLEXION_HPP 00030 #define CAT_SPHYNX_COLLEXION_HPP 00031 00032 #include <cat/threads/Mutex.hpp> 00033 #include <cat/threads/RegionAllocator.hpp> 00034 #include <cat/net/SphynxTransport.hpp> 00035 00036 namespace cat { 00037 00038 00039 namespace sphynx { 00040 00041 00042 /* 00043 The purpose of sphynx::Collexion and sphynx::CollexionIterator is to 00044 store lists of Connexion object references and iterate through them. 00045 00046 Since the number of clients may be in the thousands, I feel it is 00047 important to scale effectively. So in the Collexion data structure, 00048 insertion and removal are O(1) operations. Also locks should be held 00049 for the smallest amount of time possible, so I have taken care to make 00050 locks short and reduce the amount of locking. For example, the iterator 00051 caches large blocks of data instead of locking for each iteration. 00052 00053 The design is optimized for cache usage, re-using common code to benefit 00054 from code cache and allocating and accessing table entries on cache line 00055 boundaries to double memory performance over a naive approach. 00056 */ 00057 00058 00060 00061 template<class T> 00062 class CollexionIterator; 00063 00064 template<class T> 00065 struct CollexionElement 00066 { 00067 // Number of active enumerators using this element 00068 // If references are held it cannot be deleted 00069 // so the KILL flag is set on the 'next' member and 00070 // the final enumerator to reduce the reference count 00071 // to zero is responsible for removal. 00072 u32 refcnt; 00073 00074 // Bitfield: 00075 // 1 bit: COLLISION FLAG 00076 // 1 bit: KILL FLAG 00077 // 30 bits: Table index to next element in list + 1 00078 u32 next; 00079 00080 // Data at this table element 00081 T *conn; 00082 }; 00083 00084 struct CollexionElement2 00085 { 00086 // Previous table element in list + 1 00087 u32 last; 00088 00089 // Hash of data pointer from main entry (so it doesn't need to be recalculated during growth) 00090 u32 hash; 00091 }; 00092 00093 template<class T> 00094 class Collexion 00095 { 00096 static const u32 COLLIDE_MASK = 0x80000000; 00097 static const u32 KILL_MASK = 0x40000000; 00098 static const u32 NEXT_MASK = 0x3fffffff; 00099 static const u32 MIN_ALLOCATED = 32; 00100 00101 private: 00102 // Number of used table elements 00103 u32 _used; 00104 00105 // Number of allocated table elements 00106 u32 _allocated; 00107 00108 // First table index in list of active elements 00109 u32 _first; 00110 00111 // Primary table 00112 CollexionElement<T> *_table; 00113 00114 // Secondary table, split off so that primary table elements will 00115 // fit on a cache line. Contains data that is only accessed rarely. 00116 CollexionElement2 *_table2; 00117 00118 // Table lock 00119 Mutex _lock; 00120 00121 protected: 00122 // Attempt to double size of hash table (does not hold lock) 00123 bool DoubleTable(); 00124 00125 // Hash a pointer to a 32-bit table key 00126 static CAT_INLINE u32 HashPtr(T *ptr) 00127 { 00128 u64 key = 0xBADdecafDEADbeef; 00129 00130 #if defined(CAT_WORD_64) 00131 key ^= *(u64*)&ptr; 00132 #else 00133 key ^= *(u32*)&ptr; 00134 #endif 00135 00136 key = (~key) + (key << 18); 00137 key = key ^ (key >> 31); 00138 key = key * 21; 00139 key = key ^ (key >> 11); 00140 key = key + (key << 6); 00141 key = key ^ (key >> 22); 00142 return (u32)key; 00143 } 00144 00145 // Common functions shared by interface for good code cache usage: 00146 00147 // Unlink a table key 00148 void Unlink(u32 key); 00149 00150 // Fill an iterator with the next set of data 00151 // Returns false if no data remains to fill 00152 void Fill(CollexionIterator<T> &iter, u32 first); 00153 00154 // Find a hash table key based on data 00155 u32 Find(T *conn); 00156 00157 public: 00158 // Ctor zeros everything 00159 Collexion() 00160 { 00161 _first = 0; 00162 _used = 0; 00163 _allocated = 0; 00164 _table = 0; 00165 _table2 = 0; 00166 } 00167 00168 // Dtor releases dangling memory 00169 ~Collexion(); 00170 00171 // Returns true if table is empty 00172 CAT_INLINE bool IsEmpty() { return _used == 0; } 00173 00174 // Insert Connexion object, return false if already present or out of memory 00175 bool Insert(T *conn); 00176 00177 // Remove Connexion object from list if it exists 00178 bool Remove(T *conn); 00179 00180 // Begin iterating through list 00181 void Begin(CollexionIterator<T> &iter); 00182 00183 // Iterate 00184 void Next(CollexionIterator<T> &iter, bool refill = true); 00185 }; 00186 00187 00189 00190 template<class T> 00191 class CollexionIterator 00192 { 00193 friend class Collexion<T>; 00194 00195 static const u32 MAX_CACHE = 256; 00196 00197 // Parent Collexion object 00198 Collexion<T> *_parent; 00199 00200 // First and last hash table indices in parent 00201 u32 _first, _last; 00202 00203 // Stores the size of the parent when snapshot was taken, 00204 // will invalidate _first and _last if table changed size 00205 u32 _prev_allocated; 00206 00207 // Connexion object cache, to avoid locking and unlocking a lot 00208 T *_cache[MAX_CACHE]; 00209 00210 // Offset into cache and total elements in cache 00211 // Will grab another cache once offset reaches total 00212 u32 _offset, _total; 00213 00214 public: 00215 // Smart pointer -style accessors 00216 CAT_INLINE T *Get() throw() { return _cache[_offset]; } 00217 CAT_INLINE T *operator->() throw() { return _cache[_offset]; } 00218 CAT_INLINE T &operator*() throw() { return *_cache[_offset]; } 00219 CAT_INLINE operator T*() { return _cache[_offset]; } 00220 00221 public: 00222 // Ctor: Grabs first cache of Connexions 00223 CollexionIterator(Collexion<T> &begin); 00224 00225 // Dtor: Calls Release() 00226 ~CollexionIterator(); 00227 00228 // Iterate to next Connexion object in list 00229 CollexionIterator &operator++(); 00230 00231 // Releases reference to any outstanding Connexions 00232 void Release(); 00233 }; 00234 00235 00237 00238 template<class T> 00239 bool Collexion<T>::DoubleTable() 00240 { 00241 u32 new_allocated = _allocated << 1; 00242 if (new_allocated < MIN_ALLOCATED) new_allocated = MIN_ALLOCATED; 00243 00244 // Allocate secondary table 00245 u32 new_bytes2 = sizeof(CollexionElement2) * new_allocated; 00246 CollexionElement2 *new_table2 = reinterpret_cast<CollexionElement2*>( 00247 RegionAllocator::ii->Acquire(new_bytes2) ); 00248 00249 if (!new_table2) return false; 00250 00251 // Allocate primary table 00252 u32 new_bytes = sizeof(CollexionElement<T>) * new_allocated; 00253 CollexionElement<T> *new_table = reinterpret_cast<CollexionElement<T> *>( 00254 RegionAllocator::ii->Acquire(new_bytes) ); 00255 00256 if (!new_table) 00257 { 00258 RegionAllocator::ii->Release(new_table2); 00259 return false; 00260 } 00261 00262 CAT_CLR(new_table, new_bytes); 00263 00264 u32 new_first = 0; 00265 00266 if (_table && _table2) 00267 { 00268 // For each entry in the old table, 00269 u32 ii = _first, mask = _allocated - 1; 00270 00271 while (ii) 00272 { 00273 --ii; 00274 CollexionElement<T> *oe = &_table[ii]; 00275 u32 hash = _table2[ii].hash; 00276 u32 key = hash & mask; 00277 00278 // While collisions occur, 00279 while (new_table[key].conn) 00280 { 00281 // Mark collision 00282 new_table[key].next |= COLLIDE_MASK; 00283 00284 // Walk collision list 00285 key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask; 00286 } 00287 00288 // Fill new table element 00289 new_table[key].conn = oe->conn; 00290 new_table2[key].hash = hash; 00291 // new_table[key].refcnt is already zero 00292 00293 // Link new element to new list 00294 if (new_first) 00295 { 00296 new_table[key].next |= new_first; 00297 new_table2[new_first - 1].last = key; 00298 } 00299 // new_table[key].next is already zero so no need to zero it here 00300 new_first = key + 1; 00301 00302 // Get next old table entry 00303 ii = oe->next & NEXT_MASK; 00304 } 00305 00306 // Zero head->last 00307 new_table2[new_first - 1].last = 0; 00308 } 00309 00310 // Resulting linked list starting with _first-1 will extend until e->next == 0 00311 00312 if (_table2) RegionAllocator::ii->Release(_table2); 00313 if (_table) RegionAllocator::ii->Release(_table); 00314 00315 _table = new_table; 00316 _table2 = new_table2; 00317 _allocated = new_allocated; 00318 _first = new_first; 00319 return true; 00320 } 00321 00322 template<class T> 00323 Collexion<T>::~Collexion() 00324 { 00325 if (_table2) 00326 { 00327 RegionAllocator::ii->Release(_table2); 00328 } 00329 00330 // If table doesn't exist, return 00331 if (!_table) return; 00332 00333 // For each allocated table entry, 00334 for (u32 ii = 0; ii < _allocated; ++ii) 00335 { 00336 // Get Connexion object 00337 T *conn = _table[ii].conn; 00338 00339 // If object is valid, release it 00340 if (conn) conn->ReleaseRef(); 00341 } 00342 00343 // Release table memory 00344 RegionAllocator::ii->Release(_table); 00345 } 00346 00347 template<class T> 00348 bool Collexion<T>::Insert(T *conn) 00349 { 00350 u32 hash = HashPtr(conn); 00351 conn->AddRef(); 00352 00353 AutoMutex lock(_lock); 00354 00355 // If more than half of the table will be used, 00356 if (_used >= (_allocated >> 1)) 00357 { 00358 // Double the size of the table (O(1) allocation pattern) 00359 // Growing pains are softened by careful design 00360 if (!DoubleTable()) 00361 { 00362 // On growth failure, return false 00363 lock.Release(); 00364 00365 conn->ReleaseRef(); 00366 00367 return false; 00368 } 00369 } 00370 00371 // Mask off high bits to make table key from hash 00372 u32 mask = _allocated - 1; 00373 u32 key = hash & mask; 00374 00375 // While empty table entry not found, 00376 while (_table[key].conn) 00377 { 00378 // If Connexion object is already in the table, 00379 if (_table[key].conn == conn) 00380 { 00381 // Return false on duplicate 00382 lock.Release(); 00383 00384 conn->ReleaseRef(); 00385 00386 return false; 00387 } 00388 00389 // Mark as a collision 00390 _table[key].next |= COLLIDE_MASK; 00391 00392 // Walk collision list 00393 key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask; 00394 } 00395 00396 // Fill new element 00397 _table[key].conn = conn; 00398 _table[key].refcnt = 0; 00399 _table2[key].hash = hash; 00400 _table2[key].last = 0; 00401 00402 // Link new element to front of list 00403 if (_first) _table2[_first - 1].last = key + 1; 00404 _table[key].next = (_table[key].next & COLLIDE_MASK) | _first; 00405 _first = key + 1; 00406 00407 ++_used; 00408 return true; 00409 } 00410 00411 template<class T> 00412 void Collexion<T>::Unlink(u32 key) 00413 { 00414 // Clear reference 00415 _table[key].conn = 0; 00416 00417 // Unlink from active list 00418 u32 next = _table[key].next & NEXT_MASK; 00419 u32 last = _table2[key].last; 00420 00421 if (last) _table[last-1].next = (_table[last-1].next & ~NEXT_MASK) | next; 00422 else _first = next; 00423 if (next) _table2[next-1].last = last; 00424 00425 // If this key was a leaf on a collision wind, 00426 if (!(_table[key].next & COLLIDE_MASK)) 00427 { 00428 u32 mask = _allocated - 1; 00429 00430 do 00431 { 00432 // Go backwards through the collision list one step 00433 key = ((key + COLLISION_INCRINVERSE) * COLLISION_MULTINVERSE) & mask; 00434 00435 // Stop where collision list stops 00436 if (!(_table[key].next & COLLIDE_MASK)) 00437 break; 00438 00439 // Turn off collision key for previous entry 00440 _table[key].next &= ~COLLIDE_MASK; 00441 00442 } while (!_table[key].conn); 00443 } 00444 00445 // Update number of used elements 00446 --_used; 00447 } 00448 00449 template<class T> 00450 bool Collexion<T>::Remove(T *conn) 00451 { 00452 u32 hash = HashPtr(conn); 00453 00454 AutoMutex lock(_lock); 00455 00456 // If table doesn't exist, 00457 if (!_allocated) return false; 00458 00459 // Mask off high bits to make table key from hash 00460 u32 mask = _allocated - 1; 00461 u32 key = hash & mask; 00462 00463 // While target table entry not found, 00464 for (;;) 00465 { 00466 // If target was found, 00467 if (_table[key].conn == conn) 00468 { 00469 if (_table[key].refcnt) 00470 { 00471 // Mark it killed so iterator can clean it up when it's finished 00472 _table[key].next |= KILL_MASK; 00473 } 00474 else 00475 { 00476 Unlink(key); 00477 00478 lock.Release(); 00479 00480 conn->ReleaseRef(); 00481 } 00482 00483 // Return success 00484 return true; 00485 } 00486 00487 if (!(_table[key].next & COLLIDE_MASK)) 00488 { 00489 break; // End of collision list 00490 } 00491 00492 // Walk collision list 00493 key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask; 00494 } 00495 00496 // Return failure: not found 00497 return false; 00498 } 00499 00500 template<class T> 00501 void Collexion<T>::Fill(CollexionIterator<T> &iter, u32 first) 00502 { 00503 u32 key = first; 00504 00505 // Find first list element that does not want to die 00506 while (key && (_table[key-1].next & KILL_MASK)) 00507 { 00508 // Go to next 00509 key = _table[key-1].next & NEXT_MASK; 00510 } 00511 00512 iter._offset = 0; 00513 00514 // If no elements in table, 00515 if (!key) 00516 { 00517 // Return empty set 00518 iter._cache[0] = 0; 00519 iter._total = 0; 00520 iter._first = 0; 00521 iter._last = 0; 00522 return; 00523 } 00524 00525 // Remember size of hash table in case it grows before iterator is done 00526 iter._prev_allocated = _allocated; 00527 00528 // Remember first key for next iteration 00529 iter._first = key; 00530 00531 // For each of the first MAX_CACHE elements in the table, copy the data pointer to cache 00532 u32 ii = 0, final = 0; 00533 00534 do 00535 { 00536 // If element does not want to die, 00537 if (!(_table[key-1].next & KILL_MASK)) 00538 { 00539 // Copy data pointer 00540 iter._cache[ii] = _table[key-1].conn; 00541 00542 // Increment reference count for element 00543 _table[key-1].refcnt++; 00544 00545 // Remember key as the next iteration starting point 00546 final = key; 00547 00548 // Check if copy is complete 00549 if (++ii >= CollexionIterator<T>::MAX_CACHE) break; 00550 } 00551 00552 // Go to next key 00553 key = _table[key-1].next & NEXT_MASK; 00554 00555 } while (key); 00556 00557 // Record number of elements written 00558 iter._total = ii; 00559 00560 // Remember next key for next iteration 00561 iter._last = final; 00562 } 00563 00564 template<class T> 00565 void Collexion<T>::Begin(CollexionIterator<T> &iter) 00566 { 00567 iter._parent = this; 00568 00569 AutoMutex lock(_lock); 00570 00571 Fill(iter, _first); 00572 } 00573 00574 // Find a hash table key based on data 00575 template<class T> 00576 u32 Collexion<T>::Find(T *conn) 00577 { 00578 u32 mask = _allocated - 1; 00579 u32 key = HashPtr(conn) & mask; 00580 00581 // Find the object in the collision list starting at the expected location 00582 while (_table[key].conn != conn) 00583 { 00584 // If at the end of the collision list, 00585 if (!(_table[key].next & COLLIDE_MASK)) 00586 break; // Should never happen 00587 00588 // Walk collision list 00589 key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask; 00590 } 00591 00592 return key; 00593 } 00594 00595 template<class T> 00596 void Collexion<T>::Next(CollexionIterator<T> &iter, bool refill) 00597 { 00598 T *release_list[CollexionIterator<T>::MAX_CACHE]; 00599 u32 release_ii = 0; 00600 00601 u32 key = iter._first; 00602 u32 last = iter._last; 00603 00604 // If iteration is done, 00605 if (!key) return; 00606 00607 AutoMutex lock(_lock); 00608 00609 // If hash table changed size (rare), 00610 if (iter._prev_allocated != _allocated) 00611 { 00612 // iter._first and iter._last are invalid 00613 // ...but we can find them again based on the cached pointers! 00614 key = Find(iter._cache[0]) + 1; 00615 last = Find(iter._cache[iter._total - 1]) + 1; 00616 } 00617 00618 last = _table[last-1].next & NEXT_MASK; 00619 00620 // Release any table elements that want to die now 00621 do 00622 { 00623 u32 flags = _table[key-1].next; 00624 00625 // If reference count is now zero for this element, 00626 if (0 == --_table[key-1].refcnt) 00627 { 00628 // If element wants to die, 00629 if (flags & KILL_MASK) 00630 { 00631 // Prepare to release data after lock is released 00632 release_list[release_ii++] = _table[key-1].conn; 00633 00634 Unlink(key-1); 00635 } 00636 } 00637 00638 key = flags & NEXT_MASK; 00639 00640 } while (key != last); 00641 00642 // Fill iterator starting with next key 00643 if (refill) Fill(iter, key); 00644 00645 lock.Release(); 00646 00647 if (!refill) 00648 { 00649 // Return empty set 00650 iter._cache[0] = 0; 00651 iter._total = 0; 00652 iter._offset = 0; 00653 iter._first = 0; 00654 iter._last = 0; 00655 } 00656 00657 // Release data awaiting destruction 00658 for (u32 ii = 0; ii < release_ii; ++ii) 00659 { 00660 release_list[ii]->ReleaseRef(); 00661 } 00662 } 00663 00664 00666 00667 template<class T> 00668 CollexionIterator<T>::CollexionIterator(Collexion<T> &begin) 00669 { 00670 begin.Begin(*this); 00671 } 00672 00673 template<class T> 00674 CollexionIterator<T>::~CollexionIterator() 00675 { 00676 Release(); 00677 } 00678 00679 template<class T> 00680 CollexionIterator<T> &CollexionIterator<T>::operator++() 00681 { 00682 if (++_offset >= _total) 00683 { 00684 _parent->Next(*this, true); 00685 } 00686 00687 return *this; 00688 } 00689 00690 template<class T> 00691 void CollexionIterator<T>::Release() 00692 { 00693 if (_parent) 00694 { 00695 _parent->Next(*this, false); 00696 _parent = 0; 00697 } 00698 } 00699 00700 00701 } // namespace sphynx 00702 00703 00704 } // namespace cat 00705 00706 #endif // CAT_SPHYNX_COLLEXION_HPP
Copyright © 2007-2010 by The Shadowrun: Awakened Team. This work is licensed under the GNU Lesser General Public License 3.