| 183 | |
| 184 | template<int ThreadCount, int NUM_VALUES> |
| 185 | struct test : rl::test_suite<test<ThreadCount, NUM_VALUES>, ThreadCount> |
| 186 | { |
| 187 | SPMCSequentialHashMap<int>* hash; |
| 188 | int values[NUM_VALUES]; |
| 189 | std::atomic<int> useCounts[NUM_VALUES]; |
| 190 | std::atomic<bool> removed[NUM_VALUES]; |
| 191 | |
| 192 | void before() |
| 193 | { |
| 194 | hash = new SPMCSequentialHashMap<int>(2); |
| 195 | for (int i = 0; i != NUM_VALUES; ++i) { |
| 196 | values[i] = i; |
| 197 | useCounts[i].store(0, std::memory_order_relaxed); |
| 198 | removed[i].store(false, std::memory_order_relaxed); |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | void thread(unsigned int tid) |
| 203 | { |
| 204 | if (tid == 0) { |
| 205 | // Producer |
| 206 | for (int i = 0; i != NUM_VALUES; ++i) { |
| 207 | hash->insert(i, &values[i]); |
| 208 | useCounts[i].store(ThreadCount / 2, std::memory_order_release); |
| 209 | } |
| 210 | } |
| 211 | else { |
| 212 | // Consumer |
| 213 | for (int i = 0; i != NUM_VALUES; ++i) { |
| 214 | auto useCount = useCounts[i].fetch_add(-1, std::memory_order_acquire); |
| 215 | auto val = hash->find(i); |
| 216 | bool isRemoved = removed[i].load(std::memory_order_relaxed); |
| 217 | auto current = useCounts[i].fetch_add(0, std::memory_order_release); |
| 218 | if (useCount > 0 && (current > 0 || current == 0 && useCount == 1)) { |
| 219 | RL_ASSERT(val != nullptr && *val == i && !isRemoved); |
| 220 | } |
| 221 | if (useCount == 1) { |
| 222 | val = hash->remove(i); |
| 223 | RL_ASSERT(val != nullptr && *val == i && !removed[i].load(std::memory_order_relaxed)); |
| 224 | removed[i].store(true, std::memory_order_release); |
| 225 | } |
| 226 | } |
| 227 | } |
| 228 | } |
| 229 | |
| 230 | void after() |
| 231 | { |
| 232 | delete hash; |
| 233 | } |
| 234 | |
| 235 | void invariant() |
| 236 | { |
| 237 | } |
| 238 | }; |
| 239 | |
| 240 | int main() |
| 241 | { |
no outgoing calls
no test coverage detected