Commit 5458566c authored by Carlos Caballero's avatar Carlos Caballero Committed by Commit Bot

Use OperationsController in MessageLoopImpl::Controller

Replace all the state checking and tracking code with the new and shiny
OperationsController.

Bug: 901345
Change-Id: I8023e78ebfee679fd565de465e810dbb9c6ee2ec
Reviewed-on: https://chromium-review.googlesource.com/c/1341525
Commit-Queue: Gabriel Charette <gab@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609960}
parent ca224255
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include "base/message_loop/message_pump_for_ui.h" #include "base/message_loop/message_pump_for_ui.h"
#include "base/message_loop/sequenced_task_source.h" #include "base/message_loop/sequenced_task_source.h"
#include "base/run_loop.h" #include "base/run_loop.h"
#include "base/task/common/operations_controller.h"
#include "base/threading/thread_id_name_manager.h" #include "base/threading/thread_id_name_manager.h"
#include "base/threading/thread_restrictions.h" #include "base/threading/thread_restrictions.h"
#include "base/threading/thread_task_runner_handle.h" #include "base/threading/thread_task_runner_handle.h"
...@@ -40,8 +41,6 @@ class MessageLoopImpl::Controller : public SequencedTaskSource::Observer { ...@@ -40,8 +41,6 @@ class MessageLoopImpl::Controller : public SequencedTaskSource::Observer {
// used after DisconnectFromParent() returns. // used after DisconnectFromParent() returns.
Controller(MessageLoopImpl* message_loop); Controller(MessageLoopImpl* message_loop);
~Controller() override;
// SequencedTaskSource::Observer: // SequencedTaskSource::Observer:
void WillQueueTask(PendingTask* task) final; void WillQueueTask(PendingTask* task) final;
void DidQueueTask(bool was_empty) final; void DidQueueTask(bool was_empty) final;
...@@ -62,12 +61,7 @@ class MessageLoopImpl::Controller : public SequencedTaskSource::Observer { ...@@ -62,12 +61,7 @@ class MessageLoopImpl::Controller : public SequencedTaskSource::Observer {
debug::TaskAnnotator& task_annotator() { return task_annotator_; } debug::TaskAnnotator& task_annotator() { return task_annotator_; }
private: private:
// Helpers to be invoked before using |message_loop_| instead of operating internal::OperationsController operations_controller_;
// directly on |operations_state_| below. BeforeOperation() returns true iff
// the operation is allowed to be performed (in which case, AfterOperation()
// must be invoked when done).
bool BeforeOperation();
void AfterOperation();
// A TaskAnnotator which is owned by this Controller to be able to use it // A TaskAnnotator which is owned by this Controller to be able to use it
// without locking |message_loop_lock_|. It cannot be owned by MessageLoop // without locking |message_loop_lock_|. It cannot be owned by MessageLoop
...@@ -75,38 +69,8 @@ class MessageLoopImpl::Controller : public SequencedTaskSource::Observer { ...@@ -75,38 +69,8 @@ class MessageLoopImpl::Controller : public SequencedTaskSource::Observer {
// lock. Note: the TaskAnnotator API itself is thread-safe. // lock. Note: the TaskAnnotator API itself is thread-safe.
debug::TaskAnnotator task_annotator_; debug::TaskAnnotator task_annotator_;
// An atomic representation of the ongoing operations and shutdown state. The
// lower bit (kDisconnectedBit) is used to indicate that
// DisconnectFromParent() was initiated. The other bits are used to indicate
// ongoing operations. As such this should be incremented by
// kOperationInProgress before making any operation on |message_loop_|.
// Conversely DisconnectFromParent() will wait on |safe_to_shutdown_| if this
// was non-zero when it set the lower bit.
static constexpr int kDisconnectedBit = 1;
static constexpr int kOperationInProgress = 1 << kDisconnectedBit;
std::atomic_int operations_state_{0};
// DisconnectFromParent() will instantiate and wait on this event if
// |operations_state_| wasn't zero when it set the lower bit. Whoever then
// completes the last in-progress operation needs to signal this event to
// resume the disconnect.
Optional<WaitableEvent> safe_to_shutdown_;
enum InitializationState {
// Initial state : ScheduleWork() cannot be called yet.
kNotReady,
// ScheduleWork() cannot be called yet but should be when transitioning to
// kReadyForScheduling.
kPendingWork,
// ScheduleWork() can be called now.
kReadyForScheduling,
};
std::atomic_int initialization_state_{kNotReady};
// Points to this Controller's outer MessageLoop instance. // Points to this Controller's outer MessageLoop instance.
// |initialization_state_| must be set to kReadyForScheduling before using // Access to this member is protected by |operations_controller_|.
// this. |operations_state_| must then be incremented per the above protocol
// to use this.
MessageLoopImpl* const message_loop_; MessageLoopImpl* const message_loop_;
DISALLOW_COPY_AND_ASSIGN(Controller); DISALLOW_COPY_AND_ASSIGN(Controller);
...@@ -117,11 +81,6 @@ MessageLoopImpl::Controller::Controller(MessageLoopImpl* message_loop) ...@@ -117,11 +81,6 @@ MessageLoopImpl::Controller::Controller(MessageLoopImpl* message_loop)
DCHECK(message_loop_); DCHECK(message_loop_);
} }
MessageLoopImpl::Controller::~Controller() {
DCHECK(safe_to_shutdown_)
<< "DisconnectFromParent() needs to be invoked before destruction.";
}
void MessageLoopImpl::Controller::WillQueueTask(PendingTask* task) { void MessageLoopImpl::Controller::WillQueueTask(PendingTask* task) {
task_annotator_.WillQueueTask("MessageLoop::PostTask", task); task_annotator_.WillQueueTask("MessageLoop::PostTask", task);
} }
...@@ -130,18 +89,8 @@ void MessageLoopImpl::Controller::DidQueueTask(bool was_empty) { ...@@ -130,18 +89,8 @@ void MessageLoopImpl::Controller::DidQueueTask(bool was_empty) {
if (!was_empty) if (!was_empty)
return; return;
// Perform a lock-less check that we are ready for scheduling. If not, auto operation_token = operations_controller_.TryBeginOperation();
// atomically inform StartScheduling() about the pending work. if (!operation_token)
// std::memory_order_acquire as documented in StartScheduling().
int previous_state = kNotReady;
if (initialization_state_.compare_exchange_strong(
previous_state, kPendingWork, std::memory_order_acquire) ||
previous_state == kPendingWork) {
return;
}
DCHECK_EQ(previous_state, kReadyForScheduling);
if (!BeforeOperation())
return; return;
// Some scenarios can result in getting to this point on multiple threads at // Some scenarios can result in getting to this point on multiple threads at
...@@ -158,82 +107,21 @@ void MessageLoopImpl::Controller::DidQueueTask(bool was_empty) { ...@@ -158,82 +107,21 @@ void MessageLoopImpl::Controller::DidQueueTask(bool was_empty) {
// MessageLoop/MessagePump::ScheduleWork() is thread-safe so this is fine. // MessageLoop/MessagePump::ScheduleWork() is thread-safe so this is fine.
message_loop_->ScheduleWork(); message_loop_->ScheduleWork();
AfterOperation();
} }
void MessageLoopImpl::Controller::StartScheduling() { void MessageLoopImpl::Controller::StartScheduling() {
DCHECK_CALLED_ON_VALID_THREAD(message_loop_->bound_thread_checker_); if (operations_controller_.StartAcceptingOperations())
// std::memory_order_release because any thread that acquires this value and
// sees kReadyForScheduling needs to also see the state initialized on this
// thread before StartScheduling(). (this is also why matching reads use
// std::memory_order_acquire)
auto previous_state = initialization_state_.exchange(
kReadyForScheduling, std::memory_order_release);
DCHECK_NE(previous_state, kReadyForScheduling);
if (previous_state == kPendingWork)
message_loop_->ScheduleWork(); message_loop_->ScheduleWork();
} }
void MessageLoopImpl::Controller::DisconnectFromParent() { void MessageLoopImpl::Controller::DisconnectFromParent() {
DCHECK_CALLED_ON_VALID_THREAD(message_loop_->bound_thread_checker_); ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait_for_fast_ops;
DCHECK(!safe_to_shutdown_); operations_controller_.ShutdownAndWaitForZeroOperations();
safe_to_shutdown_.emplace();
// Acquire semantics are required to guarantee that all memory side-effects
// made to |message_loop_| by other threads are visible to this thread before
// it returns from this method. Release semantics are required to guarantee
// that threads seeing the disconnect bit will also see a fully initialized
// |safe_to_shutdown_|.
if (operations_state_.fetch_add(kDisconnectedBit,
std::memory_order_acq_rel) != 0) {
ScopedAllowBaseSyncPrimitivesOutsideBlockingScope allow_wait_for_fast_ops;
safe_to_shutdown_->Wait();
}
}
bool MessageLoopImpl::Controller::BeforeOperation() {
// Acquire semantics are required to ensure that no operation on the current
// thread can be reordered before this one.
const bool allowed = (operations_state_.fetch_add(kOperationInProgress,
std::memory_order_acquire) &
kDisconnectedBit) == 0;
// Undo the increment if disallowed (and potentially signal if that racily
// ended up being the last operation).
if (!allowed)
AfterOperation();
return allowed;
}
void MessageLoopImpl::Controller::AfterOperation() {
constexpr int kWasDisconnectedWithOnlyOneOperationLeft =
kOperationInProgress | kDisconnectedBit;
// Release semantics are required to ensure that no operation on the current
// thread can be reordered after this one. Technically, acquire semantics are
// only required if the conditional is true (to synchronize with
// DisconnectFromParent() before using |safe_to_shutdown_|). As such, per
// "Atomic-fence synchronization" semantics [1], it'd be sufficient to
// fetch_sub(std::memory_order_release) and only have a
// std::atomic_thread_fence(std::memory_order_acquire) inside the
// conditional's body. However, as documented in atomic_ref_count.h TSAN
// doesn't support fences at the moment. Hence, this uses acq_rel for now.
// [1] https://en.cppreference.com/w/cpp/atomic/atomic_thread_fence
if (operations_state_.fetch_sub(kOperationInProgress,
std::memory_order_acq_rel) ==
kWasDisconnectedWithOnlyOneOperationLeft) {
safe_to_shutdown_->Signal();
}
} }
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
MessageLoopImpl::~MessageLoopImpl() { MessageLoopImpl::~MessageLoopImpl() {
#if defined(OS_WIN) #if defined(OS_WIN)
if (in_high_res_mode_) if (in_high_res_mode_)
Time::ActivateHighResolutionTimer(false); Time::ActivateHighResolutionTimer(false);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment