Index: mojo/public/cpp/bindings/lib/sync_handle_registry.cc |
diff --git a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc |
index 5ae763bbb24fe95ac796b840e78fe9ae4ae0d230..5c0262d5e2591b57da20e171095ad1d297e91c4b 100644 |
--- a/mojo/public/cpp/bindings/lib/sync_handle_registry.cc |
+++ b/mojo/public/cpp/bindings/lib/sync_handle_registry.cc |
@@ -4,35 +4,144 @@ |
#include "mojo/public/cpp/bindings/sync_handle_registry.h" |
+#include <unordered_map> |
+ |
#include "base/lazy_instance.h" |
#include "base/logging.h" |
+#include "base/sequence_token.h" |
+#include "base/sequenced_task_runner.h" |
#include "base/stl_util.h" |
#include "base/threading/thread_local.h" |
+#include "base/threading/thread_task_runner_handle.h" |
#include "mojo/public/c/system/core.h" |
namespace mojo { |
namespace { |
-base::LazyInstance<base::ThreadLocalPointer<SyncHandleRegistry>>::Leaky |
- g_current_sync_handle_watcher = LAZY_INSTANCE_INITIALIZER; |
+struct WorkerPoolSequenceTokenHasher { |
+ size_t operator()( |
+ const base::SequencedWorkerPool::SequenceToken& value) const { |
+ return std::hash<std::string>()(value.ToString()); |
+ } |
+}; |
+ |
+struct WorkerPoolSequenceTokenEquals { |
+ size_t operator()(const base::SequencedWorkerPool::SequenceToken& a, |
+ const base::SequencedWorkerPool::SequenceToken& b) const { |
+ return a.Equals(b); |
+ } |
+}; |
+ |
+struct SequenceTokenHasher { |
+ size_t operator()(const base::SequenceToken& value) const { |
+ return std::hash<decltype(value.ToInternalValue())>()( |
+ value.ToInternalValue()); |
+ } |
+}; |
+ |
+// Provides access to a sequence-local SyncHandleRegistry pointer. For |
+// standalone threads, thread-local storage is used. Otherwise, global maps |
+// keyed by SequenceToken are used if one is set for the current thread. Note |
+// that both types of SequenceToken are used: base::SequenceToken and |
+// base::SequencedWorkerPool::SequenceToken, in that order. |
+class SequenceLocalPointer { |
+ public: |
+ template <typename Factory> |
+ scoped_refptr<SyncHandleRegistry> GetOrCreate(const Factory& create) { |
+ SyncHandleRegistry** registry_pointer_storage = GetRegistryPointerStorage(); |
+ if (!registry_pointer_storage) { |
+ scoped_refptr<SyncHandleRegistry> registry(thread_local_storage_.Get()); |
+ if (registry) |
+ return registry; |
+ |
+ registry = create(); |
+ thread_local_storage_.Set(registry.get()); |
+ return registry; |
+ } |
+ |
+ scoped_refptr<SyncHandleRegistry> registry(*registry_pointer_storage); |
+ if (!registry) { |
+ registry = create(); |
+ *registry_pointer_storage = registry.get(); |
+ } |
+ return registry; |
+ } |
+ |
+ void Clear() { |
+ auto sequence_token = base::SequenceToken::GetForCurrentThread(); |
+ if (sequence_token.IsValid()) { |
+ base::AutoLock l(lock_); |
+ size_t erased = sequence_to_registry_map_.erase(sequence_token); |
+ DCHECK(erased); |
+ return; |
+ } |
+ auto worker_pool_sequence_token = |
+ base::SequencedWorkerPool::GetSequenceTokenForCurrentThread(); |
+ if (worker_pool_sequence_token.IsValid()) { |
+ base::AutoLock l(lock_); |
+ size_t erased = worker_pool_sequence_to_registry_map_.erase( |
+ worker_pool_sequence_token); |
+ DCHECK(erased); |
+ return; |
+ } |
+ thread_local_storage_.Set(nullptr); |
+ return; |
+ } |
+ |
+ private: |
+ // Returns a pointer to the storage for a |SyncHandleRegistry*| for the |
+ // current sequence. Note that the returned pointer may be safely read and |
+ // written without |lock_| held since this entry in the map is only accessed |
+ // on the current sequence. |
+ SyncHandleRegistry** GetRegistryPointerStorage() { |
+ auto sequence_token = base::SequenceToken::GetForCurrentThread(); |
+ if (sequence_token.IsValid()) { |
+ base::AutoLock l(lock_); |
+ return &sequence_to_registry_map_[sequence_token]; |
+ } |
+ auto worker_pool_sequence_token = |
+ base::SequencedWorkerPool::GetSequenceTokenForCurrentThread(); |
+ if (worker_pool_sequence_token.IsValid()) { |
+ base::AutoLock l(lock_); |
+ return &worker_pool_sequence_to_registry_map_[worker_pool_sequence_token]; |
+ } |
+ return nullptr; |
+ } |
+ |
+ base::ThreadLocalPointer<SyncHandleRegistry> thread_local_storage_; |
+ |
+ // Guards |sequence_to_registry_map_| and |
+ // |worker_pool_sequence_to_registry_map_|. |
+ base::Lock lock_; |
+ |
+ std::unordered_map<base::SequenceToken, |
+ SyncHandleRegistry*, |
+ SequenceTokenHasher> |
+ sequence_to_registry_map_; |
+ |
+ // TODO(sammc): Remove this once base::SequenceToken is used everywhere. |
+ std::unordered_map<base::SequencedWorkerPool::SequenceToken, |
+ SyncHandleRegistry*, |
+ WorkerPoolSequenceTokenHasher, |
+ WorkerPoolSequenceTokenEquals> |
+ worker_pool_sequence_to_registry_map_; |
+}; |
+ |
+base::LazyInstance<SequenceLocalPointer>::Leaky g_current_sync_handle_registry = |
+ LAZY_INSTANCE_INITIALIZER; |
} // namespace |
// static |
scoped_refptr<SyncHandleRegistry> SyncHandleRegistry::current() { |
- scoped_refptr<SyncHandleRegistry> result( |
- g_current_sync_handle_watcher.Pointer()->Get()); |
- if (!result) { |
- result = new SyncHandleRegistry(); |
- DCHECK_EQ(result.get(), g_current_sync_handle_watcher.Pointer()->Get()); |
- } |
- return result; |
+ return g_current_sync_handle_registry.Pointer()->GetOrCreate( |
+ []() { return make_scoped_refptr(new SyncHandleRegistry()); }); |
} |
bool SyncHandleRegistry::RegisterHandle(const Handle& handle, |
MojoHandleSignals handle_signals, |
const HandleCallback& callback) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(sequence_checker_.CalledOnValidSequence()); |
if (base::ContainsKey(handles_, handle)) |
return false; |
@@ -47,7 +156,7 @@ bool SyncHandleRegistry::RegisterHandle(const Handle& handle, |
} |
void SyncHandleRegistry::UnregisterHandle(const Handle& handle) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(sequence_checker_.CalledOnValidSequence()); |
if (!base::ContainsKey(handles_, handle)) |
return; |
@@ -59,7 +168,7 @@ void SyncHandleRegistry::UnregisterHandle(const Handle& handle) { |
bool SyncHandleRegistry::WatchAllHandles(const bool* should_stop[], |
size_t count) { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
+ DCHECK(sequence_checker_.CalledOnValidSequence()); |
MojoResult result; |
uint32_t num_ready_handles; |
@@ -100,27 +209,11 @@ SyncHandleRegistry::SyncHandleRegistry() { |
CHECK_EQ(MOJO_RESULT_OK, result); |
wait_set_handle_.reset(Handle(handle)); |
CHECK(wait_set_handle_.is_valid()); |
- |
- DCHECK(!g_current_sync_handle_watcher.Pointer()->Get()); |
- g_current_sync_handle_watcher.Pointer()->Set(this); |
} |
SyncHandleRegistry::~SyncHandleRegistry() { |
- DCHECK(thread_checker_.CalledOnValidThread()); |
- |
- // This object may be destructed after the thread local storage slot used by |
- // |g_current_sync_handle_watcher| is reset during thread shutdown. |
- // For example, another slot in the thread local storage holds a referrence to |
- // this object, and that slot is cleaned up after |
- // |g_current_sync_handle_watcher|. |
- if (!g_current_sync_handle_watcher.Pointer()->Get()) |
- return; |
- |
- // If this breaks, it is likely that the global variable is bulit into and |
- // accessed from multiple modules. |
- DCHECK_EQ(this, g_current_sync_handle_watcher.Pointer()->Get()); |
- |
- g_current_sync_handle_watcher.Pointer()->Set(nullptr); |
+ DCHECK(sequence_checker_.CalledOnValidSequence()); |
+ g_current_sync_handle_registry.Pointer()->Clear(); |
} |
} // namespace mojo |