Empty tests that the queue is not claimed to be empty when is is in fact not. Emptiness property is crucial part of thread pool blocking scheme, so we go to great effort to ensure this property. We create a queue with 1 element and then push 1 element (either front or back at random) and pop 1 element (either front or back at random). So queue always contains at least 1 element, but otherwise chan
| 118 | // 1 element, but otherwise changes chaotically. Another thread constantly tests |
| 119 | // that the queue is not claimed to be empty. |
| 120 | void test_empty_runqueue() |
| 121 | { |
| 122 | RunQueue<int, 4> q; |
| 123 | q.PushFront(1); |
| 124 | std::atomic<bool> done(false); |
| 125 | std::thread mutator([&q, &done]() { |
| 126 | unsigned rnd = 0; |
| 127 | std::vector<int> stolen; |
| 128 | for (int i = 0; i < 1 << 18; i++) { |
| 129 | if (rand_reentrant(&rnd) % 2) |
| 130 | VERIFY_IS_EQUAL(0, q.PushFront(1)); |
| 131 | else |
| 132 | VERIFY_IS_EQUAL(0, q.PushBack(1)); |
| 133 | if (rand_reentrant(&rnd) % 2) |
| 134 | VERIFY_IS_EQUAL(1, q.PopFront()); |
| 135 | else { |
| 136 | for (;;) { |
| 137 | if (q.PopBackHalf(&stolen) == 1) { |
| 138 | stolen.clear(); |
| 139 | break; |
| 140 | } |
| 141 | VERIFY_IS_EQUAL(0u, stolen.size()); |
| 142 | } |
| 143 | } |
| 144 | } |
| 145 | done = true; |
| 146 | }); |
| 147 | while (!done) { |
| 148 | VERIFY(!q.Empty()); |
| 149 | int size = q.Size(); |
| 150 | VERIFY_GE(size, 1); |
| 151 | VERIFY_LE(size, 2); |
| 152 | } |
| 153 | VERIFY_IS_EQUAL(1, q.PopFront()); |
| 154 | mutator.join(); |
| 155 | } |
| 156 | |
| 157 | // Stress is a chaotic random test. |
| 158 | // One thread (owner) calls PushFront/PopFront, other threads call PushBack/ |
no test coverage detected