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_SEQUENCE_MANAGER_ASSOCIATED_THREAD_ID_H_ 6 #define BASE_TASK_SEQUENCE_MANAGER_ASSOCIATED_THREAD_ID_H_ 7 8 #include <atomic> 9 #include <memory> 10 #include <optional> 11 12 #include "base/base_export.h" 13 #include "base/memory/ref_counted.h" 14 #include "base/sequence_checker.h" 15 #include "base/sequence_token.h" 16 #include "base/threading/platform_thread.h" 17 #include "base/threading/platform_thread_ref.h" 18 #include "base/threading/thread_checker.h" 19 20 namespace base { 21 namespace sequence_manager { 22 namespace internal { 23 24 // TODO(eseckler): Make this owned by SequenceManager once the TaskQueue 25 // refactor has happened (https://crbug.com/865411). 26 // 27 // This class is thread-safe. But see notes about memory ordering guarantees for 28 // the various methods. 29 class BASE_EXPORT AssociatedThreadId 30 : public base::RefCountedThreadSafe<AssociatedThreadId> { 31 public: 32 AssociatedThreadId(); 33 34 // TODO(eseckler): Replace thread_checker with sequence_checker everywhere. 35 THREAD_CHECKER(thread_checker); 36 SEQUENCE_CHECKER(sequence_checker); 37 CreateUnbound()38 static scoped_refptr<AssociatedThreadId> CreateUnbound() { 39 return MakeRefCounted<AssociatedThreadId>(); 40 } 41 CreateBound()42 static scoped_refptr<AssociatedThreadId> CreateBound() { 43 auto associated_thread = MakeRefCounted<AssociatedThreadId>(); 44 associated_thread->BindToCurrentThread(); 45 return associated_thread; 46 } 47 48 // Rebind the associated thread to the current thread. This allows creating 49 // the SequenceManager and TaskQueues on a different thread/sequence than the 50 // one it will manage. 51 // 52 // Can only be called once. 53 void BindToCurrentThread(); 54 55 // Checks whether this object has already been bound to a thread. 56 // 57 // This method guarantees a happens-before ordering with 58 // BindToCurrentThread(), that is all memory writes that happened-before the 59 // call to BindToCurrentThread() will become visible side-effects in the 60 // current thread. 61 // 62 // By the time this returns false, the thread may have racily be bound. 63 // However, a bound thread is never unbound. IsBound()64 bool IsBound() const { 65 return !bound_thread_ref_.load(std::memory_order_acquire).is_null(); 66 } 67 68 // Returns whether this object is bound to the current thread. Returns false 69 // if this object is not bound to any thread. 70 // 71 // Note that this method provides no memory ordering guarantees but those are 72 // not really needed. If this method returns true we are on the same thread 73 // that called BindToCurrentThread(). If the method returns false this object 74 // could be unbound, so there is no possible ordering. 75 // 76 // Attention:: The result might be stale by the time this method returns. 77 bool IsBoundToCurrentThread() const; 78 79 // Asserts that the current thread runs in sequence with the thread to which 80 // this object is bound. 81 void AssertInSequenceWithCurrentThread() const; 82 83 // Returns the `SequenceToken` associated with the bound thread. The caller 84 // must ensure that this is sequenced after `BindToCurrentThread()`. GetBoundSequenceToken()85 base::internal::SequenceToken GetBoundSequenceToken() const { 86 DCHECK(IsBound()); 87 return sequence_token_; 88 } 89 90 // Indicates that the current thread starts/stops running in sequence with the 91 // bound thread. `IsBoundToCurrentThread()` and 92 // `AssertInSequenceWithCurrentThread()` fail if invoked on the bound thread 93 // when another thread runs in sequence with it (that indicates that mutual 94 // exclusion is not guaranteed). 95 void StartInSequenceWithCurrentThread(); 96 void StopInSequenceWithCurrentThread(); 97 98 private: 99 friend class base::RefCountedThreadSafe<AssociatedThreadId>; 100 ~AssociatedThreadId(); 101 102 std::atomic<PlatformThreadRef> bound_thread_ref_; 103 std::atomic<PlatformThreadRef> in_sequence_thread_ref_; 104 base::internal::SequenceToken sequence_token_; 105 }; 106 107 } // namespace internal 108 } // namespace sequence_manager 109 } // namespace base 110 111 #endif // BASE_TASK_SEQUENCE_MANAGER_ASSOCIATED_THREAD_ID_H_ 112