SphynxCollexion.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*
  2. Copyright (c) 2009-2010 Christopher A. Taylor. All rights reserved.
  3. Redistribution and use in source and binary forms, with or without
  4. modification, are permitted provided that the following conditions are met:
  5. * Redistributions of source code must retain the above copyright notice,
  6. this list of conditions and the following disclaimer.
  7. * Redistributions in binary form must reproduce the above copyright notice,
  8. this list of conditions and the following disclaimer in the documentation
  9. and/or other materials provided with the distribution.
  10. * Neither the name of LibCat nor the names of its contributors may be used
  11. to endorse or promote products derived from this software without
  12. specific prior written permission.
  13. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  14. AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  15. IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  16. ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
  17. LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  18. CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  19. SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  20. INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  21. CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  22. ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  23. POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #ifndef CAT_SPHYNX_COLLEXION_HPP
  26. #define CAT_SPHYNX_COLLEXION_HPP
  27. #include <cat/threads/Mutex.hpp>
  28. #include <cat/threads/RegionAllocator.hpp>
  29. #include <cat/net/SphynxTransport.hpp>
  30. namespace cat {
  31. namespace sphynx {
  32. /*
  33. The purpose of sphynx::Collexion and sphynx::CollexionIterator is to
  34. store lists of Connexion object references and iterate through them.
  35. Since the number of clients may be in the thousands, I feel it is
  36. important to scale effectively. So in the Collexion data structure,
  37. insertion and removal are O(1) operations. Also locks should be held
  38. for the smallest amount of time possible, so I have taken care to make
  39. locks short and reduce the amount of locking. For example, the iterator
  40. caches large blocks of data instead of locking for each iteration.
  41. The design is optimized for cache usage, re-using common code to benefit
  42. from code cache and allocating and accessing table entries on cache line
  43. boundaries to double memory performance over a naive approach.
  44. */
  45. //// sphynx::Collexion
  46. template<class T>
  47. class CollexionIterator;
  48. template<class T>
  49. struct CollexionElement
  50. {
  51. // Number of active enumerators using this element
  52. // If references are held it cannot be deleted
  53. // so the KILL flag is set on the 'next' member and
  54. // the final enumerator to reduce the reference count
  55. // to zero is responsible for removal.
  56. u32 refcnt;
  57. // Bitfield:
  58. // 1 bit: COLLISION FLAG
  59. // 1 bit: KILL FLAG
  60. // 30 bits: Table index to next element in list + 1
  61. u32 next;
  62. // Data at this table element
  63. T *conn;
  64. };
  65. struct CollexionElement2
  66. {
  67. // Previous table element in list + 1
  68. u32 last;
  69. // Hash of data pointer from main entry (so it doesn't need to be recalculated during growth)
  70. u32 hash;
  71. };
  72. template<class T>
  73. class Collexion
  74. {
  75. static const u32 COLLIDE_MASK = 0x80000000;
  76. static const u32 KILL_MASK = 0x40000000;
  77. static const u32 NEXT_MASK = 0x3fffffff;
  78. static const u32 MIN_ALLOCATED = 32;
  79. private:
  80. // Number of used table elements
  81. u32 _used;
  82. // Number of allocated table elements
  83. u32 _allocated;
  84. // First table index in list of active elements
  85. u32 _first;
  86. // Primary table
  87. CollexionElement<T> *_table;
  88. // Secondary table, split off so that primary table elements will
  89. // fit on a cache line. Contains data that is only accessed rarely.
  90. CollexionElement2 *_table2;
  91. // Table lock
  92. Mutex _lock;
  93. protected:
  94. // Attempt to double size of hash table (does not hold lock)
  95. bool DoubleTable();
  96. // Hash a pointer to a 32-bit table key
  97. static CAT_INLINE u32 HashPtr(T *ptr)
  98. {
  99. u64 key = 0xBADdecafDEADbeef;
  100. #if defined(CAT_WORD_64)
  101. key ^= *(u64*)&ptr;
  102. #else
  103. key ^= *(u32*)&ptr;
  104. #endif
  105. key = (~key) + (key << 18);
  106. key = key ^ (key >> 31);
  107. key = key * 21;
  108. key = key ^ (key >> 11);
  109. key = key + (key << 6);
  110. key = key ^ (key >> 22);
  111. return (u32)key;
  112. }
  113. // Common functions shared by interface for good code cache usage:
  114. // Unlink a table key
  115. void Unlink(u32 key);
  116. // Fill an iterator with the next set of data
  117. // Returns false if no data remains to fill
  118. void Fill(CollexionIterator<T> &iter, u32 first);
  119. // Find a hash table key based on data
  120. u32 Find(T *conn);
  121. public:
  122. // Ctor zeros everything
  123. Collexion()
  124. {
  125. _first = 0;
  126. _used = 0;
  127. _allocated = 0;
  128. _table = 0;
  129. _table2 = 0;
  130. }
  131. // Dtor releases dangling memory
  132. ~Collexion();
  133. // Returns true if table is empty
  134. CAT_INLINE bool IsEmpty() { return _used == 0; }
  135. // Insert Connexion object, return false if already present or out of memory
  136. bool Insert(T *conn);
  137. // Remove Connexion object from list if it exists
  138. bool Remove(T *conn);
  139. // Begin iterating through list
  140. void Begin(CollexionIterator<T> &iter);
  141. // Iterate
  142. void Next(CollexionIterator<T> &iter, bool refill = true);
  143. };
  144. //// sphynx::CollexionIterator
  145. template<class T>
  146. class CollexionIterator
  147. {
  148. friend class Collexion<T>;
  149. static const u32 MAX_CACHE = 256;
  150. // Parent Collexion object
  151. Collexion<T> *_parent;
  152. // First and last hash table indices in parent
  153. u32 _first, _last;
  154. // Stores the size of the parent when snapshot was taken,
  155. // will invalidate _first and _last if table changed size
  156. u32 _prev_allocated;
  157. // Connexion object cache, to avoid locking and unlocking a lot
  158. T *_cache[MAX_CACHE];
  159. // Offset into cache and total elements in cache
  160. // Will grab another cache once offset reaches total
  161. u32 _offset, _total;
  162. public:
  163. // Smart pointer -style accessors
  164. CAT_INLINE T *Get() throw() { return _cache[_offset]; }
  165. CAT_INLINE T *operator->() throw() { return _cache[_offset]; }
  166. CAT_INLINE T &operator*() throw() { return *_cache[_offset]; }
  167. CAT_INLINE operator T*() { return _cache[_offset]; }
  168. public:
  169. // Ctor: Grabs first cache of Connexions
  170. CollexionIterator(Collexion<T> &begin);
  171. // Dtor: Calls Release()
  172. ~CollexionIterator();
  173. // Iterate to next Connexion object in list
  174. CollexionIterator &operator++();
  175. // Releases reference to any outstanding Connexions
  176. void Release();
  177. };
  178. //// sphynx::Collexion
  179. template<class T>
  180. bool Collexion<T>::DoubleTable()
  181. {
  182. u32 new_allocated = _allocated << 1;
  183. if (new_allocated < MIN_ALLOCATED) new_allocated = MIN_ALLOCATED;
  184. // Allocate secondary table
  185. u32 new_bytes2 = sizeof(CollexionElement2) * new_allocated;
  186. CollexionElement2 *new_table2 = reinterpret_cast<CollexionElement2*>(
  187. RegionAllocator::ii->Acquire(new_bytes2) );
  188. if (!new_table2) return false;
  189. // Allocate primary table
  190. u32 new_bytes = sizeof(CollexionElement<T>) * new_allocated;
  191. CollexionElement<T> *new_table = reinterpret_cast<CollexionElement<T> *>(
  192. RegionAllocator::ii->Acquire(new_bytes) );
  193. if (!new_table)
  194. {
  195. RegionAllocator::ii->Release(new_table2);
  196. return false;
  197. }
  198. CAT_CLR(new_table, new_bytes);
  199. u32 new_first = 0;
  200. if (_table && _table2)
  201. {
  202. // For each entry in the old table,
  203. u32 ii = _first, mask = _allocated - 1;
  204. while (ii)
  205. {
  206. --ii;
  207. CollexionElement<T> *oe = &_table[ii];
  208. u32 hash = _table2[ii].hash;
  209. u32 key = hash & mask;
  210. // While collisions occur,
  211. while (new_table[key].conn)
  212. {
  213. // Mark collision
  214. new_table[key].next |= COLLIDE_MASK;
  215. // Walk collision list
  216. key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
  217. }
  218. // Fill new table element
  219. new_table[key].conn = oe->conn;
  220. new_table2[key].hash = hash;
  221. // new_table[key].refcnt is already zero
  222. // Link new element to new list
  223. if (new_first)
  224. {
  225. new_table[key].next |= new_first;
  226. new_table2[new_first - 1].last = key;
  227. }
  228. // new_table[key].next is already zero so no need to zero it here
  229. new_first = key + 1;
  230. // Get next old table entry
  231. ii = oe->next & NEXT_MASK;
  232. }
  233. // Zero head->last
  234. new_table2[new_first - 1].last = 0;
  235. }
  236. // Resulting linked list starting with _first-1 will extend until e->next == 0
  237. if (_table2) RegionAllocator::ii->Release(_table2);
  238. if (_table) RegionAllocator::ii->Release(_table);
  239. _table = new_table;
  240. _table2 = new_table2;
  241. _allocated = new_allocated;
  242. _first = new_first;
  243. return true;
  244. }
  245. template<class T>
  246. Collexion<T>::~Collexion()
  247. {
  248. if (_table2)
  249. {
  250. RegionAllocator::ii->Release(_table2);
  251. }
  252. // If table doesn't exist, return
  253. if (!_table) return;
  254. // For each allocated table entry,
  255. for (u32 ii = 0; ii < _allocated; ++ii)
  256. {
  257. // Get Connexion object
  258. T *conn = _table[ii].conn;
  259. // If object is valid, release it
  260. if (conn) conn->ReleaseRef();
  261. }
  262. // Release table memory
  263. RegionAllocator::ii->Release(_table);
  264. }
  265. template<class T>
  266. bool Collexion<T>::Insert(T *conn)
  267. {
  268. u32 hash = HashPtr(conn);
  269. conn->AddRef();
  270. AutoMutex lock(_lock);
  271. // If more than half of the table will be used,
  272. if (_used >= (_allocated >> 1))
  273. {
  274. // Double the size of the table (O(1) allocation pattern)
  275. // Growing pains are softened by careful design
  276. if (!DoubleTable())
  277. {
  278. // On growth failure, return false
  279. lock.Release();
  280. conn->ReleaseRef();
  281. return false;
  282. }
  283. }
  284. // Mask off high bits to make table key from hash
  285. u32 mask = _allocated - 1;
  286. u32 key = hash & mask;
  287. // While empty table entry not found,
  288. while (_table[key].conn)
  289. {
  290. // If Connexion object is already in the table,
  291. if (_table[key].conn == conn)
  292. {
  293. // Return false on duplicate
  294. lock.Release();
  295. conn->ReleaseRef();
  296. return false;
  297. }
  298. // Mark as a collision
  299. _table[key].next |= COLLIDE_MASK;
  300. // Walk collision list
  301. key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
  302. }
  303. // Fill new element
  304. _table[key].conn = conn;
  305. _table[key].refcnt = 0;
  306. _table2[key].hash = hash;
  307. _table2[key].last = 0;
  308. // Link new element to front of list
  309. if (_first) _table2[_first - 1].last = key + 1;
  310. _table[key].next = (_table[key].next & COLLIDE_MASK) | _first;
  311. _first = key + 1;
  312. ++_used;
  313. return true;
  314. }
  315. template<class T>
  316. void Collexion<T>::Unlink(u32 key)
  317. {
  318. // Clear reference
  319. _table[key].conn = 0;
  320. // Unlink from active list
  321. u32 next = _table[key].next & NEXT_MASK;
  322. u32 last = _table2[key].last;
  323. if (last) _table[last-1].next = (_table[last-1].next & ~NEXT_MASK) | next;
  324. else _first = next;
  325. if (next) _table2[next-1].last = last;
  326. // If this key was a leaf on a collision wind,
  327. if (!(_table[key].next & COLLIDE_MASK))
  328. {
  329. u32 mask = _allocated - 1;
  330. do
  331. {
  332. // Go backwards through the collision list one step
  333. key = ((key + COLLISION_INCRINVERSE) * COLLISION_MULTINVERSE) & mask;
  334. // Stop where collision list stops
  335. if (!(_table[key].next & COLLIDE_MASK))
  336. break;
  337. // Turn off collision key for previous entry
  338. _table[key].next &= ~COLLIDE_MASK;
  339. } while (!_table[key].conn);
  340. }
  341. // Update number of used elements
  342. --_used;
  343. }
  344. template<class T>
  345. bool Collexion<T>::Remove(T *conn)
  346. {
  347. u32 hash = HashPtr(conn);
  348. AutoMutex lock(_lock);
  349. // If table doesn't exist,
  350. if (!_allocated) return false;
  351. // Mask off high bits to make table key from hash
  352. u32 mask = _allocated - 1;
  353. u32 key = hash & mask;
  354. // While target table entry not found,
  355. for (;;)
  356. {
  357. // If target was found,
  358. if (_table[key].conn == conn)
  359. {
  360. if (_table[key].refcnt)
  361. {
  362. // Mark it killed so iterator can clean it up when it's finished
  363. _table[key].next |= KILL_MASK;
  364. }
  365. else
  366. {
  367. Unlink(key);
  368. lock.Release();
  369. conn->ReleaseRef();
  370. }
  371. // Return success
  372. return true;
  373. }
  374. if (!(_table[key].next & COLLIDE_MASK))
  375. {
  376. break; // End of collision list
  377. }
  378. // Walk collision list
  379. key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
  380. }
  381. // Return failure: not found
  382. return false;
  383. }
  384. template<class T>
  385. void Collexion<T>::Fill(CollexionIterator<T> &iter, u32 first)
  386. {
  387. u32 key = first;
  388. // Find first list element that does not want to die
  389. while (key && (_table[key-1].next & KILL_MASK))
  390. {
  391. // Go to next
  392. key = _table[key-1].next & NEXT_MASK;
  393. }
  394. iter._offset = 0;
  395. // If no elements in table,
  396. if (!key)
  397. {
  398. // Return empty set
  399. iter._cache[0] = 0;
  400. iter._total = 0;
  401. iter._first = 0;
  402. iter._last = 0;
  403. return;
  404. }
  405. // Remember size of hash table in case it grows before iterator is done
  406. iter._prev_allocated = _allocated;
  407. // Remember first key for next iteration
  408. iter._first = key;
  409. // For each of the first MAX_CACHE elements in the table, copy the data pointer to cache
  410. u32 ii = 0, final = 0;
  411. do
  412. {
  413. // If element does not want to die,
  414. if (!(_table[key-1].next & KILL_MASK))
  415. {
  416. // Copy data pointer
  417. iter._cache[ii] = _table[key-1].conn;
  418. // Increment reference count for element
  419. _table[key-1].refcnt++;
  420. // Remember key as the next iteration starting point
  421. final = key;
  422. // Check if copy is complete
  423. if (++ii >= CollexionIterator<T>::MAX_CACHE) break;
  424. }
  425. // Go to next key
  426. key = _table[key-1].next & NEXT_MASK;
  427. } while (key);
  428. // Record number of elements written
  429. iter._total = ii;
  430. // Remember next key for next iteration
  431. iter._last = final;
  432. }
  433. template<class T>
  434. void Collexion<T>::Begin(CollexionIterator<T> &iter)
  435. {
  436. iter._parent = this;
  437. AutoMutex lock(_lock);
  438. Fill(iter, _first);
  439. }
  440. // Find a hash table key based on data
  441. template<class T>
  442. u32 Collexion<T>::Find(T *conn)
  443. {
  444. u32 mask = _allocated - 1;
  445. u32 key = HashPtr(conn) & mask;
  446. // Find the object in the collision list starting at the expected location
  447. while (_table[key].conn != conn)
  448. {
  449. // If at the end of the collision list,
  450. if (!(_table[key].next & COLLIDE_MASK))
  451. break; // Should never happen
  452. // Walk collision list
  453. key = (key * COLLISION_MULTIPLIER + COLLISION_INCREMENTER) & mask;
  454. }
  455. return key;
  456. }
  457. template<class T>
  458. void Collexion<T>::Next(CollexionIterator<T> &iter, bool refill)
  459. {
  460. T *release_list[CollexionIterator<T>::MAX_CACHE];
  461. u32 release_ii = 0;
  462. u32 key = iter._first;
  463. u32 last = iter._last;
  464. // If iteration is done,
  465. if (!key) return;
  466. AutoMutex lock(_lock);
  467. // If hash table changed size (rare),
  468. if (iter._prev_allocated != _allocated)
  469. {
  470. // iter._first and iter._last are invalid
  471. // ...but we can find them again based on the cached pointers!
  472. key = Find(iter._cache[0]) + 1;
  473. last = Find(iter._cache[iter._total - 1]) + 1;
  474. }
  475. last = _table[last-1].next & NEXT_MASK;
  476. // Release any table elements that want to die now
  477. do
  478. {
  479. u32 flags = _table[key-1].next;
  480. // If reference count is now zero for this element,
  481. if (0 == --_table[key-1].refcnt)
  482. {
  483. // If element wants to die,
  484. if (flags & KILL_MASK)
  485. {
  486. // Prepare to release data after lock is released
  487. release_list[release_ii++] = _table[key-1].conn;
  488. Unlink(key-1);
  489. }
  490. }
  491. key = flags & NEXT_MASK;
  492. } while (key != last);
  493. // Fill iterator starting with next key
  494. if (refill) Fill(iter, key);
  495. lock.Release();
  496. if (!refill)
  497. {
  498. // Return empty set
  499. iter._cache[0] = 0;
  500. iter._total = 0;
  501. iter._offset = 0;
  502. iter._first = 0;
  503. iter._last = 0;
  504. }
  505. // Release data awaiting destruction
  506. for (u32 ii = 0; ii < release_ii; ++ii)
  507. {
  508. release_list[ii]->ReleaseRef();
  509. }
  510. }
  511. //// sphynx::CollexionIterator
  512. template<class T>
  513. CollexionIterator<T>::CollexionIterator(Collexion<T> &begin)
  514. {
  515. begin.Begin(*this);
  516. }
  517. template<class T>
  518. CollexionIterator<T>::~CollexionIterator()
  519. {
  520. Release();
  521. }
  522. template<class T>
  523. CollexionIterator<T> &CollexionIterator<T>::operator++()
  524. {
  525. if (++_offset >= _total)
  526. {
  527. _parent->Next(*this, true);
  528. }
  529. return *this;
  530. }
  531. template<class T>
  532. void CollexionIterator<T>::Release()
  533. {
  534. if (_parent)
  535. {
  536. _parent->Next(*this, false);
  537. _parent = 0;
  538. }
  539. }
  540. } // namespace sphynx
  541. } // namespace cat
  542. #endif // CAT_SPHYNX_COLLEXION_HPP
粤ICP备19079148号