diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:20:33 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-05-15 10:28:57 +0000 |
commit | d17ea114e5ef69ad5d5d7413280a13e6428098aa (patch) | |
tree | 2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/base/threading | |
parent | 8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (diff) |
BASELINE: Update Chromium to 67.0.3396.47
Change-Id: Idcb1341782e417561a2473eeecc82642dafda5b7
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
Diffstat (limited to 'chromium/base/threading')
18 files changed, 553 insertions, 137 deletions
diff --git a/chromium/base/threading/platform_thread_android.cc b/chromium/base/threading/platform_thread_android.cc index d4c9a04856b..fd90d35102f 100644 --- a/chromium/base/threading/platform_thread_android.cc +++ b/chromium/base/threading/platform_thread_android.cc @@ -60,7 +60,7 @@ bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { } // namespace internal void PlatformThread::SetName(const std::string& name) { - ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); + ThreadIdNameManager::GetInstance()->SetName(name); // Like linux, on android we can get the thread names to show up in the // debugger by setting the process name for the LWP. diff --git a/chromium/base/threading/platform_thread_fuchsia.cc b/chromium/base/threading/platform_thread_fuchsia.cc index 1939f820912..eb06795c685 100644 --- a/chromium/base/threading/platform_thread_fuchsia.cc +++ b/chromium/base/threading/platform_thread_fuchsia.cc @@ -27,8 +27,7 @@ void PlatformThread::SetName(const std::string& name) { name.data(), name.size()); DCHECK_EQ(status, ZX_OK); - ThreadIdNameManager::GetInstance()->SetName(PlatformThread::CurrentId(), - name); + ThreadIdNameManager::GetInstance()->SetName(name); } // static diff --git a/chromium/base/threading/platform_thread_linux.cc b/chromium/base/threading/platform_thread_linux.cc index 9917a7b1fd6..190acedf7c5 100644 --- a/chromium/base/threading/platform_thread_linux.cc +++ b/chromium/base/threading/platform_thread_linux.cc @@ -126,7 +126,7 @@ bool GetCurrentThreadPriorityForPlatform(ThreadPriority* priority) { // static void PlatformThread::SetName(const std::string& name) { - ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); + ThreadIdNameManager::GetInstance()->SetName(name); #if !defined(OS_NACL) && !defined(OS_AIX) // On linux we can get the thread names to show up in the debugger by setting diff --git a/chromium/base/threading/platform_thread_mac.mm b/chromium/base/threading/platform_thread_mac.mm index ea180618522..39d979d6600 100644 --- a/chromium/base/threading/platform_thread_mac.mm +++ b/chromium/base/threading/platform_thread_mac.mm @@ -49,7 +49,7 @@ void InitThreading() { // static void PlatformThread::SetName(const std::string& name) { - ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); + ThreadIdNameManager::GetInstance()->SetName(name); // Mac OS X does not expose the length limit of the name, so // hardcode it. diff --git a/chromium/base/threading/platform_thread_win.cc b/chromium/base/threading/platform_thread_win.cc index c53d24e57b5..daccc0e72d0 100644 --- a/chromium/base/threading/platform_thread_win.cc +++ b/chromium/base/threading/platform_thread_win.cc @@ -175,7 +175,7 @@ void PlatformThread::Sleep(TimeDelta duration) { // static void PlatformThread::SetName(const std::string& name) { - ThreadIdNameManager::GetInstance()->SetName(CurrentId(), name); + ThreadIdNameManager::GetInstance()->SetName(name); // The SetThreadDescription API works even if no debugger is attached. auto set_thread_description_func = @@ -188,9 +188,7 @@ void PlatformThread::SetName(const std::string& name) { // The debugger needs to be around to catch the name in the exception. If // there isn't a debugger, we are just needlessly throwing an exception. - // If this image file is instrumented, we raise the exception anyway - // to provide the profiler with human-readable thread names. - if (!::IsDebuggerPresent() && !base::debug::IsBinaryInstrumented()) + if (!::IsDebuggerPresent()) return; SetNameInternal(CurrentId(), name.c_str()); diff --git a/chromium/base/threading/post_task_and_reply_impl.cc b/chromium/base/threading/post_task_and_reply_impl.cc index 4eba45a1c07..5aacdada670 100644 --- a/chromium/base/threading/post_task_and_reply_impl.cc +++ b/chromium/base/threading/post_task_and_reply_impl.cc @@ -10,7 +10,6 @@ #include "base/debug/leak_annotations.h" #include "base/logging.h" #include "base/memory/ref_counted.h" -#include "base/sequence_checker.h" #include "base/sequenced_task_runner.h" #include "base/threading/sequenced_task_runner_handle.h" @@ -18,56 +17,93 @@ namespace base { namespace { -// This relay class remembers the sequence that it was created on, and ensures -// that both the |task| and |reply| Closures are deleted on this same sequence. -// Also, |task| is guaranteed to be deleted before |reply| is run or deleted. -// -// If RunReplyAndSelfDestruct() doesn't run because the originating execution -// context is no longer available, then the |task| and |reply| Closures are -// leaked. Leaking is considered preferable to having a thread-safetey -// violations caused by invoking the Closure destructor on the wrong sequence. class PostTaskAndReplyRelay { public: PostTaskAndReplyRelay(const Location& from_here, OnceClosure task, OnceClosure reply) - : sequence_checker_(), - from_here_(from_here), - origin_task_runner_(SequencedTaskRunnerHandle::Get()), - reply_(std::move(reply)), - task_(std::move(task)) {} + : from_here_(from_here), + task_(std::move(task)), + reply_(std::move(reply)) {} + PostTaskAndReplyRelay(PostTaskAndReplyRelay&&) = default; ~PostTaskAndReplyRelay() { - DCHECK(sequence_checker_.CalledOnValidSequence()); + if (reply_) { + // This can run: + // 1) On origin sequence, when: + // 1a) Posting |task_| fails. + // 1b) |reply_| is cancelled before running. + // 1c) The DeleteSoon() below is scheduled. + // 2) On destination sequence, when: + // 2a) |task_| is cancelled before running. + // 2b) Posting |reply_| fails. + + if (!reply_task_runner_->RunsTasksInCurrentSequence()) { + // Case 2a) or 2b). + // + // Destroy callbacks asynchronously on |reply_task_runner| since their + // destructors can rightfully be affine to it. As always, DeleteSoon() + // might leak its argument if the target execution environment is + // shutdown (e.g. MessageLoop deleted, TaskScheduler shutdown). + // + // Note: while it's obvious why |reply_| can be affine to + // |reply_task_runner|, the reason that |task_| can also be affine to it + // is that it if neither tasks ran, |task_| may still hold an object + // which was intended to be moved to |reply_| when |task_| ran (such an + // object's destruction can be affine to |reply_task_runner_| -- e.g. + // https://crbug.com/829122). + auto relay_to_delete = + std::make_unique<PostTaskAndReplyRelay>(std::move(*this)); + ANNOTATE_LEAKING_OBJECT_PTR(relay_to_delete.get()); + reply_task_runner_->DeleteSoon(from_here_, std::move(relay_to_delete)); + } + + // Case 1a), 1b), 1c). + // + // Callbacks will be destroyed synchronously at the end of this scope. + } else { + // This can run when both callbacks have run or have been moved to another + // PostTaskAndReplyRelay instance. If |reply_| is null, |task_| must be + // null too. + DCHECK(!task_); + } } - void RunTaskAndPostReply() { - std::move(task_).Run(); - origin_task_runner_->PostTask( - from_here_, BindOnce(&PostTaskAndReplyRelay::RunReplyAndSelfDestruct, - base::Unretained(this))); - } + // No assignment operator because of const members. + PostTaskAndReplyRelay& operator=(PostTaskAndReplyRelay&&) = delete; - private: - void RunReplyAndSelfDestruct() { - DCHECK(sequence_checker_.CalledOnValidSequence()); + // Static function is used because it is not possible to bind a method call to + // a non-pointer type. + static void RunTaskAndPostReply(PostTaskAndReplyRelay relay) { + DCHECK(relay.task_); + std::move(relay.task_).Run(); - // Ensure |task_| has already been released before |reply_| to ensure that - // no one accidentally depends on |task_| keeping one of its arguments alive - // while |reply_| is executing. - DCHECK(!task_); + // Keep a reference to the reply TaskRunner for the PostTask() call before + // |relay| is moved into a callback. + scoped_refptr<SequencedTaskRunner> reply_task_runner = + relay.reply_task_runner_; - std::move(reply_).Run(); + reply_task_runner->PostTask( + relay.from_here_, + BindOnce(&PostTaskAndReplyRelay::RunReply, std::move(relay))); + } - // Cue mission impossible theme. - delete this; + private: + // Static function is used because it is not possible to bind a method call to + // a non-pointer type. + static void RunReply(PostTaskAndReplyRelay relay) { + DCHECK(!relay.task_); + DCHECK(relay.reply_); + std::move(relay.reply_).Run(); } - const SequenceChecker sequence_checker_; const Location from_here_; - const scoped_refptr<SequencedTaskRunner> origin_task_runner_; - OnceClosure reply_; OnceClosure task_; + OnceClosure reply_; + const scoped_refptr<SequencedTaskRunner> reply_task_runner_ = + SequencedTaskRunnerHandle::Get(); + + DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyRelay); }; } // namespace @@ -77,23 +113,13 @@ namespace internal { bool PostTaskAndReplyImpl::PostTaskAndReply(const Location& from_here, OnceClosure task, OnceClosure reply) { - DCHECK(!task.is_null()) << from_here.ToString(); - DCHECK(!reply.is_null()) << from_here.ToString(); - PostTaskAndReplyRelay* relay = - new PostTaskAndReplyRelay(from_here, std::move(task), std::move(reply)); - // PostTaskAndReplyRelay self-destructs after executing |reply|. On the flip - // side though, it is intentionally leaked if the |task| doesn't complete - // before the origin sequence stops executing tasks. Annotate |relay| as leaky - // to avoid having to suppress every callsite which happens to flakily trigger - // this race. - ANNOTATE_LEAKING_OBJECT_PTR(relay); - if (!PostTask(from_here, BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply, - Unretained(relay)))) { - delete relay; - return false; - } + DCHECK(task) << from_here.ToString(); + DCHECK(reply) << from_here.ToString(); - return true; + return PostTask(from_here, + BindOnce(&PostTaskAndReplyRelay::RunTaskAndPostReply, + PostTaskAndReplyRelay(from_here, std::move(task), + std::move(reply)))); } } // namespace internal diff --git a/chromium/base/threading/post_task_and_reply_impl.h b/chromium/base/threading/post_task_and_reply_impl.h index 696a655db0b..54038ceecd1 100644 --- a/chromium/base/threading/post_task_and_reply_impl.h +++ b/chromium/base/threading/post_task_and_reply_impl.h @@ -18,17 +18,20 @@ namespace internal { // custom execution context. // // If you're looking for a concrete implementation of PostTaskAndReply, you -// probably want base::TaskRunner. -// -// TODO(fdoray): Move this to the anonymous namespace of base/task_runner.cc. +// probably want base::TaskRunner or base/task_scheduler/post_task.h class BASE_EXPORT PostTaskAndReplyImpl { public: virtual ~PostTaskAndReplyImpl() = default; - // Posts |task| by calling PostTask(). On completion, |reply| is posted to the - // sequence or thread that called this. Can only be called when - // SequencedTaskRunnerHandle::IsSet(). Both |task| and |reply| are guaranteed - // to be deleted on the sequence or thread that called this. + // Posts |task| by calling PostTask(). On completion, posts |reply| to the + // origin sequence. Can only be called when + // SequencedTaskRunnerHandle::IsSet(). Each callback is deleted synchronously + // after running, or scheduled for asynchronous deletion on the origin + // sequence if it can't run (e.g. if a TaskRunner skips it on shutdown). See + // SequencedTaskRunner::DeleteSoon() for when objects scheduled for + // asynchronous deletion can be leaked. Note: All //base task posting APIs + // require callbacks to support deletion on the posting sequence if they can't + // be scheduled. bool PostTaskAndReply(const Location& from_here, OnceClosure task, OnceClosure reply); diff --git a/chromium/base/threading/post_task_and_reply_impl_unittest.cc b/chromium/base/threading/post_task_and_reply_impl_unittest.cc index 6678c95a8d2..319327dfea8 100644 --- a/chromium/base/threading/post_task_and_reply_impl_unittest.cc +++ b/chromium/base/threading/post_task_and_reply_impl_unittest.cc @@ -6,12 +6,12 @@ #include <utility> +#include "base/auto_reset.h" #include "base/bind.h" #include "base/bind_helpers.h" #include "base/macros.h" #include "base/memory/ref_counted.h" -#include "base/test/test_simple_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" +#include "base/test/test_mock_time_task_runner.h" #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" @@ -57,53 +57,141 @@ class MockObject { MockObject() = default; MOCK_METHOD1(Task, void(scoped_refptr<ObjectToDelete>)); - MOCK_METHOD0(Reply, void()); + MOCK_METHOD1(Reply, void(scoped_refptr<ObjectToDelete>)); private: DISALLOW_COPY_AND_ASSIGN(MockObject); }; +class MockRunsTasksInCurrentSequenceTaskRunner : public TestMockTimeTaskRunner { + public: + MockRunsTasksInCurrentSequenceTaskRunner( + TestMockTimeTaskRunner::Type type = + TestMockTimeTaskRunner::Type::kStandalone) + : TestMockTimeTaskRunner(type) {} + + void RunUntilIdleWithRunsTasksInCurrentSequence() { + AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true); + RunUntilIdle(); + } + + void ClearPendingTasksWithRunsTasksInCurrentSequence() { + AutoReset<bool> reset(&runs_tasks_in_current_sequence_, true); + ClearPendingTasks(); + } + + // TestMockTimeTaskRunner: + bool RunsTasksInCurrentSequence() const override { + return runs_tasks_in_current_sequence_; + } + + private: + ~MockRunsTasksInCurrentSequenceTaskRunner() override = default; + + bool runs_tasks_in_current_sequence_ = false; + + DISALLOW_COPY_AND_ASSIGN(MockRunsTasksInCurrentSequenceTaskRunner); +}; + +class PostTaskAndReplyImplTest : public testing::Test { + protected: + PostTaskAndReplyImplTest() = default; + + void PostTaskAndReplyToMockObject() { + // Expect the post to succeed. + EXPECT_TRUE( + PostTaskAndReplyTaskRunner(post_runner_.get()) + .PostTaskAndReply( + FROM_HERE, + BindOnce(&MockObject::Task, Unretained(&mock_object_), + MakeRefCounted<ObjectToDelete>(&delete_task_flag_)), + BindOnce(&MockObject::Reply, Unretained(&mock_object_), + MakeRefCounted<ObjectToDelete>(&delete_reply_flag_)))); + + // Expect the first task to be posted to |post_runner_|. + EXPECT_TRUE(post_runner_->HasPendingTask()); + EXPECT_FALSE(reply_runner_->HasPendingTask()); + EXPECT_FALSE(delete_task_flag_); + EXPECT_FALSE(delete_reply_flag_); + } + + scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> post_runner_ = + MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>(); + scoped_refptr<MockRunsTasksInCurrentSequenceTaskRunner> reply_runner_ = + MakeRefCounted<MockRunsTasksInCurrentSequenceTaskRunner>( + TestMockTimeTaskRunner::Type::kBoundToThread); + testing::StrictMock<MockObject> mock_object_; + bool delete_task_flag_ = false; + bool delete_reply_flag_ = false; + + private: + DISALLOW_COPY_AND_ASSIGN(PostTaskAndReplyImplTest); +}; + } // namespace -TEST(PostTaskAndReplyImplTest, PostTaskAndReply) { - scoped_refptr<TestSimpleTaskRunner> post_runner(new TestSimpleTaskRunner); - scoped_refptr<TestSimpleTaskRunner> reply_runner(new TestSimpleTaskRunner); - ThreadTaskRunnerHandle task_runner_handle(reply_runner); - - testing::StrictMock<MockObject> mock_object; - bool delete_flag = false; - - EXPECT_TRUE(PostTaskAndReplyTaskRunner(post_runner.get()) - .PostTaskAndReply( - FROM_HERE, - BindOnce(&MockObject::Task, Unretained(&mock_object), - MakeRefCounted<ObjectToDelete>(&delete_flag)), - BindOnce(&MockObject::Reply, Unretained(&mock_object)))); - - // Expect the task to be posted to |post_runner|. - EXPECT_TRUE(post_runner->HasPendingTask()); - EXPECT_FALSE(reply_runner->HasPendingTask()); - EXPECT_FALSE(delete_flag); - - EXPECT_CALL(mock_object, Task(_)); - post_runner->RunUntilIdle(); - testing::Mock::VerifyAndClear(&mock_object); - - // |task| should have been deleted right after being run. - EXPECT_TRUE(delete_flag); - - // Expect the reply to be posted to |reply_runner|. - EXPECT_FALSE(post_runner->HasPendingTask()); - EXPECT_TRUE(reply_runner->HasPendingTask()); - - EXPECT_CALL(mock_object, Reply()); - reply_runner->RunUntilIdle(); - testing::Mock::VerifyAndClear(&mock_object); - EXPECT_TRUE(delete_flag); - - // Expect no pending task in |post_runner| and |reply_runner|. - EXPECT_FALSE(post_runner->HasPendingTask()); - EXPECT_FALSE(reply_runner->HasPendingTask()); +TEST_F(PostTaskAndReplyImplTest, PostTaskAndReply) { + PostTaskAndReplyToMockObject(); + + EXPECT_CALL(mock_object_, Task(_)); + post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); + testing::Mock::VerifyAndClear(&mock_object_); + // The task should have been deleted right after being run. + EXPECT_TRUE(delete_task_flag_); + EXPECT_FALSE(delete_reply_flag_); + + // Expect the reply to be posted to |reply_runner_|. + EXPECT_FALSE(post_runner_->HasPendingTask()); + EXPECT_TRUE(reply_runner_->HasPendingTask()); + + EXPECT_CALL(mock_object_, Reply(_)); + reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); + testing::Mock::VerifyAndClear(&mock_object_); + EXPECT_TRUE(delete_task_flag_); + // The reply should have been deleted right after being run. + EXPECT_TRUE(delete_reply_flag_); + + // Expect no pending task in |post_runner_| and |reply_runner_|. + EXPECT_FALSE(post_runner_->HasPendingTask()); + EXPECT_FALSE(reply_runner_->HasPendingTask()); +} + +TEST_F(PostTaskAndReplyImplTest, TaskDoesNotRun) { + PostTaskAndReplyToMockObject(); + + // Clear the |post_runner_|. Both callbacks should be scheduled for deletion + // on the |reply_runner_|. + post_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence(); + EXPECT_FALSE(post_runner_->HasPendingTask()); + EXPECT_TRUE(reply_runner_->HasPendingTask()); + EXPECT_FALSE(delete_task_flag_); + EXPECT_FALSE(delete_reply_flag_); + + // Run the |reply_runner_|. Both callbacks should be deleted. + reply_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); + EXPECT_TRUE(delete_task_flag_); + EXPECT_TRUE(delete_reply_flag_); +} + +TEST_F(PostTaskAndReplyImplTest, ReplyDoesNotRun) { + PostTaskAndReplyToMockObject(); + + EXPECT_CALL(mock_object_, Task(_)); + post_runner_->RunUntilIdleWithRunsTasksInCurrentSequence(); + testing::Mock::VerifyAndClear(&mock_object_); + // The task should have been deleted right after being run. + EXPECT_TRUE(delete_task_flag_); + EXPECT_FALSE(delete_reply_flag_); + + // Expect the reply to be posted to |reply_runner_|. + EXPECT_FALSE(post_runner_->HasPendingTask()); + EXPECT_TRUE(reply_runner_->HasPendingTask()); + + // Clear the |reply_runner_| queue without running tasks. The reply callback + // should be deleted. + reply_runner_->ClearPendingTasksWithRunsTasksInCurrentSequence(); + EXPECT_TRUE(delete_task_flag_); + EXPECT_TRUE(delete_reply_flag_); } } // namespace internal diff --git a/chromium/base/threading/scoped_blocking_call.h b/chromium/base/threading/scoped_blocking_call.h index c8c4b36222c..e376c308c56 100644 --- a/chromium/base/threading/scoped_blocking_call.h +++ b/chromium/base/threading/scoped_blocking_call.h @@ -24,14 +24,54 @@ namespace internal { class BlockingObserver; } -// This class can be instantiated in a scope where a a blocking call (which -// isn't using local computing resources -- e.g. a synchronous network request) -// is made. Instantiation will hint the BlockingObserver for this thread about -// the scope of the blocking operation. +// This class must be instantiated in every scope where a blocking call is made. +// CPU usage should be minimal within that scope. //base APIs that block +// instantiate their own ScopedBlockingCall; it is not necessary to instantiate +// another ScopedBlockingCall in the scope where these APIs are used. // -// In particular, when instantiated from a TaskScheduler parallel or sequenced -// task, this will allow the thread to be replaced in its pool (more or less -// aggressively depending on BlockingType). +// Good: +// Data data; +// { +// ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK); +// data = GetDataFromNetwork(); +// } +// CPUIntensiveProcessing(data); +// +// Bad: +// ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK); +// Data data = GetDataFromNetwork(); +// CPUIntensiveProcessing(data); // CPU usage within a ScopedBlockingCall. +// +// Good: +// Data a; +// Data b; +// { +// ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK); +// a = GetDataFromMemoryCacheOrNetwork(); +// b = GetDataFromMemoryCacheOrNetwork(); +// } +// CPUIntensiveProcessing(a); +// CPUIntensiveProcessing(b); +// +// Bad: +// ScopedBlockingCall scoped_blocking_call(BlockingType::MAY_BLOCK); +// Data a = GetDataFromMemoryCacheOrNetwork(); +// Data b = GetDataFromMemoryCacheOrNetwork(); +// CPUIntensiveProcessing(a); // CPU usage within a ScopedBlockingCall. +// CPUIntensiveProcessing(b); // CPU usage within a ScopedBlockingCall. +// +// Good: +// base::WaitableEvent waitable_event(...); +// waitable_event.Wait(); +// +// Bad: +// base::WaitableEvent waitable_event(...); +// ScopedBlockingCall scoped_blocking_call(BlockingType::WILL_BLOCK); +// waitable_event.Wait(); // Wait() instantiates its own ScopedBlockingCall. +// +// When a ScopedBlockingCall is instantiated from a TaskScheduler parallel or +// sequenced task, the thread pool size is incremented to compensate for the +// blocked thread (more or less aggressively depending on BlockingType). class BASE_EXPORT ScopedBlockingCall { public: ScopedBlockingCall(BlockingType blocking_type); diff --git a/chromium/base/threading/thread.cc b/chromium/base/threading/thread.cc index 183f27e0a1d..97e160f91e8 100644 --- a/chromium/base/threading/thread.cc +++ b/chromium/base/threading/thread.cc @@ -308,9 +308,8 @@ void Thread::ThreadMain() { // Allow threads running a MessageLoopForIO to use FileDescriptorWatcher API. std::unique_ptr<FileDescriptorWatcher> file_descriptor_watcher; if (MessageLoopForIO::IsCurrent()) { - DCHECK_EQ(message_loop_, MessageLoopForIO::current()); - file_descriptor_watcher.reset( - new FileDescriptorWatcher(MessageLoopForIO::current())); + file_descriptor_watcher.reset(new FileDescriptorWatcher( + static_cast<MessageLoopForIO*>(message_loop_))); } #endif diff --git a/chromium/base/threading/thread.h b/chromium/base/threading/thread.h index 8237a23a57c..a88d70f76fa 100644 --- a/chromium/base/threading/thread.h +++ b/chromium/base/threading/thread.h @@ -271,6 +271,9 @@ class BASE_EXPORT Thread : PlatformThread::Delegate { static bool GetThreadWasQuitProperly(); // Bind this Thread to an existing MessageLoop instead of starting a new one. + // TODO(gab): Remove this after ios/ has undergone the same surgery as + // BrowserThreadImpl (ref. + // https://chromium-review.googlesource.com/c/chromium/src/+/969104). void SetMessageLoop(MessageLoop* message_loop); bool using_external_message_loop() const { diff --git a/chromium/base/threading/thread_id_name_manager.cc b/chromium/base/threading/thread_id_name_manager.cc index ebcc3ce13f8..ca1979d1557 100644 --- a/chromium/base/threading/thread_id_name_manager.cc +++ b/chromium/base/threading/thread_id_name_manager.cc @@ -9,7 +9,9 @@ #include "base/logging.h" #include "base/memory/singleton.h" +#include "base/no_destructor.h" #include "base/strings/string_util.h" +#include "base/threading/thread_local.h" #include "base/trace_event/heap_profiler_allocation_context_tracker.h" namespace base { @@ -18,6 +20,10 @@ namespace { static const char kDefaultName[] = ""; static std::string* g_default_name; +ThreadLocalStorage::Slot& GetThreadNameTLS() { + static base::NoDestructor<base::ThreadLocalStorage::Slot> thread_name_tls; + return *thread_name_tls; +} } ThreadIdNameManager::ThreadIdNameManager() @@ -52,8 +58,8 @@ void ThreadIdNameManager::InstallSetNameCallback(SetNameCallback callback) { set_name_callback_ = std::move(callback); } -void ThreadIdNameManager::SetName(PlatformThreadId id, - const std::string& name) { +void ThreadIdNameManager::SetName(const std::string& name) { + PlatformThreadId id = PlatformThread::CurrentId(); std::string* leaked_str = nullptr; { AutoLock locked(lock_); @@ -68,6 +74,7 @@ void ThreadIdNameManager::SetName(PlatformThreadId id, ThreadIdToHandleMap::iterator id_to_handle_iter = thread_id_to_handle_.find(id); + GetThreadNameTLS().Set(const_cast<char*>(leaked_str->c_str())); if (set_name_callback_) { set_name_callback_.Run(leaked_str->c_str()); } @@ -107,6 +114,11 @@ const char* ThreadIdNameManager::GetName(PlatformThreadId id) { return handle_to_name_iter->second->c_str(); } +const char* ThreadIdNameManager::GetNameForCurrentThread() { + const char* name = reinterpret_cast<const char*>(GetThreadNameTLS().Get()); + return name ? name : kDefaultName; +} + void ThreadIdNameManager::RemoveName(PlatformThreadHandle::Handle handle, PlatformThreadId id) { AutoLock locked(lock_); diff --git a/chromium/base/threading/thread_id_name_manager.h b/chromium/base/threading/thread_id_name_manager.h index d0717b09bdb..f17dc1a4e84 100644 --- a/chromium/base/threading/thread_id_name_manager.h +++ b/chromium/base/threading/thread_id_name_manager.h @@ -34,12 +34,15 @@ class BASE_EXPORT ThreadIdNameManager { using SetNameCallback = base::RepeatingCallback<void(const char* name)>; void InstallSetNameCallback(SetNameCallback callback); - // Set the name for the given id. - void SetName(PlatformThreadId id, const std::string& name); + // Set the name for the current thread. + void SetName(const std::string& name); // Get the name for the given id. const char* GetName(PlatformThreadId id); + // Unlike |GetName|, this method using TLS and avoids touching |lock_|. + const char* GetNameForCurrentThread(); + // Remove the name for the given id. void RemoveName(PlatformThreadHandle::Handle handle, PlatformThreadId id); diff --git a/chromium/base/threading/thread_local_storage.cc b/chromium/base/threading/thread_local_storage.cc index de5fd26bad0..dae54fbb9a1 100644 --- a/chromium/base/threading/thread_local_storage.cc +++ b/chromium/base/threading/thread_local_storage.cc @@ -69,6 +69,45 @@ namespace { base::subtle::Atomic32 g_native_tls_key = PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES; +// The OS TLS slot has three states: +// * kUninitialized: Any call to Slot::Get()/Set() will create the base +// per-thread TLS state. On POSIX, kUninitialized must be 0. +// * [Memory Address]: Raw pointer to the base per-thread TLS state. +// * kDestroyed: The base per-thread TLS state has been freed. +// +// Final States: +// * Windows: kDestroyed. Windows does not iterate through the OS TLS to clean +// up the values. +// * POSIX: kUninitialized. POSIX iterates through TLS until all slots contain +// nullptr. +// +// More details on this design: +// We need some type of thread-local state to indicate that the TLS system has +// been destroyed. To do so, we leverage the multi-pass nature of destruction +// of pthread_key. +// +// a) After destruction of TLS system, we set the pthread_key to a sentinel +// kDestroyed. +// b) All calls to Slot::Get() DCHECK that the state is not kDestroyed, and +// any system which might potentially invoke Slot::Get() after destruction +// of TLS must check ThreadLOcalStorage::ThreadIsBeingDestroyed(). +// c) After a full pass of the pthread_keys, on the next invocation of +// ConstructTlsVector(), we'll then set the key to nullptr. +// d) At this stage, the TLS system is back in its uninitialized state. +// e) If in the second pass of destruction of pthread_keys something were to +// re-initialize TLS [this should never happen! Since the only code which +// uses Chrome TLS is Chrome controlled, we should really be striving for +// single-pass destruction], then TLS will be re-initialized and then go +// through the 2-pass destruction system again. Everything should just +// work (TM). + +// The consumers of kUninitialized and kDestroyed expect void*, since that's +// what the API exposes on both POSIX and Windows. +void* const kUninitialized = nullptr; + +// A sentinel value to indicate that the TLS system has been destroyed. +void* const kDestroyed = reinterpret_cast<void*>(-3); + // The maximum number of slots in our thread local storage stack. constexpr int kThreadLocalStorageSize = 256; @@ -139,7 +178,7 @@ TlsVectorEntry* ConstructTlsVector() { key = base::subtle::NoBarrier_Load(&g_native_tls_key); } } - CHECK(!PlatformThreadLocalStorage::GetTLSValue(key)); + CHECK_EQ(PlatformThreadLocalStorage::GetTLSValue(key), kUninitialized); // Some allocators, such as TCMalloc, make use of thread local storage. As a // result, any attempt to call new (or malloc) will lazily cause such a system @@ -162,6 +201,16 @@ TlsVectorEntry* ConstructTlsVector() { } void OnThreadExitInternal(TlsVectorEntry* tls_data) { + // This branch is for POSIX, where this function is called twice. The first + // pass calls dtors and sets state to kDestroyed. The second pass sets + // kDestroyed to kUninitialized. + if (tls_data == kDestroyed) { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + PlatformThreadLocalStorage::SetTLSValue(key, kUninitialized); + return; + } + DCHECK(tls_data); // Some allocators, such as TCMalloc, use TLS. As a result, when a thread // terminates, one of the destructor calls we make may be to shut down an @@ -221,7 +270,7 @@ void OnThreadExitInternal(TlsVectorEntry* tls_data) { } // Remove our stack allocated vector. - PlatformThreadLocalStorage::SetTLSValue(key, nullptr); + PlatformThreadLocalStorage::SetTLSValue(key, kDestroyed); } } // namespace @@ -237,8 +286,13 @@ void PlatformThreadLocalStorage::OnThreadExit() { if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) return; void *tls_data = GetTLSValue(key); + + // On Windows, thread destruction callbacks are only invoked once per module, + // so there should be no way that this could be invoked twice. + DCHECK_NE(tls_data, kDestroyed); + // Maybe we have never initialized TLS for this thread. - if (!tls_data) + if (tls_data == kUninitialized) return; OnThreadExitInternal(static_cast<TlsVectorEntry*>(tls_data)); } @@ -250,11 +304,19 @@ void PlatformThreadLocalStorage::OnThreadExit(void* value) { } // namespace internal +bool ThreadLocalStorage::HasBeenDestroyed() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) + return false; + return PlatformThreadLocalStorage::GetTLSValue(key) == kDestroyed; +} + void ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { PlatformThreadLocalStorage::TLSKey key = base::subtle::NoBarrier_Load(&g_native_tls_key); if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES || - !PlatformThreadLocalStorage::GetTLSValue(key)) { + PlatformThreadLocalStorage::GetTLSValue(key) == kUninitialized) { ConstructTlsVector(); } @@ -300,8 +362,9 @@ void* ThreadLocalStorage::Slot::Get() const { TlsVectorEntry* tls_data = static_cast<TlsVectorEntry*>( PlatformThreadLocalStorage::GetTLSValue( base::subtle::NoBarrier_Load(&g_native_tls_key))); + DCHECK_NE(tls_data, kDestroyed); if (!tls_data) - tls_data = ConstructTlsVector(); + return nullptr; DCHECK_NE(slot_, kInvalidSlotValue); DCHECK_LT(slot_, kThreadLocalStorageSize); // Version mismatches means this slot was previously freed. @@ -314,6 +377,7 @@ void ThreadLocalStorage::Slot::Set(void* value) { TlsVectorEntry* tls_data = static_cast<TlsVectorEntry*>( PlatformThreadLocalStorage::GetTLSValue( base::subtle::NoBarrier_Load(&g_native_tls_key))); + DCHECK_NE(tls_data, kDestroyed); if (!tls_data) tls_data = ConstructTlsVector(); DCHECK_NE(slot_, kInvalidSlotValue); diff --git a/chromium/base/threading/thread_local_storage.h b/chromium/base/threading/thread_local_storage.h index 4f2b7f18ff0..844f4e742b6 100644 --- a/chromium/base/threading/thread_local_storage.h +++ b/chromium/base/threading/thread_local_storage.h @@ -18,10 +18,22 @@ #include <pthread.h> #endif +namespace heap_profiling { +class MemlogAllocatorShimInternal; +} // namespace heap_profiling + namespace base { +class SamplingHeapProfiler; + +namespace trace_event { +class MallocDumpProvider; +} // namespace trace_event + namespace internal { +class ThreadLocalStorageTestInternal; + // WARNING: You should *NOT* use this class directly. // PlatformThreadLocalStorage is a low-level abstraction of the OS's TLS // interface. Instead, you should use one of the following: @@ -90,7 +102,6 @@ class BASE_EXPORT PlatformThreadLocalStorage { // an API for portability. class BASE_EXPORT ThreadLocalStorage { public: - // Prototype for the TLS destructor function, which can be optionally used to // cleanup thread local storage on thread exit. 'value' is the data that is // stored in thread local storage. @@ -134,6 +145,20 @@ class BASE_EXPORT ThreadLocalStorage { }; private: + // In most cases, most callers should not need access to HasBeenDestroyed(). + // If you are working in code that runs during thread destruction, contact the + // base OWNERs for advice and then make a friend request. + // + // Returns |true| if Chrome's implementation of TLS has been destroyed during + // thread destruction. Attempting to call Slot::Get() during destruction is + // disallowed and will hit a DCHECK. Any code that relies on TLS during thread + // destruction must first check this method before calling Slot::Get(). + friend class base::SamplingHeapProfiler; + friend class base::internal::ThreadLocalStorageTestInternal; + friend class base::trace_event::MallocDumpProvider; + friend class heap_profiling::MemlogAllocatorShimInternal; + static bool HasBeenDestroyed(); + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); }; diff --git a/chromium/base/threading/thread_local_storage_unittest.cc b/chromium/base/threading/thread_local_storage_unittest.cc index 02794b4b666..9062ff0c7fa 100644 --- a/chromium/base/threading/thread_local_storage_unittest.cc +++ b/chromium/base/threading/thread_local_storage_unittest.cc @@ -23,6 +23,22 @@ namespace base { +#if defined(OS_POSIX) + +namespace internal { + +// This class is friended by ThreadLocalStorage. +class ThreadLocalStorageTestInternal { + public: + static bool HasBeenDestroyed() { + return ThreadLocalStorage::HasBeenDestroyed(); + } +}; + +} // namespace internal + +#endif // defined(OS_POSIX) + namespace { const int kInitialTlsValue = 0x5555; @@ -80,6 +96,105 @@ void ThreadLocalStorageCleanup(void *value) { TLSSlot().Set(value); } +#if defined(OS_POSIX) +constexpr intptr_t kDummyValue = 0xABCD; +constexpr size_t kKeyCount = 20; + +// The order in which pthread keys are destructed is not specified by the POSIX +// specification. Hopefully, of the 20 keys we create, some of them should be +// destroyed after the TLS key is destroyed. +class UseTLSDuringDestructionRunner { + public: + UseTLSDuringDestructionRunner() = default; + + // The order in which pthread_key destructors are called is not well defined. + // Hopefully, by creating 10 both before and after initializing TLS on the + // thread, at least 1 will be called after TLS destruction. + void Run() { + ASSERT_FALSE(internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()); + + // Create 10 pthread keys before initializing TLS on the thread. + size_t slot_index = 0; + for (; slot_index < 10; ++slot_index) { + CreateTlsKeyWithDestructor(slot_index); + } + + // Initialize the Chrome TLS system. It's possible that base::Thread has + // already initialized Chrome TLS, but we don't rely on that. + slot_.Set(reinterpret_cast<void*>(kDummyValue)); + + // Create 10 pthread keys after initializing TLS on the thread. + for (; slot_index < kKeyCount; ++slot_index) { + CreateTlsKeyWithDestructor(slot_index); + } + } + + bool teardown_works_correctly() { return teardown_works_correctly_; } + + private: + struct TLSState { + pthread_key_t key; + bool* teardown_works_correctly; + }; + + // The POSIX TLS destruction API takes as input a single C-function, which is + // called with the current |value| of a (key, value) pair. We need this + // function to do two things: set the |value| to nullptr, which requires + // knowing the associated |key|, and update the |teardown_works_correctly_| + // state. + // + // To accomplish this, we set the value to an instance of TLSState, which + // contains |key| as well as a pointer to |teardown_works_correctly|. + static void ThreadLocalDestructor(void* value) { + TLSState* state = static_cast<TLSState*>(value); + int result = pthread_setspecific(state->key, nullptr); + ASSERT_EQ(result, 0); + + // If this path is hit, then the thread local destructor was called after + // the Chrome-TLS destructor and the internal state was updated correctly. + // No further checks are necessary. + if (internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()) { + *(state->teardown_works_correctly) = true; + return; + } + + // If this path is hit, then the thread local destructor was called before + // the Chrome-TLS destructor is hit. The ThreadLocalStorage::Slot should + // still function correctly. + ASSERT_EQ(reinterpret_cast<intptr_t>(slot_.Get()), kDummyValue); + } + + void CreateTlsKeyWithDestructor(size_t index) { + ASSERT_LT(index, kKeyCount); + + tls_states_[index].teardown_works_correctly = &teardown_works_correctly_; + int result = pthread_key_create( + &(tls_states_[index].key), + UseTLSDuringDestructionRunner::ThreadLocalDestructor); + ASSERT_EQ(result, 0); + + result = pthread_setspecific(tls_states_[index].key, &tls_states_[index]); + ASSERT_EQ(result, 0); + } + + static base::ThreadLocalStorage::Slot slot_; + bool teardown_works_correctly_ = false; + TLSState tls_states_[kKeyCount]; + + DISALLOW_COPY_AND_ASSIGN(UseTLSDuringDestructionRunner); +}; + +base::ThreadLocalStorage::Slot UseTLSDuringDestructionRunner::slot_; + +void* UseTLSTestThreadRun(void* input) { + UseTLSDuringDestructionRunner* runner = + static_cast<UseTLSDuringDestructionRunner*>(input); + runner->Run(); + return nullptr; +} + +#endif // defined(OS_POSIX) + } // namespace TEST(ThreadLocalStorageTest, Basics) { @@ -142,4 +257,22 @@ TEST(ThreadLocalStorageTest, TLSReclaim) { } } +#if defined(OS_POSIX) +// Unlike POSIX, Windows does not iterate through the OS TLS to cleanup any +// values there. Instead a per-module thread destruction function is called. +// However, it is not possible to perform a check after this point (as the code +// is detached from the thread), so this check remains POSIX only. +TEST(ThreadLocalStorageTest, UseTLSDuringDestruction) { + UseTLSDuringDestructionRunner runner; + pthread_t thread; + int result = pthread_create(&thread, nullptr, UseTLSTestThreadRun, &runner); + ASSERT_EQ(result, 0); + + result = pthread_join(thread, nullptr); + ASSERT_EQ(result, 0); + + EXPECT_TRUE(runner.teardown_works_correctly()); +} +#endif // defined(OS_POSIX) + } // namespace base diff --git a/chromium/base/threading/thread_restrictions.cc b/chromium/base/threading/thread_restrictions.cc index e7e1716e080..633bcb26e6e 100644 --- a/chromium/base/threading/thread_restrictions.cc +++ b/chromium/base/threading/thread_restrictions.cc @@ -118,6 +118,13 @@ void ResetThreadRestrictionsForTesting() { } // namespace internal +ThreadRestrictions::ScopedAllowIO::ScopedAllowIO() + : was_allowed_(SetIOAllowed(true)) {} + +ThreadRestrictions::ScopedAllowIO::~ScopedAllowIO() { + SetIOAllowed(was_allowed_); +} + // static bool ThreadRestrictions::SetIOAllowed(bool allowed) { bool previous_disallowed = g_blocking_disallowed.Get().Get(); @@ -157,6 +164,13 @@ bool ThreadRestrictions::SetWaitAllowed(bool allowed) { return !previous_disallowed; } +ThreadRestrictions::ScopedAllowWait::ScopedAllowWait() + : was_allowed_(SetWaitAllowed(true)) {} + +ThreadRestrictions::ScopedAllowWait::~ScopedAllowWait() { + SetWaitAllowed(was_allowed_); +} + } // namespace base #endif // DCHECK_IS_ON() diff --git a/chromium/base/threading/thread_restrictions.h b/chromium/base/threading/thread_restrictions.h index e63c406810b..e3d70e31083 100644 --- a/chromium/base/threading/thread_restrictions.h +++ b/chromium/base/threading/thread_restrictions.h @@ -38,6 +38,7 @@ namespace content { class BrowserGpuChannelHostFactory; class BrowserGpuMemoryBufferManager; class BrowserMainLoop; +class BrowserProcessSubThread; class BrowserShutdownProfileDumper; class BrowserSurfaceViewManager; class BrowserTestBase; @@ -79,6 +80,7 @@ namespace midi { class TaskService; // https://crbug.com/796830 } namespace mojo { +class CoreLibraryInitializer; class SyncCallRestrictions; namespace edk { class ScopedIPCSupport; @@ -91,6 +93,7 @@ namespace ui { class CommandBufferClientImpl; class CommandBufferLocal; class GpuState; +class MaterialDesignController; } namespace net { class MultiThreadedCertVerifierScopedAllowBaseSyncPrimitives; @@ -211,9 +214,12 @@ class BASE_EXPORT ScopedAllowBlocking { // in unit tests to avoid the friend requirement. FRIEND_TEST_ALL_PREFIXES(ThreadRestrictionsTest, ScopedAllowBlocking); friend class android_webview::ScopedAllowInitGLBindings; + friend class content::BrowserProcessSubThread; friend class cronet::CronetPrefsManager; friend class cronet::CronetURLRequestContext; + friend class mojo::CoreLibraryInitializer; friend class resource_coordinator::TabManagerDelegate; // crbug.com/778703 + friend class ui::MaterialDesignController; friend class ScopedAllowBlockingForTesting; friend class StackSamplingProfiler; @@ -359,11 +365,13 @@ class BASE_EXPORT ThreadRestrictions { // DEPRECATED. Use ScopedAllowBlocking(ForTesting). class BASE_EXPORT ScopedAllowIO { public: - ScopedAllowIO() { previous_value_ = SetIOAllowed(true); } - ~ScopedAllowIO() { SetIOAllowed(previous_value_); } + ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowIO() EMPTY_BODY_IF_DCHECK_IS_OFF; + private: - // Whether IO is allowed when the ScopedAllowIO was constructed. - bool previous_value_; +#if DCHECK_IS_ON() + const bool was_allowed_; +#endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowIO); }; @@ -470,12 +478,13 @@ class BASE_EXPORT ThreadRestrictions { // DEPRECATED. Use ScopedAllowBaseSyncPrimitives. class BASE_EXPORT ScopedAllowWait { public: - ScopedAllowWait() { previous_value_ = SetWaitAllowed(true); } - ~ScopedAllowWait() { SetWaitAllowed(previous_value_); } + ScopedAllowWait() EMPTY_BODY_IF_DCHECK_IS_OFF; + ~ScopedAllowWait() EMPTY_BODY_IF_DCHECK_IS_OFF; + private: - // Whether singleton use is allowed when the ScopedAllowWait was - // constructed. - bool previous_value_; +#if DCHECK_IS_ON() + const bool was_allowed_; +#endif DISALLOW_COPY_AND_ASSIGN(ScopedAllowWait); }; |