latch.h 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  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. #ifndef CARBON_COMMON_LATCH_H_
  5. #define CARBON_COMMON_LATCH_H_
  6. #include <atomic>
  7. #include "llvm/ADT/FunctionExtras.h"
  8. namespace Carbon {
  9. // A synchronization primitive similar to `std::latch` to coordinate starting
  10. // some action once all of a set of other actions complete.
  11. //
  12. // Users initialize the latch (with `Init`), and receive a handle RAII object.
  13. // This handle can be copied, and the latch is satisfied when the last copy of
  14. // the handle returned by `Init` is destroyed.
  15. //
  16. // The latch synchronizes between every destruction of a handle and the
  17. // destruction of the last handle, allowing code that runs after the latch is
  18. // satisfied to access everything written by any thread that destroyed a handle.
  19. // For more details of the synchronization mechanics, see the comments on `Inc`
  20. // and `Dec` that implement this logic.
  21. //
  22. // This type also supports holding a closure to run when satisfied to simplify
  23. // patterns where that body of code is easier to express at the start of work
  24. // being synchronized instead of as each work item completes.
  25. //
  26. // The initialization API is separate from the constructor both for convenience
  27. // and to enable it to provide the initial handle. This makes it easy to build
  28. // constructively correct code where each work unit holds a handle until
  29. // finished, including the initializer of the latch, often using by-value
  30. // captures in a lambda that does the work.
  31. class Latch {
  32. public:
  33. class Handle;
  34. Latch() = default;
  35. Latch(const Latch&) = delete;
  36. Latch(Latch&&) = delete;
  37. // Initialize a latch and get the initial handle to it.
  38. //
  39. // When the last copy of the returned handle is destroyed, the latch will be
  40. // satisfied.
  41. //
  42. // A closure may be provided which will be called when that last handle is
  43. // destroyed. Note that the closure will run on whatever thread executes the
  44. // last handle destruction. Typically, the closure here should _schedule_ the
  45. // next step of work on some thread pool rather than performing it directly.
  46. //
  47. // Once this method is called, it cannot be called again until all handles are
  48. // destroyed and the latch is satisfied. It can then be called again to get a
  49. // fresh handle (and provide a new closure if desired).
  50. auto Init(llvm::unique_function<auto()->void> on_zero = [] {}) -> Handle;
  51. private:
  52. // Increments the latch's counter.
  53. //
  54. // This is thread-safe, and may be called concurrently on multiple threads,
  55. // and may be called concurrently with `Dec`. However, the caller _must_ call
  56. // `Inc` and then `Dec`, and provide some happens-before relationship between
  57. // the `Inc` and `Dec`. Typically, this is done with either same-thread
  58. // happens-before, or because some other synchronization event such as
  59. // starting a thread or popping a task from a thread pool provides the
  60. // inter-thread happens-before relationship.
  61. auto Inc() -> void;
  62. // Decrements the latch's counter, and returns true when it reaches zero.
  63. //
  64. // This is thread-safe, and may be called concurrently with other calls to
  65. // `Dec` or `Inc`.
  66. //
  67. // It also ensures that all threads which call `Dec` and receive `false`
  68. // synchronize-with the thread that calls `Dec` and receives `true`. As a
  69. // consequence everything that happens-before the call to `Dec` has an
  70. // inter-thread happens-before for any code when `Dec` returns `true`.
  71. //
  72. // Note that there is no guarantee of inter-thread happens-before to
  73. // operations after a `Dec` call that returns `false`.
  74. auto Dec() -> bool;
  75. std::atomic<int> count_;
  76. llvm::unique_function<auto()->void> on_zero_;
  77. };
  78. // A copyable RAII handle around a `Latch`.
  79. //
  80. // When the last copy of a handle returned by `Latch::Init` is destroyed, the
  81. // latch is considered satisfied. Copying is supported by incrementing the
  82. // count of the latch. That increment can always be performed because it starts
  83. // from a live handle and so the count cannot have reached zero.
  84. //
  85. // For more details, see the `Latch` class.
  86. class Latch::Handle {
  87. public:
  88. Handle(const Handle& arg) : latch_(arg.latch_) {
  89. if (latch_) {
  90. arg.latch_->Inc();
  91. }
  92. }
  93. Handle(Handle&& arg) noexcept : latch_(std::exchange(arg.latch_, nullptr)) {}
  94. ~Handle() {
  95. if (latch_) {
  96. latch_->Dec();
  97. }
  98. }
  99. // Drops a handle explicitly, rather than waiting for it to fall out of scope.
  100. //
  101. // This also allows observing whether the underlying latch is satisfied.
  102. // Calls to this function synchronize with all other drops or destructions of
  103. // latch handles when it returns true, and only the last will return true.
  104. auto Drop() && -> bool {
  105. bool last = latch_->Dec();
  106. latch_ = nullptr;
  107. return last;
  108. }
  109. private:
  110. friend Latch;
  111. // Private constructor used by `Latch::Init` to create the initial handle for
  112. // a latch.
  113. explicit Handle(Latch* latch) : latch_(latch) { latch_->Inc(); }
  114. Latch* latch_ = nullptr;
  115. };
  116. } // namespace Carbon
  117. #endif // CARBON_COMMON_LATCH_H_