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 11 #include "base/base_export.h" 12 #include "base/memory/ref_counted.h" 13 #include "base/sequence_checker.h" 14 #include "base/threading/platform_thread.h" 15 #include "base/threading/platform_thread_ref.h" 16 #include "base/threading/thread_checker.h" 17 #include "third_party/abseil-cpp/absl/types/optional.h" 18 19 namespace base { 20 namespace sequence_manager { 21 namespace internal { 22 23 // TODO(eseckler): Make this owned by SequenceManager once the TaskQueue 24 // refactor has happened (https://crbug.com/865411). 25 // 26 // This class is thread-safe. But see notes about memory ordering guarantees for 27 // the various methods. 28 class BASE_EXPORT AssociatedThreadId 29 : public base::RefCountedThreadSafe<AssociatedThreadId> { 30 public: 31 AssociatedThreadId(); 32 33 // TODO(eseckler): Replace thread_checker with sequence_checker everywhere. 34 THREAD_CHECKER(thread_checker); 35 SEQUENCE_CHECKER(sequence_checker); 36 CreateUnbound()37 static scoped_refptr<AssociatedThreadId> CreateUnbound() { 38 return MakeRefCounted<AssociatedThreadId>(); 39 } 40 CreateBound()41 static scoped_refptr<AssociatedThreadId> CreateBound() { 42 auto associated_thread = MakeRefCounted<AssociatedThreadId>(); 43 associated_thread->BindToCurrentThread(); 44 return associated_thread; 45 } 46 47 // Rebind the associated thread to the current thread. This allows creating 48 // the SequenceManager and TaskQueues on a different thread/sequence than the 49 // one it will manage. 50 // 51 // Can only be called once. 52 void BindToCurrentThread(); 53 54 // Checks whether this object has already been bound to a thread. 55 // 56 // This method guarantees a happens-before ordering with 57 // BindToCurrentThread(), that is all memory writes that happened-before the 58 // call to BindToCurrentThread() will become visible side-effects in the 59 // current thread. 60 // 61 // Attention: The result might be stale by the time this method returns. IsBound()62 bool IsBound() const { 63 return !thread_ref_.load(std::memory_order_acquire).is_null(); 64 } 65 66 // Checks whether this object is bound to the current thread. Returns false if 67 // this object is not bound to any thread. 68 // 69 // Note that this method provides no memory ordering guarantees but those are 70 // not really needed. If this method returns true we are on the same thread 71 // that called BindToCurrentThread(). If the method returns false this object 72 // could be unbound, so there is no possible ordering. 73 // 74 // Attention:: The result might be stale by the time this method returns. IsBoundToCurrentThread()75 bool IsBoundToCurrentThread() const { 76 return thread_ref_.load(std::memory_order_relaxed) == 77 PlatformThread::CurrentRef(); 78 } 79 80 private: 81 friend class base::RefCountedThreadSafe<AssociatedThreadId>; 82 ~AssociatedThreadId(); 83 84 std::atomic<PlatformThreadRef> thread_ref_{}; 85 }; 86 87 } // namespace internal 88 } // namespace sequence_manager 89 } // namespace base 90 91 #endif // BASE_TASK_SEQUENCE_MANAGER_ASSOCIATED_THREAD_ID_H_ 92