A C++ library for logging very fast and without allocating.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

274 lines
6.5 KiB

  1. #include <thread>
  2. #include "catch2/catch_all.hpp"
  3. #include "../LibSnugLog/include/disruptor.h"
  4. struct strategy {
  5. static constexpr overflow_response_t on_overflow = overflow_response_t::must_wait;
  6. void wait() {}
  7. };
  8. struct strategy_that_overflows {
  9. static constexpr overflow_response_t on_overflow = overflow_response_t::must_overflow;
  10. void wait() {}
  11. };
  12. TEST_CASE("Disruptor works sequentially") {
  13. std::array<char, 8192> buffer{};
  14. disruptor<strategy> v{buffer.data(), buffer.size()};
  15. SECTION("117") {
  16. auto W = v.reserve_write(100);
  17. v[W.start] = 117;
  18. v.conclude_write(W);
  19. auto R = v.reserve_read();
  20. REQUIRE(v[R.start]== 117);
  21. v.conclude_read(R);
  22. }
  23. SECTION("12") {
  24. {
  25. auto W = v.reserve_write(6);
  26. v[W.start] = 12;
  27. v.conclude_write(W);
  28. auto R = v.reserve_read();
  29. REQUIRE(v[R.start]== 12);
  30. v.conclude_read(R);
  31. }
  32. {
  33. auto W = v.reserve_write(6);
  34. v[W.start] = 8;
  35. v.conclude_write(W);
  36. auto R = v.reserve_read();
  37. REQUIRE(v[R.start]== 8);
  38. v.conclude_read(R);
  39. }
  40. }
  41. SECTION("Disruptor loop around") {
  42. std::multiset<char> mset;
  43. for(int i = 0; i != 255; i++) {
  44. auto W = v.reserve_write(100);
  45. v[W.start] = (char)i;
  46. for(size_t idx = W.start; idx != W.end; idx = (idx+1)%v.size()) {
  47. v[idx] = (char)i;
  48. }
  49. v.conclude_write(W);
  50. auto R = v.reserve_read();
  51. for(size_t idx = R.start; idx != R.end; idx = (idx+1)%v.size()) {
  52. mset.insert(v[idx]);
  53. }
  54. v.conclude_read(R);
  55. }
  56. for(int i = 0; i != 255; i++) {
  57. REQUIRE(mset.count((char)i) == 100);
  58. }
  59. }
  60. SECTION("Disruptor concurrent odd vs even") {
  61. std::atomic<bool> trigger = false;
  62. std::multiset<char> mset;
  63. std::stringstream continuity;
  64. int acc = 0;
  65. for(int i = 0; i<= 255; i++) {
  66. acc+=i;
  67. }
  68. std::thread reader([&](){
  69. int cnt = 0;
  70. while (cnt != acc) {
  71. auto R = v.reserve_read();
  72. for (size_t idx = R.start; idx != R.end; idx = (idx + 1) % v.size()) {
  73. mset.insert(v[idx]);
  74. continuity << (char)v[idx];
  75. }
  76. v.conclude_read(R);
  77. cnt += (R.end > R.start) * (R.end - R.start)
  78. + (R.end < R.start) * (v.size() - R.start + R.end);
  79. }
  80. });
  81. std::thread even([&]() {
  82. while(!trigger.load());
  83. for (int i = 2; i <= 255; i += 2) {
  84. auto W = v.reserve_write(i);
  85. v[W.start] = (char) i;
  86. for (size_t idx = W.start; idx != W.end; idx = (idx + 1) % v.size()) {
  87. v[idx] = (char) i;
  88. }
  89. v.conclude_write(W);
  90. }
  91. });
  92. std::thread odd([&]() {
  93. while(!trigger.load());
  94. for (int i = 1; i <= 255; i += 2) {
  95. auto W = v.reserve_write(i);
  96. v[W.start] = (char) i;
  97. for (size_t idx = W.start; idx != W.end; idx = (idx + 1) % v.size()) {
  98. v[idx] = (char) i;
  99. }
  100. v.conclude_write(W);
  101. }
  102. });
  103. // byte received count test
  104. trigger.store(true);
  105. reader.join(); even.join(); odd.join();
  106. for(int i = 1; i <= 255; i++) {
  107. REQUIRE(mset.count((char)i) == i);
  108. }
  109. // Continuity tests
  110. int changes = 0;
  111. auto str = continuity.str();
  112. char current = *str.begin();
  113. auto it = str.begin();
  114. for(;it != str.end();) {
  115. while(it != str.end() && *it == current) {++it;}
  116. changes += 1;
  117. current = *it;
  118. }
  119. REQUIRE(changes == 255);
  120. }
  121. }
  122. TEST_CASE("Fails if buffer too small") {
  123. REQUIRE_THROWS_AS(disruptor<OverflowWait>(nullptr, page_size), disruptor_exception);
  124. }
  125. TEST_CASE("Fails if buffer size is 0") {
  126. REQUIRE_THROWS_AS(disruptor<OverflowWait>(nullptr, 0), disruptor_exception);
  127. }
  128. TEST_CASE("Disruptor works on overflow mode if things are not too contentious", "[long][unstable]") {
  129. std::array<char, 8192> buffer{};
  130. disruptor<strategy_that_overflows> v{buffer.data(), buffer.size()};
  131. SECTION("117") {
  132. auto W = v.reserve_write(100);
  133. v[W.start] = 117;
  134. v.conclude_write(W);
  135. auto R = v.reserve_read();
  136. REQUIRE(v[R.start]== 117);
  137. v.conclude_read(R);
  138. }
  139. SECTION("12") {
  140. {
  141. auto W = v.reserve_write(6);
  142. v[W.start] = 12;
  143. v.conclude_write(W);
  144. auto R = v.reserve_read();
  145. REQUIRE(v[R.start]== 12);
  146. v.conclude_read(R);
  147. }
  148. {
  149. auto W = v.reserve_write(6);
  150. v[W.start] = 8;
  151. v.conclude_write(W);
  152. auto R = v.reserve_read();
  153. REQUIRE(v[R.start]== 8);
  154. v.conclude_read(R);
  155. }
  156. }
  157. SECTION("Disruptor loop around") {
  158. std::multiset<char> mset;
  159. for(int i = 0; i != 255; i++) {
  160. auto W = v.reserve_write(100);
  161. v[W.start] = (char)i;
  162. for(size_t idx = W.start; idx != W.end; idx = (idx+1)%v.size()) {
  163. v[idx] = (char)i;
  164. }
  165. v.conclude_write(W);
  166. auto R = v.reserve_read();
  167. for(size_t idx = R.start; idx != R.end; idx = (idx+1)%v.size()) {
  168. mset.insert(v[idx]);
  169. }
  170. v.conclude_read(R);
  171. }
  172. for(int i = 0; i != 255; i++) {
  173. REQUIRE(mset.count((char)i) == 100);
  174. }
  175. }
  176. SECTION("Disruptor concurrent odd vs even") {
  177. std::atomic<bool> trigger = false;
  178. std::multiset<char> mset;
  179. std::stringstream continuity;
  180. int acc = 0;
  181. for(int i = 0; i<= 255; i++) {
  182. acc+=i;
  183. }
  184. using namespace std::chrono_literals;
  185. std::thread reader([&](){
  186. int cnt = 0;
  187. auto start = std::chrono::high_resolution_clock::now();
  188. while (std::chrono::high_resolution_clock::now() - start < 150ms) {
  189. auto R = v.reserve_read();
  190. for (size_t idx = R.start; idx != R.end; idx = (idx + 1) % v.size()) {
  191. mset.insert(v[idx]);
  192. continuity << (char)v[idx];
  193. }
  194. v.conclude_read(R);
  195. cnt += (R.end > R.start) * (R.end - R.start)
  196. + (R.end < R.start) * (v.size() - R.start + R.end);
  197. }
  198. });
  199. std::thread even([&]() {
  200. while(!trigger.load());
  201. for (int i = 2; i <= 255; i += 2) {
  202. auto W = v.reserve_write(i);
  203. v[W.start] = (char) i;
  204. for (size_t idx = W.start; idx != W.end; idx = (idx + 1) % v.size()) {
  205. v[idx] = (char) i;
  206. }
  207. v.conclude_write(W);
  208. std::this_thread::sleep_for(50us);
  209. }
  210. });
  211. std::thread odd([&]() {
  212. while(!trigger.load());
  213. for (int i = 1; i <= 255; i += 2) {
  214. auto W = v.reserve_write(i);
  215. v[W.start] = (char) i;
  216. for (size_t idx = W.start; idx != W.end; idx = (idx + 1) % v.size()) {
  217. v[idx] = (char) i;
  218. }
  219. v.conclude_write(W);
  220. std::this_thread::sleep_for(50us);
  221. }
  222. });
  223. // byte received count test
  224. trigger.store(true);
  225. reader.join();
  226. even.join();
  227. odd.join();
  228. uint16_t cnt = 0;
  229. for(int i = 1; i <= 255; i++) {
  230. cnt += (mset.count((char)i) == i);
  231. }
  232. REQUIRE(cnt >= 100);
  233. // Continuity tests
  234. int changes = 0;
  235. auto str = continuity.str();
  236. char current = *str.begin();
  237. auto it = str.begin();
  238. for(;it != str.end();) {
  239. while(it != str.end() && *it == current) {++it;}
  240. changes += 1;
  241. current = *it;
  242. }
  243. REQUIRE(changes >= 100);
  244. }
  245. }