|
21 | 21 | #include "client/crashpad_info.h" |
22 | 22 | #include "gtest/gtest.h" |
23 | 23 | #include "test/gtest_death.h" |
| 24 | +#include "util/misc/clock.h" |
| 25 | +#include "util/synchronization/scoped_spin_guard.h" |
| 26 | +#include "util/thread/thread.h" |
24 | 27 |
|
25 | 28 | namespace crashpad { |
26 | 29 | namespace test { |
27 | 30 | namespace { |
28 | 31 |
|
| 32 | +class SpinGuardAnnotation final : public Annotation { |
| 33 | + public: |
| 34 | + SpinGuardAnnotation(Annotation::Type type, const char name[]) |
| 35 | + : Annotation(type, |
| 36 | + name, |
| 37 | + &value_, |
| 38 | + ConcurrentAccessGuardMode::kScopedSpinGuard) {} |
| 39 | + |
| 40 | + bool Set(bool value, uint64_t spin_guard_timeout_ns) { |
| 41 | + auto guard = TryCreateScopedSpinGuard(spin_guard_timeout_ns); |
| 42 | + if (!guard) { |
| 43 | + return false; |
| 44 | + } |
| 45 | + value_ = value; |
| 46 | + SetSize(sizeof(value_)); |
| 47 | + return true; |
| 48 | + } |
| 49 | + |
| 50 | + private: |
| 51 | + bool value_; |
| 52 | +}; |
| 53 | + |
| 54 | +class ScopedSpinGuardUnlockThread final : public Thread { |
| 55 | + public: |
| 56 | + ScopedSpinGuardUnlockThread(ScopedSpinGuard scoped_spin_guard, |
| 57 | + uint64_t sleep_time_ns) |
| 58 | + : scoped_spin_guard_(std::move(scoped_spin_guard)), |
| 59 | + sleep_time_ns_(sleep_time_ns) {} |
| 60 | + |
| 61 | + private: |
| 62 | + void ThreadMain() override { |
| 63 | + SleepNanoseconds(sleep_time_ns_); |
| 64 | + |
| 65 | + // Move the ScopedSpinGuard member into a local variable which is |
| 66 | + // destroyed when ThreadMain() returns. |
| 67 | + ScopedSpinGuard local_scoped_spin_guard(std::move(scoped_spin_guard_)); |
| 68 | + |
| 69 | + // After this point, local_scoped_spin_guard will be destroyed and unlocked. |
| 70 | + } |
| 71 | + |
| 72 | + ScopedSpinGuard scoped_spin_guard_; |
| 73 | + const uint64_t sleep_time_ns_; |
| 74 | +}; |
| 75 | + |
29 | 76 | class Annotation : public testing::Test { |
30 | 77 | public: |
31 | 78 | void SetUp() override { |
@@ -108,6 +155,61 @@ TEST_F(Annotation, StringType) { |
108 | 155 | EXPECT_EQ("loooo", annotation.value()); |
109 | 156 | } |
110 | 157 |
|
| 158 | +TEST_F(Annotation, BaseAnnotationShouldNotSupportSpinGuard) { |
| 159 | + char buffer[1024]; |
| 160 | + crashpad::Annotation annotation( |
| 161 | + crashpad::Annotation::Type::kString, "no-spin-guard", buffer); |
| 162 | + EXPECT_EQ(annotation.concurrent_access_guard_mode(), |
| 163 | + crashpad::Annotation::ConcurrentAccessGuardMode::kUnguarded); |
| 164 | +#ifdef NDEBUG |
| 165 | + // This fails a DCHECK() in debug builds, so only test it for NDEBUG builds. |
| 166 | + EXPECT_EQ(std::nullopt, annotation.TryCreateScopedSpinGuard(0)); |
| 167 | +#endif |
| 168 | +} |
| 169 | + |
| 170 | +TEST_F(Annotation, CustomAnnotationShouldSupportSpinGuardAndSet) { |
| 171 | + constexpr crashpad::Annotation::Type kType = |
| 172 | + crashpad::Annotation::UserDefinedType(1); |
| 173 | + SpinGuardAnnotation spin_guard_annotation(kType, "spin-guard"); |
| 174 | + EXPECT_EQ(spin_guard_annotation.concurrent_access_guard_mode(), |
| 175 | + crashpad::Annotation::ConcurrentAccessGuardMode::kScopedSpinGuard); |
| 176 | + EXPECT_TRUE(spin_guard_annotation.Set(true, 0)); |
| 177 | + EXPECT_EQ(1U, spin_guard_annotation.size()); |
| 178 | +} |
| 179 | + |
| 180 | +TEST_F(Annotation, CustomAnnotationSetShouldFailIfRunConcurrently) { |
| 181 | + constexpr crashpad::Annotation::Type kType = |
| 182 | + crashpad::Annotation::UserDefinedType(1); |
| 183 | + SpinGuardAnnotation spin_guard_annotation(kType, "spin-guard"); |
| 184 | + auto guard = spin_guard_annotation.TryCreateScopedSpinGuard(0); |
| 185 | + EXPECT_NE(std::nullopt, guard); |
| 186 | + // This should fail, since the guard is already held and the timeout is 0. |
| 187 | + EXPECT_FALSE(spin_guard_annotation.Set(true, 0)); |
| 188 | +} |
| 189 | + |
| 190 | +TEST_F(Annotation, |
| 191 | + CustomAnnotationSetShouldSucceedIfSpinGuardUnlockedAsynchronously) { |
| 192 | + constexpr crashpad::Annotation::Type kType = |
| 193 | + crashpad::Annotation::UserDefinedType(1); |
| 194 | + SpinGuardAnnotation spin_guard_annotation(kType, "spin-guard"); |
| 195 | + auto guard = spin_guard_annotation.TryCreateScopedSpinGuard(0); |
| 196 | + EXPECT_NE(std::nullopt, guard); |
| 197 | + // Pass the guard off to a background thread which unlocks it after 1 ms. |
| 198 | + constexpr uint64_t kSleepTimeNs = 1000000; // 1 ms |
| 199 | + ScopedSpinGuardUnlockThread unlock_thread(std::move(guard.value()), |
| 200 | + kSleepTimeNs); |
| 201 | + unlock_thread.Start(); |
| 202 | + |
| 203 | + // Try to set the annotation with a 100 ms timeout. |
| 204 | + constexpr uint64_t kSpinGuardTimeoutNanos = 100000000; // 100 ms |
| 205 | + |
| 206 | + // This should succeed after 1 ms, since the timeout is much larger than the |
| 207 | + // time the background thread holds the guard. |
| 208 | + EXPECT_TRUE(spin_guard_annotation.Set(true, kSpinGuardTimeoutNanos)); |
| 209 | + |
| 210 | + unlock_thread.Join(); |
| 211 | +} |
| 212 | + |
111 | 213 | TEST(StringAnnotation, ArrayOfString) { |
112 | 214 | static crashpad::StringAnnotation<4> annotations[] = { |
113 | 215 | {"test-1", crashpad::StringAnnotation<4>::Tag::kArray}, |
|
0 commit comments