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