latch_test.cpp 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include "common/latch.h"
  5. #include <gtest/gtest.h>
  6. #include <chrono>
  7. #include <thread>
  8. #include <vector>
  9. namespace Carbon {
  10. namespace {
  11. // Basic test for the Latch.
  12. TEST(LatchTest, Basic) {
  13. Latch latch;
  14. Latch::Handle handle = latch.Init();
  15. // Dropping a copy doesn't satisfy the latch.
  16. Latch::Handle handle_copy = handle;
  17. EXPECT_FALSE(std::move(handle_copy).Drop());
  18. // Can create more copies even after we start dropping.
  19. Latch::Handle handle_copy2 = handle;
  20. EXPECT_FALSE(std::move(handle_copy2).Drop());
  21. // Now drop the last handle.
  22. EXPECT_TRUE(std::move(handle).Drop());
  23. }
  24. // Tests that the on-zero callback is called.
  25. TEST(LatchTest, OnZeroCallback) {
  26. Latch latch;
  27. bool called = false;
  28. Latch::Handle handle = latch.Init([&] { called = true; });
  29. Latch::Handle handle2 = handle;
  30. Latch::Handle handle3 = handle;
  31. EXPECT_FALSE(called);
  32. EXPECT_FALSE(std::move(handle).Drop());
  33. EXPECT_FALSE(called);
  34. EXPECT_FALSE(std::move(handle2).Drop());
  35. EXPECT_FALSE(called);
  36. EXPECT_TRUE(std::move(handle3).Drop());
  37. EXPECT_TRUE(called);
  38. }
  39. // Tests moving a handle.
  40. TEST(LatchTest, MoveHandle) {
  41. Latch latch;
  42. bool called = false;
  43. Latch::Handle handle = latch.Init([&] { called = true; });
  44. Latch::Handle handle2 = std::move(handle);
  45. // Check that dropping the new handle satisfies the latch.
  46. EXPECT_FALSE(called);
  47. EXPECT_TRUE(std::move(handle2).Drop());
  48. EXPECT_TRUE(called);
  49. }
  50. // Test that creating and destroying a handle without dropping works.
  51. TEST(LatchTest, Destructor) {
  52. Latch latch;
  53. bool called = false;
  54. Latch::Handle handle = latch.Init([&] { called = true; });
  55. {
  56. // NOLINTNEXTLINE(performance-unnecessary-copy-initialization)
  57. Latch::Handle handle2 = handle;
  58. EXPECT_FALSE(called);
  59. }
  60. EXPECT_FALSE(called);
  61. EXPECT_TRUE(std::move(handle).Drop());
  62. EXPECT_TRUE(called);
  63. }
  64. // Tests calling `Init` more than once.
  65. TEST(LatchTest, Reuse) {
  66. Latch latch;
  67. bool called = false;
  68. Latch::Handle handle = latch.Init([&] { called = true; });
  69. Latch::Handle handle2 = handle;
  70. EXPECT_FALSE(called);
  71. EXPECT_FALSE(std::move(handle).Drop());
  72. EXPECT_FALSE(called);
  73. EXPECT_TRUE(std::move(handle2).Drop());
  74. EXPECT_TRUE(called);
  75. // Now initialize the latch again with a new closure.
  76. bool called2 = false;
  77. Latch::Handle handle3 = latch.Init([&] { called2 = true; });
  78. Latch::Handle handle4 = handle3;
  79. EXPECT_FALSE(called2);
  80. EXPECT_FALSE(std::move(handle3).Drop());
  81. EXPECT_FALSE(called2);
  82. EXPECT_TRUE(std::move(handle4).Drop());
  83. EXPECT_TRUE(called2);
  84. }
  85. // Tests the latch with multiple threads.
  86. TEST(LatchTest, MultiThreaded) {
  87. Latch latch;
  88. std::atomic<int> counter = 0;
  89. bool called = false;
  90. constexpr int NumThreads = 5;
  91. // The `on_zero` callback will be executed by the last thread to drop its
  92. // handle.
  93. auto handle = latch.Init([&] {
  94. // Check that all threads have done their work.
  95. EXPECT_EQ(counter.load(), NumThreads);
  96. called = true;
  97. });
  98. std::vector<std::thread> threads;
  99. threads.reserve(NumThreads);
  100. for (int i = 0; i < NumThreads; ++i) {
  101. threads.emplace_back([&, handle_copy = handle] {
  102. // Each thread has its own copy of the handle.
  103. // Simulate some work.
  104. std::this_thread::sleep_for(std::chrono::milliseconds(10));
  105. counter++;
  106. // The handle is dropped when the thread exits.
  107. });
  108. }
  109. // Drop the main thread's handle.
  110. std::move(handle).Drop();
  111. for (auto& thread : threads) {
  112. thread.join();
  113. }
  114. EXPECT_TRUE(called);
  115. }
  116. } // namespace
  117. } // namespace Carbon