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