summaryrefslogtreecommitdiffstats
path: root/chromium/base/threading
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:20:33 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2018-05-15 10:28:57 +0000
commitd17ea114e5ef69ad5d5d7413280a13e6428098aa (patch)
tree2c01a75df69f30d27b1432467cfe7c1467a498da /chromium/base/threading
parent8c5c43c7b138c9b4b0bf56d946e61d3bbc111bec (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')
-rw-r--r--chromium/base/threading/platform_thread_android.cc2
-rw-r--r--chromium/base/threading/platform_thread_fuchsia.cc3
-rw-r--r--chromium/base/threading/platform_thread_linux.cc2
-rw-r--r--chromium/base/threading/platform_thread_mac.mm2
-rw-r--r--chromium/base/threading/platform_thread_win.cc6
-rw-r--r--chromium/base/threading/post_task_and_reply_impl.cc126
-rw-r--r--chromium/base/threading/post_task_and_reply_impl.h17
-rw-r--r--chromium/base/threading/post_task_and_reply_impl_unittest.cc172
-rw-r--r--chromium/base/threading/scoped_blocking_call.h54
-rw-r--r--chromium/base/threading/thread.cc5
-rw-r--r--chromium/base/threading/thread.h3
-rw-r--r--chromium/base/threading/thread_id_name_manager.cc16
-rw-r--r--chromium/base/threading/thread_id_name_manager.h7
-rw-r--r--chromium/base/threading/thread_local_storage.cc74
-rw-r--r--chromium/base/threading/thread_local_storage.h27
-rw-r--r--chromium/base/threading/thread_local_storage_unittest.cc133
-rw-r--r--chromium/base/threading/thread_restrictions.cc14
-rw-r--r--chromium/base/threading/thread_restrictions.h27
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);
};