|
15 | 15 | #include <gtest/gtest.h> |
16 | 16 |
|
17 | 17 | #include <atomic> |
| 18 | +#include <chrono> |
| 19 | +#include <condition_variable> |
| 20 | +#include <memory> |
| 21 | +#include <mutex> |
18 | 22 | #include <thread> |
19 | 23 | #include <vector> |
20 | 24 |
|
@@ -63,18 +67,30 @@ enum class RoutineStatus |
63 | 67 |
|
64 | 68 | struct EntityOwner |
65 | 69 | { |
| 70 | + struct SyncPoint |
| 71 | + { |
| 72 | + virtual void notify_and_wait() = 0; |
| 73 | + }; |
| 74 | + |
66 | 75 | EntityOwner( |
67 | 76 | const EntityMock& entity) |
68 | 77 | : entity_ptr(entity.get_refcounter_pointer()) |
69 | 78 | , routine_status(RoutineStatus::NON_INITIALIZED) |
70 | 79 | { |
71 | 80 | } |
72 | 81 |
|
73 | | - void spawn_routine() |
| 82 | + void spawn_routine( |
| 83 | + SyncPoint* sync = nullptr) |
74 | 84 | { |
75 | | - th = std::thread([&]() |
| 85 | + th = std::thread([this, sync]() |
76 | 86 | { |
77 | 87 | RefCountedPointer<EntityMock>::Instance entity_instance(entity_ptr); |
| 88 | + |
| 89 | + if (sync != nullptr) |
| 90 | + { |
| 91 | + sync->notify_and_wait(); |
| 92 | + } |
| 93 | + |
78 | 94 | if (entity_instance) |
79 | 95 | { |
80 | 96 | entity_instance->dummy_process_data(nullptr); |
@@ -105,7 +121,7 @@ class RefCountedPointerTests : public ::testing::Test |
105 | 121 |
|
106 | 122 | void SetUp() override |
107 | 123 | { |
108 | | - owners_.reserve(5); |
| 124 | + owners_.reserve(n_owners); |
109 | 125 | for (std::size_t i = 0; i < n_owners; ++i) |
110 | 126 | { |
111 | 127 | owners_.emplace_back(entity_); |
@@ -153,21 +169,61 @@ TEST_F(RefCountedPointerTests, refcountedpointer_inactive) |
153 | 169 |
|
154 | 170 | TEST_F(RefCountedPointerTests, refcounterpointer_deactivate_waits_for_no_references) |
155 | 171 | { |
| 172 | + struct WaitForAllOwners : public EntityOwner::SyncPoint |
| 173 | + { |
| 174 | + WaitForAllOwners() |
| 175 | + : num_notifications_(0) |
| 176 | + { |
| 177 | + } |
| 178 | + |
| 179 | + void notify_and_wait() override |
| 180 | + { |
| 181 | + std::unique_lock<std::mutex> lock(mutex_); |
| 182 | + ++num_notifications_; |
| 183 | + if (num_notifications_ == n_owners) |
| 184 | + { |
| 185 | + cv_.notify_all(); |
| 186 | + } |
| 187 | + |
| 188 | + cv_.wait(lock, [this]() -> bool |
| 189 | + { |
| 190 | + return num_notifications_ >= n_owners; |
| 191 | + }); |
| 192 | + } |
| 193 | + |
| 194 | + void wait_for_all_notifications() |
| 195 | + { |
| 196 | + std::unique_lock<std::mutex> lock(mutex_); |
| 197 | + cv_.wait(lock, [this]() -> bool |
| 198 | + { |
| 199 | + return num_notifications_ == n_owners; |
| 200 | + }); |
| 201 | + } |
| 202 | + |
| 203 | + private: |
| 204 | + |
| 205 | + std::mutex mutex_; |
| 206 | + std::condition_variable cv_; |
| 207 | + std::size_t num_notifications_; |
| 208 | + }; |
| 209 | + |
| 210 | + WaitForAllOwners sync_point; |
| 211 | + |
156 | 212 | // Spawn some routines |
157 | 213 | for (std::size_t i = 0; i < n_owners; ++i) |
158 | 214 | { |
159 | | - owners_[i].spawn_routine(); |
| 215 | + owners_[i].spawn_routine(&sync_point); |
160 | 216 | } |
161 | 217 |
|
162 | | - // Ensure owners' routines have started |
163 | | - std::this_thread::sleep_for(std::chrono::milliseconds(20)); |
| 218 | + // Wait for all routines to be started |
| 219 | + sync_point.wait_for_all_notifications(); |
164 | 220 |
|
165 | 221 | auto t0 = std::chrono::steady_clock::now(); |
166 | 222 | entity_.destroy(); |
167 | 223 | auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::steady_clock::now() - t0).count(); |
168 | 224 |
|
169 | 225 | std::cout << "Elapsed time: " << elapsed << " ms" << std::endl; |
170 | | - ASSERT_GT(elapsed, 50); // destroy should have taken at least 50 ms. Being strict it should be 80, but we allow some margin |
| 226 | + ASSERT_GT(elapsed, 50); // destroy should have taken at least 50 ms. Being strict it should be 100, but we allow some margin |
171 | 227 | ASSERT_EQ(entity_.n_times_data_processed, 5); |
172 | 228 | } |
173 | 229 |
|
|
0 commit comments