operations_controller.h 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #ifndef BASE_TASK_COMMON_OPERATIONS_CONTROLLER_H_
  5. #define BASE_TASK_COMMON_OPERATIONS_CONTROLLER_H_
  6. #include <atomic>
  7. #include <cstdint>
  8. #include "base/synchronization/waitable_event.h"
  9. namespace base {
  10. namespace internal {
  11. // A lock-free thread-safe controller to manage critical multi-threaded
  12. // operations without locks.
  13. //
  14. // The controller is used to determine if operations are allowed, and to keep
  15. // track of how many are currently active. Users will call TryBeginOperation()
  16. // before starting such operations. If the call succeeds the user can run the
  17. // operation and the controller will keep track of it until the user signals
  18. // that the operation is completed. No operations are allowed before
  19. // StartAcceptingOperations() is called, or after
  20. // ShutdownAndWaitForZeroOperations() is called.
  21. //
  22. // There is no explicit way of telling the controller when an operation is
  23. // completed, instead for convenience TryBeginOperation() will return a RAII
  24. // like object that will do so on destruction.
  25. //
  26. // For example:
  27. //
  28. // OperationsController controller_;
  29. //
  30. // void SetUp() {
  31. // controller_.StartAcceptingOperations();
  32. // }
  33. //
  34. // void TearDown() {
  35. // controller_.ShutdownAndWaitForZeroOperations();
  36. // }
  37. //
  38. // void MaybeRunOperation() {
  39. // auto operation_token = controller_.TryBeginOperation();
  40. // if (operation_token) {
  41. // Process();
  42. // }
  43. // }
  44. //
  45. // This class is thread-safe.
  46. // But note that StartAcceptingOperations can never be called after
  47. // ShutdownAndWaitForZeroOperations.
  48. class BASE_EXPORT OperationsController {
  49. public:
  50. // The owner of an OperationToken which evaluates to true can safely perform
  51. // an operation while being certain it happens-after
  52. // StartAcceptingOperations() and happens-before
  53. // ShutdownAndWaitForZeroOperations(). Releasing this OperationToken
  54. // relinquishes this right.
  55. //
  56. // This class is thread-safe
  57. class OperationToken {
  58. public:
  59. ~OperationToken() {
  60. if (outer_)
  61. outer_->DecrementBy(1);
  62. }
  63. OperationToken(const OperationToken&) = delete;
  64. OperationToken(OperationToken&& other) {
  65. this->outer_ = other.outer_;
  66. other.outer_ = nullptr;
  67. }
  68. operator bool() const { return !!outer_; }
  69. private:
  70. friend class OperationsController;
  71. explicit OperationToken(OperationsController* outer) : outer_(outer) {}
  72. OperationsController* outer_;
  73. };
  74. OperationsController();
  75. // Users must call ShutdownAndWaitForZeroOperations() before destroying an
  76. // instance of this class if StartAcceptingOperations() was called.
  77. ~OperationsController();
  78. OperationsController(const OperationsController&) = delete;
  79. OperationsController& operator=(const OperationsController&) = delete;
  80. // Starts to accept operations (before this point TryBeginOperation() returns
  81. // an invalid token). Returns true if an attempt to perform an operation was
  82. // made and denied before StartAcceptingOperations() was called. Can be called
  83. // at most once, never after ShutdownAndWaitForZeroOperations().
  84. bool StartAcceptingOperations();
  85. // Returns a RAII like object that implicitly converts to true if operations
  86. // are allowed i.e. if this call happens-after StartAcceptingOperations() and
  87. // happens-before Shutdown(), otherwise the object will convert to false. On
  88. // successful return, this OperationsController will keep track of the
  89. // operation until the returned object goes out of scope.
  90. OperationToken TryBeginOperation();
  91. // Prevents further calls to TryBeginOperation() from succeeding and waits for
  92. // all the ongoing operations to complete.
  93. //
  94. // Attention: Can only be called once.
  95. void ShutdownAndWaitForZeroOperations();
  96. private:
  97. // Atomic representation of the state of this class. We use the upper 2 bits
  98. // to keep track of flag like values and the remainder bits are used as a
  99. // counter. The 2 flags are used to represent 3 different states:
  100. //
  101. // State | AcceptOperations Bit | ShuttingDown Bit
  102. // --------------------------------------------------------------
  103. // kRejectingOperations | 0 | 0
  104. // kAcceptingOperations | 1 | 0
  105. // kShuttingDown | * | 1
  106. //
  107. // The counter keeps track of the rejected operations when we are in
  108. // the kRejectingOperations state, the number of inflight operations
  109. // otherwise. If the count reaches zero and we are in the shutting down state
  110. // |shutdown_complete_| will be signaled.
  111. static constexpr uint32_t kShuttingDownBitMask = uint32_t{1} << 31;
  112. static constexpr uint32_t kAcceptingOperationsBitMask = uint32_t{1} << 30;
  113. static constexpr uint32_t kFlagsBitMask =
  114. (kShuttingDownBitMask | kAcceptingOperationsBitMask);
  115. static constexpr uint32_t kCountBitMask = ~kFlagsBitMask;
  116. enum class State {
  117. kRejectingOperations,
  118. kAcceptingOperations,
  119. kShuttingDown,
  120. };
  121. // Helper methods for the bit fiddling. Pass a |state_and_count_| value to
  122. // extract state or count out of it.
  123. static uint32_t ExtractCount(uint32_t value) { return value & kCountBitMask; }
  124. static State ExtractState(uint32_t value);
  125. // Decrements the counter by |n| and signals |shutdown_complete_| if needed.
  126. void DecrementBy(uint32_t n);
  127. std::atomic<uint32_t> state_and_count_{0};
  128. WaitableEvent shutdown_complete_;
  129. };
  130. } // namespace internal
  131. } // namespace base
  132. #endif // BASE_TASK_COMMON_OPERATIONS_CONTROLLER_H_