LockFreePoolAllocator.h 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. /* Copyright The kNet Project.
  2. Licensed under the Apache License, Version 2.0 (the "License");
  3. you may not use this file except in compliance with the License.
  4. You may obtain a copy of the License at
  5. http://www.apache.org/licenses/LICENSE-2.0
  6. Unless required by applicable law or agreed to in writing, software
  7. distributed under the License is distributed on an "AS IS" BASIS,
  8. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  9. See the License for the specific language governing permissions and
  10. limitations under the License. */
  11. #pragma once
  12. /** @file LockFreePoolAllocator.h
  13. @brief The PoolAllocatable<T> and LockFreePoolAllocator<T> template classes. */
  14. #include <iostream>
  15. #include "Atomics.h"
  16. namespace kNet
  17. {
  18. template<typename T>
  19. struct PoolAllocatable
  20. {
  21. /// Points to the next item in the lock-free linked list. Do not access or
  22. /// dereference this variable in client code, as it is used internally by
  23. /// LockFreePoolAllocator only.
  24. T * volatile next;
  25. };
  26. /// T must implement PoolAllocatable.
  27. template<typename T>
  28. class LockFreePoolAllocator
  29. {
  30. public:
  31. LockFreePoolAllocator()
  32. :root(0)
  33. {
  34. }
  35. ~LockFreePoolAllocator()
  36. {
  37. UnsafeClearAll();
  38. }
  39. /// Allocates a new object of type T. Call Free() to deallocate the object.
  40. T *New()
  41. {
  42. // Currently the use of lockfree allocation pool is disabled, since it has a known race condition, documented below.
  43. return new T();
  44. /*
  45. for(;;)
  46. {
  47. T *allocated = root;
  48. if (!allocated) // If the root is null, there are no objects in the pool, so we must create new from the runtime heap.
  49. {
  50. allocated = new T();
  51. allocated->next = 0;
  52. return allocated;
  53. }
  54. ///\bug Note that this function is not thread-safe. Accessing allocated->next may already be dangling if another thread has deleted it.
  55. T *newRoot = allocated->next;
  56. if (CmpXChgPointer((void**)&root, newRoot, allocated))
  57. {
  58. allocated->next = 0;
  59. return allocated;
  60. }
  61. }
  62. */
  63. }
  64. void Free(T *ptr)
  65. {
  66. delete ptr;
  67. // Currently the use of lockfree allocation pool is disabled, since it has a known race condition.
  68. /*
  69. if (!ptr)
  70. return;
  71. assert(ptr != root);
  72. for(;;)
  73. {
  74. ptr->next = root;
  75. if (CmpXChgPointer((void**)&root, ptr, ptr->next))
  76. return;
  77. }
  78. */
  79. }
  80. /// Deallocates all cached unused nodes in this pool. Thread-safe and lock-free. If you are manually tearing
  81. /// down the pool and there are no other threads accessing this, you may call the even faster version
  82. /// UnsafeClearAll(), which ignores compare-and-swap updates.
  83. void ClearAll()
  84. {
  85. while(root)
  86. {
  87. T *node = New();
  88. delete node;
  89. }
  90. }
  91. /// A fast method to free all items allocated in the pool.
  92. /// Not thread-safe, only call when you can guarantee there are no threads calling New() or Free().
  93. void UnsafeClearAll()
  94. {
  95. assert(!DebugHasCycle());
  96. T *node = root;
  97. while(node)
  98. {
  99. T *next = node->next;
  100. delete node;
  101. node = next;
  102. }
  103. root = 0;
  104. }
  105. /// A debugging function that checks whether the underlying linked list has a cycle or not. Not thread-safe!
  106. bool DebugHasCycle()
  107. {
  108. T *n1 = root;
  109. if (!n1)
  110. return false;
  111. T *n2 = n1->next;
  112. while(n2 != 0)
  113. {
  114. if (n1 == n2)
  115. return true;
  116. n1 = n1->next;
  117. n2 = n2->next;
  118. if (n2)
  119. n2 = n2->next;
  120. }
  121. return false;
  122. }
  123. /// A debugging function that prints out all the objects in the pool. Not thread-safe!
  124. void DumpPool()
  125. {
  126. using namespace std;
  127. T *node = root;
  128. cout << "Root: 0x" << ios::hex << root << ".Next: " << ios::hex << (root ? root->next : 0) << std::endl;
  129. int size = 0;
  130. if (node)
  131. node = node->next;
  132. while(node)
  133. {
  134. cout << "Node: 0x" << ios::hex << node << ".Next: " << ios::hex << (node ? node->next : 0) << std::endl;
  135. node = node->next;
  136. ++size;
  137. }
  138. cout << "Total: " << size << " elements." << endl;
  139. }
  140. private:
  141. T * volatile root;
  142. };
  143. } // ~kNet