• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 the V8 project 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 
5 #ifndef V8_TASKS_OPERATIONS_BARRIER_H_
6 #define V8_TASKS_OPERATIONS_BARRIER_H_
7 
8 #include <cstdint>
9 
10 #include "src/base/macros.h"
11 #include "src/base/platform/condition-variable.h"
12 #include "src/base/platform/mutex.h"
13 
14 namespace v8 {
15 namespace internal {
16 
17 // A thread-safe barrier to manage lifetime of muti-threaded operations.
18 //
19 // The barrier is used to determine if operations are allowed, and to keep track
20 // of how many are currently active. Users will call TryLock() before starting
21 // such operations. If the call succeeds the user can run the operation and the
22 // barrier will keep track of it until the user signals that the operation is
23 // completed. No operations are allowed after CancelAndWait() is called.
24 //
25 // There is no explicit way of telling the barrier when an operation is
26 // completed, instead for convenience TryLock() will return a RAII
27 // like object that will do so on destruction.
28 //
29 // For example:
30 //
31 // OperationsBarrier barrier_;
32 //
33 // void TearDown() {
34 //   barrier_.CancelAndWait();
35 // }
36 //
37 // void MaybeRunOperation() {
38 //   if (token = barrier_.TryLock()) Process();
39 // }
40 //
41 class V8_EXPORT_PRIVATE OperationsBarrier {
42  public:
43   // The owner of a Token which evaluates to true can safely perform an
44   // operation while being certain it happens-before CancelAndWait(). Releasing
45   // this Token relinquishes this right.
46   //
47   // This class is thread-safe.
48   class Token {
49    public:
50     Token() = default;
~Token()51     ~Token() {
52       if (outer_) outer_->Release();
53     }
54     Token(const Token&) = delete;
Token(Token && other)55     Token(Token&& other) V8_NOEXCEPT : outer_(other.outer_) {
56       other.outer_ = nullptr;
57     }
58 
59     Token& operator=(const Token&) = delete;
60     Token& operator=(Token&& other) V8_NOEXCEPT {
61       DCHECK_NE(this, &other);
62       if (outer_) outer_->Release();
63       outer_ = other.outer_;
64       other.outer_ = nullptr;
65       return *this;
66     }
67 
68     operator bool() const { return !!outer_; }
69 
70    private:
71     friend class OperationsBarrier;
Token(OperationsBarrier * outer)72     explicit Token(OperationsBarrier* outer) : outer_(outer) {
73       DCHECK_NOT_NULL(outer_);
74     }
75     OperationsBarrier* outer_ = nullptr;
76   };
77 
78   OperationsBarrier() = default;
79 
80   // Users must call CancelAndWait() before destroying an instance of this
81   // class.
~OperationsBarrier()82   ~OperationsBarrier() { DCHECK(cancelled_); }
83 
84   OperationsBarrier(const OperationsBarrier&) = delete;
85   OperationsBarrier& operator=(const OperationsBarrier&) = delete;
86 
87   // Returns a RAII like object that implicitly converts to true if operations
88   // are allowed i.e. if this call happens-before CancelAndWait(), otherwise the
89   // object will convert to false. On successful return, this OperationsBarrier
90   // will keep track of the operation until the returned object goes out of
91   // scope.
92   Token TryLock();
93 
94   // Prevents further calls to TryLock() from succeeding and waits for
95   // all the ongoing operations to complete.
96   //
97   // Attention: Can only be called once.
98   void CancelAndWait();
99 
cancelled()100   bool cancelled() const { return cancelled_; }
101 
102  private:
103   void Release();
104 
105   // Mutex and condition variable enabling concurrent register and removing, as
106   // well as waiting for background tasks on {CancelAndWait}.
107   base::Mutex mutex_;
108   base::ConditionVariable release_condition_;
109   bool cancelled_ = false;
110   size_t operations_count_{0};
111 };
112 
113 }  // namespace internal
114 }  // namespace v8
115 
116 #endif  // V8_TASKS_OPERATIONS_BARRIER_H_
117