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 !thread_ref_.load(std::memory_order_acquire).is_null(); 66 } 67 68 // Checks whether this object is bound to the current thread. Returns false if 69 // 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. IsBoundToCurrentThread()77 bool IsBoundToCurrentThread() const { 78 return thread_ref_.load(std::memory_order_relaxed) == 79 PlatformThread::CurrentRef(); 80 } 81 82 // Returns the `SequenceToken` associated with the bound thread. The caller 83 // must ensure that this is sequenced after `BindToCurrentThread()`. GetBoundSequenceToken()84 base::internal::SequenceToken GetBoundSequenceToken() const { 85 DCHECK(IsBound()); 86 return sequence_token_; 87 } 88 89 private: 90 friend class base::RefCountedThreadSafe<AssociatedThreadId>; 91 ~AssociatedThreadId(); 92 93 std::atomic<PlatformThreadRef> thread_ref_{}; 94 base::internal::SequenceToken sequence_token_; 95 }; 96 97 } // namespace internal 98 } // namespace sequence_manager 99 } // namespace base 100 101 #endif // BASE_TASK_SEQUENCE_MANAGER_ASSOCIATED_THREAD_ID_H_ 102