Repo created
This commit is contained in:
parent
81b91f4139
commit
f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions
164
TMessagesProj/jni/voip/webrtc/base/message_loop/message_loop.cc
Normal file
164
TMessagesProj/jni/voip/webrtc/base/message_loop/message_loop.cc
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_loop.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/message_loop/message_pump_default.h"
|
||||
#include "base/message_loop/message_pump_for_io.h"
|
||||
#include "base/message_loop/message_pump_for_ui.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/task/sequence_manager/sequence_manager.h"
|
||||
#include "base/task/sequence_manager/sequence_manager_impl.h"
|
||||
#include "base/task/sequence_manager/task_queue.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include "base/message_loop/message_pump_mac.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
MessageLoop::MessageLoop(MessagePumpType type) : MessageLoop(type, nullptr) {
|
||||
// For TYPE_CUSTOM you must either use
|
||||
// MessageLoop(std::unique_ptr<MessagePump> pump) or
|
||||
// MessageLoop::CreateUnbound()
|
||||
DCHECK_NE(type_, MessagePumpType::CUSTOM);
|
||||
BindToCurrentThread();
|
||||
}
|
||||
|
||||
MessageLoop::MessageLoop(std::unique_ptr<MessagePump> pump)
|
||||
: MessageLoop(MessagePumpType::CUSTOM, std::move(pump)) {
|
||||
BindToCurrentThread();
|
||||
}
|
||||
|
||||
MessageLoop::~MessageLoop() {
|
||||
// Clean up any unprocessed tasks, but take care: deleting a task could
|
||||
// result in the addition of more tasks (e.g., via DeleteSoon). This is taken
|
||||
// care by the queue as it will prevent further tasks from being posted to its
|
||||
// associated TaskRunner instances.
|
||||
default_task_queue_->ShutdownTaskQueue();
|
||||
|
||||
// If |pump_| is non-null, this message loop has been bound and should be the
|
||||
// current one on this thread. Otherwise, this loop is being destructed before
|
||||
// it was bound to a thread, so a different message loop (or no loop at all)
|
||||
// may be current.
|
||||
DCHECK((pump_ && IsBoundToCurrentThread()) ||
|
||||
(!pump_ && !IsBoundToCurrentThread()));
|
||||
|
||||
// iOS just attaches to the loop, it doesn't Run it.
|
||||
// TODO(stuartmorgan): Consider wiring up a Detach().
|
||||
#if !defined(OS_IOS)
|
||||
// There should be no active RunLoops on this thread, unless this MessageLoop
|
||||
// isn't bound to the current thread (see other condition at the top of this
|
||||
// method).
|
||||
DCHECK((!pump_ && !IsBoundToCurrentThread()) ||
|
||||
!RunLoop::IsRunningOnCurrentThread());
|
||||
#endif // !defined(OS_IOS)
|
||||
}
|
||||
|
||||
bool MessageLoop::IsType(MessagePumpType type) const {
|
||||
return type_ == type;
|
||||
}
|
||||
|
||||
// TODO(gab): Migrate TaskObservers to RunLoop as part of separating concerns
|
||||
// between MessageLoop and RunLoop and making MessageLoop a swappable
|
||||
// implementation detail. http://crbug.com/703346
|
||||
void MessageLoop::AddTaskObserver(TaskObserver* task_observer) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
|
||||
sequence_manager_->AddTaskObserver(task_observer);
|
||||
}
|
||||
|
||||
void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
|
||||
sequence_manager_->RemoveTaskObserver(task_observer);
|
||||
}
|
||||
|
||||
bool MessageLoop::IsBoundToCurrentThread() const {
|
||||
return sequence_manager_->IsBoundToCurrentThread();
|
||||
}
|
||||
|
||||
bool MessageLoop::IsIdleForTesting() {
|
||||
return sequence_manager_->IsIdleForTesting();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
// static
|
||||
std::unique_ptr<MessageLoop> MessageLoop::CreateUnbound(MessagePumpType type) {
|
||||
return WrapUnique(new MessageLoop(type, nullptr));
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<MessageLoop> MessageLoop::CreateUnbound(
|
||||
std::unique_ptr<MessagePump> custom_pump) {
|
||||
return WrapUnique(
|
||||
new MessageLoop(MessagePumpType::CUSTOM, std::move(custom_pump)));
|
||||
}
|
||||
|
||||
MessageLoop::MessageLoop(MessagePumpType type,
|
||||
std::unique_ptr<MessagePump> custom_pump)
|
||||
: sequence_manager_(
|
||||
sequence_manager::internal::SequenceManagerImpl::CreateUnbound(
|
||||
sequence_manager::SequenceManager::Settings::Builder()
|
||||
.SetMessagePumpType(type)
|
||||
.Build())),
|
||||
default_task_queue_(CreateDefaultTaskQueue()),
|
||||
type_(type),
|
||||
custom_pump_(std::move(custom_pump)) {
|
||||
// Bound in BindToCurrentThread();
|
||||
DETACH_FROM_THREAD(bound_thread_checker_);
|
||||
}
|
||||
|
||||
scoped_refptr<sequence_manager::TaskQueue>
|
||||
MessageLoop::CreateDefaultTaskQueue() {
|
||||
auto default_task_queue = sequence_manager_->CreateTaskQueue(
|
||||
sequence_manager::TaskQueue::Spec("default_tq"));
|
||||
sequence_manager_->SetTaskRunner(default_task_queue->task_runner());
|
||||
return default_task_queue;
|
||||
}
|
||||
|
||||
void MessageLoop::BindToCurrentThread() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
|
||||
thread_id_ = PlatformThread::CurrentId();
|
||||
|
||||
DCHECK(!pump_);
|
||||
|
||||
std::unique_ptr<MessagePump> pump = CreateMessagePump();
|
||||
pump_ = pump.get();
|
||||
|
||||
DCHECK(!MessageLoopCurrent::IsSet())
|
||||
<< "should only have one message loop per thread";
|
||||
|
||||
sequence_manager_->BindToCurrentThread(std::move(pump));
|
||||
}
|
||||
|
||||
std::unique_ptr<MessagePump> MessageLoop::CreateMessagePump() {
|
||||
if (custom_pump_) {
|
||||
return std::move(custom_pump_);
|
||||
} else {
|
||||
return MessagePump::Create(type_);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageLoop::SetTimerSlack(TimerSlack timer_slack) {
|
||||
sequence_manager_->SetTimerSlack(timer_slack);
|
||||
}
|
||||
|
||||
scoped_refptr<SingleThreadTaskRunner> MessageLoop::task_runner() const {
|
||||
return sequence_manager_->GetTaskRunner();
|
||||
}
|
||||
|
||||
void MessageLoop::SetTaskRunner(
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner) {
|
||||
DCHECK(task_runner);
|
||||
sequence_manager_->SetTaskRunner(task_runner);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
201
TMessagesProj/jni/voip/webrtc/base/message_loop/message_loop.h
Normal file
201
TMessagesProj/jni/voip/webrtc/base/message_loop/message_loop.h
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
// Copyright 2013 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback_forward.h"
|
||||
#include "base/gtest_prod_util.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/message_loop/message_pump_type.h"
|
||||
#include "base/message_loop/timer_slack.h"
|
||||
#include "base/pending_task.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class MessagePump;
|
||||
class TaskObserver;
|
||||
|
||||
namespace sequence_manager {
|
||||
class TaskQueue;
|
||||
namespace internal {
|
||||
class SequenceManagerImpl;
|
||||
} // namespace internal
|
||||
} // namespace sequence_manager
|
||||
|
||||
// A MessageLoop is used to process events for a particular thread. There is
|
||||
// at most one MessageLoop instance per thread.
|
||||
//
|
||||
// Events include at a minimum Task instances submitted to the MessageLoop's
|
||||
// TaskRunner. Depending on the Type of message pump used by the MessageLoop
|
||||
// other events such as UI messages may be processed. On Windows APC calls (as
|
||||
// time permits) and signals sent to a registered set of HANDLEs may also be
|
||||
// processed.
|
||||
//
|
||||
// The MessageLoop's API should only be used directly by its owner (and users
|
||||
// which the owner opts to share a MessageLoop* with). Other ways to access
|
||||
// subsets of the MessageLoop API:
|
||||
// - base::RunLoop : Drive the MessageLoop from the thread it's bound to.
|
||||
// - base::Thread/SequencedTaskRunnerHandle : Post back to the MessageLoop
|
||||
// from a task running on it.
|
||||
// - SequenceLocalStorageSlot : Bind external state to this MessageLoop.
|
||||
// - base::MessageLoopCurrent : Access statically exposed APIs of this
|
||||
// MessageLoop.
|
||||
// - Embedders may provide their own static accessors to post tasks on
|
||||
// specific loops (e.g. content::BrowserThreads).
|
||||
//
|
||||
// NOTE: Unless otherwise specified, a MessageLoop's methods may only be called
|
||||
// on the thread where the MessageLoop's Run method executes.
|
||||
//
|
||||
// NOTE: MessageLoop has task reentrancy protection. This means that if a
|
||||
// task is being processed, a second task cannot start until the first task is
|
||||
// finished. Reentrancy can happen when processing a task, and an inner
|
||||
// message pump is created. That inner pump then processes native messages
|
||||
// which could implicitly start an inner task. Inner message pumps are created
|
||||
// with dialogs (DialogBox), common dialogs (GetOpenFileName), OLE functions
|
||||
// (DoDragDrop), printer functions (StartDoc) and *many* others.
|
||||
//
|
||||
// Sample workaround when inner task processing is needed:
|
||||
// HRESULT hr;
|
||||
// {
|
||||
// MessageLoopCurrent::ScopedNestableTaskAllower allow;
|
||||
// hr = DoDragDrop(...); // Implicitly runs a modal message loop.
|
||||
// }
|
||||
// // Process |hr| (the result returned by DoDragDrop()).
|
||||
//
|
||||
// Please be SURE your task is reentrant (nestable) and all global variables
|
||||
// are stable and accessible before calling SetNestableTasksAllowed(true).
|
||||
//
|
||||
// DEPRECATED: Use a SingleThreadTaskExecutor instead or TaskEnvironment
|
||||
// for tests. TODO(https://crbug.com/891670/) remove this class.
|
||||
class BASE_EXPORT MessageLoop {
|
||||
public:
|
||||
// Normally, it is not necessary to instantiate a MessageLoop. Instead, it
|
||||
// is typical to make use of the current thread's MessageLoop instance.
|
||||
explicit MessageLoop(MessagePumpType type = MessagePumpType::DEFAULT);
|
||||
// Creates a MessageLoop with the supplied MessagePump, which must be
|
||||
// non-null.
|
||||
explicit MessageLoop(std::unique_ptr<MessagePump> custom_pump);
|
||||
|
||||
virtual ~MessageLoop();
|
||||
|
||||
// Set the timer slack for this message loop.
|
||||
void SetTimerSlack(TimerSlack timer_slack);
|
||||
|
||||
// Returns true if this loop's pump is |type|. This allows subclasses
|
||||
// (especially those in tests) to specialize how they are identified.
|
||||
virtual bool IsType(MessagePumpType type) const;
|
||||
|
||||
// Returns the type passed to the constructor.
|
||||
MessagePumpType type() const { return type_; }
|
||||
|
||||
// Sets a new TaskRunner for this message loop. If the message loop was
|
||||
// already bound, this must be called on the thread to which it is bound.
|
||||
void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
|
||||
|
||||
// Gets the TaskRunner associated with this message loop.
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner() const;
|
||||
|
||||
// These functions can only be called on the same thread that |this| is
|
||||
// running on.
|
||||
// These functions must not be called from a TaskObserver callback.
|
||||
void AddTaskObserver(TaskObserver* task_observer);
|
||||
void RemoveTaskObserver(TaskObserver* task_observer);
|
||||
|
||||
// Returns true if the message loop is idle (ignoring delayed tasks). This is
|
||||
// the same condition which triggers DoWork() to return false: i.e.
|
||||
// out of tasks which can be processed at the current run-level -- there might
|
||||
// be deferred non-nestable tasks remaining if currently in a nested run
|
||||
// level.
|
||||
// TODO(alexclarke): Make this const when MessageLoopImpl goes away.
|
||||
bool IsIdleForTesting();
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
protected:
|
||||
// Returns true if this is the active MessageLoop for the current thread.
|
||||
bool IsBoundToCurrentThread() const;
|
||||
|
||||
using MessagePumpFactoryCallback =
|
||||
OnceCallback<std::unique_ptr<MessagePump>()>;
|
||||
|
||||
// Common protected constructor. Other constructors delegate the
|
||||
// initialization to this constructor.
|
||||
// A subclass can invoke this constructor to create a message_loop of a
|
||||
// specific type with a custom loop. The implementation does not call
|
||||
// BindToCurrentThread. If this constructor is invoked directly by a subclass,
|
||||
// then the subclass must subsequently bind the message loop.
|
||||
MessageLoop(MessagePumpType type, std::unique_ptr<MessagePump> pump);
|
||||
|
||||
// Configure various members and bind this message loop to the current thread.
|
||||
void BindToCurrentThread();
|
||||
|
||||
// A raw pointer to the MessagePump handed-off to |sequence_manager_|.
|
||||
// Valid for the lifetime of |sequence_manager_|.
|
||||
MessagePump* pump_ = nullptr;
|
||||
|
||||
// TODO(crbug.com/891670): We shouldn't publicly expose all of
|
||||
// SequenceManagerImpl.
|
||||
const std::unique_ptr<sequence_manager::internal::SequenceManagerImpl>
|
||||
sequence_manager_;
|
||||
// SequenceManager requires an explicit initialisation of the default task
|
||||
// queue.
|
||||
const scoped_refptr<sequence_manager::TaskQueue> default_task_queue_;
|
||||
|
||||
private:
|
||||
friend class MessageLoopTypedTest;
|
||||
friend class ScheduleWorkTest;
|
||||
friend class Thread;
|
||||
friend class sequence_manager::internal::SequenceManagerImpl;
|
||||
FRIEND_TEST_ALL_PREFIXES(MessageLoopTest, DeleteUnboundLoop);
|
||||
|
||||
// Creates a MessageLoop without binding to a thread.
|
||||
//
|
||||
// It is valid to call this to create a new message loop on one thread,
|
||||
// and then pass it to the thread where the message loop actually runs.
|
||||
// The message loop's BindToCurrentThread() method must be called on the
|
||||
// thread the message loop runs on, before calling Run().
|
||||
// Before BindToCurrentThread() is called, only Post*Task() functions can
|
||||
// be called on the message loop.
|
||||
static std::unique_ptr<MessageLoop> CreateUnbound(MessagePumpType type);
|
||||
static std::unique_ptr<MessageLoop> CreateUnbound(
|
||||
std::unique_ptr<MessagePump> pump);
|
||||
|
||||
scoped_refptr<sequence_manager::TaskQueue> CreateDefaultTaskQueue();
|
||||
|
||||
std::unique_ptr<MessagePump> CreateMessagePump();
|
||||
|
||||
sequence_manager::internal::SequenceManagerImpl* GetSequenceManagerImpl()
|
||||
const {
|
||||
return sequence_manager_.get();
|
||||
}
|
||||
|
||||
const MessagePumpType type_;
|
||||
|
||||
// If set this will be returned by the next call to CreateMessagePump().
|
||||
// This is only set if |type_| is TYPE_CUSTOM and |pump_| is null.
|
||||
std::unique_ptr<MessagePump> custom_pump_;
|
||||
|
||||
// Id of the thread this message loop is bound to. Initialized once when the
|
||||
// MessageLoop is bound to its thread and constant forever after.
|
||||
PlatformThreadId thread_id_ = kInvalidThreadId;
|
||||
|
||||
// Verifies that calls are made on the thread on which BindToCurrentThread()
|
||||
// was invoked.
|
||||
THREAD_CHECKER(bound_thread_checker_);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessageLoop);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_H_
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_loop_current.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/message_loop/message_pump_for_io.h"
|
||||
#include "base/message_loop/message_pump_for_ui.h"
|
||||
#include "base/message_loop/message_pump_type.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/task/sequence_manager/sequence_manager_impl.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MessageLoopCurrent
|
||||
|
||||
// static
|
||||
sequence_manager::internal::SequenceManagerImpl*
|
||||
MessageLoopCurrent::GetCurrentSequenceManagerImpl() {
|
||||
return sequence_manager::internal::SequenceManagerImpl::GetCurrent();
|
||||
}
|
||||
|
||||
// static
|
||||
MessageLoopCurrent MessageLoopCurrent::Get() {
|
||||
return MessageLoopCurrent(GetCurrentSequenceManagerImpl());
|
||||
}
|
||||
|
||||
// static
|
||||
MessageLoopCurrent MessageLoopCurrent::GetNull() {
|
||||
return MessageLoopCurrent(nullptr);
|
||||
}
|
||||
|
||||
// static
|
||||
bool MessageLoopCurrent::IsSet() {
|
||||
return !!GetCurrentSequenceManagerImpl();
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::AddDestructionObserver(
|
||||
DestructionObserver* destruction_observer) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->AddDestructionObserver(destruction_observer);
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::RemoveDestructionObserver(
|
||||
DestructionObserver* destruction_observer) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->RemoveDestructionObserver(destruction_observer);
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::SetTaskRunner(
|
||||
scoped_refptr<SingleThreadTaskRunner> task_runner) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->SetTaskRunner(std::move(task_runner));
|
||||
}
|
||||
|
||||
bool MessageLoopCurrent::IsBoundToCurrentThread() const {
|
||||
return current_ == GetCurrentSequenceManagerImpl();
|
||||
}
|
||||
|
||||
bool MessageLoopCurrent::IsIdleForTesting() {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return current_->IsIdleForTesting();
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::AddTaskObserver(TaskObserver* task_observer) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->AddTaskObserver(task_observer);
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::RemoveTaskObserver(TaskObserver* task_observer) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->RemoveTaskObserver(task_observer);
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::SetAddQueueTimeToTasks(bool enable) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->SetAddQueueTimeToTasks(enable);
|
||||
}
|
||||
|
||||
void MessageLoopCurrent::SetNestableTasksAllowed(bool allowed) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
current_->SetTaskExecutionAllowed(allowed);
|
||||
}
|
||||
|
||||
bool MessageLoopCurrent::NestableTasksAllowed() const {
|
||||
return current_->IsTaskExecutionAllowed();
|
||||
}
|
||||
|
||||
MessageLoopCurrent::ScopedNestableTaskAllower::ScopedNestableTaskAllower()
|
||||
: sequence_manager_(GetCurrentSequenceManagerImpl()),
|
||||
old_state_(sequence_manager_->IsTaskExecutionAllowed()) {
|
||||
sequence_manager_->SetTaskExecutionAllowed(true);
|
||||
}
|
||||
|
||||
MessageLoopCurrent::ScopedNestableTaskAllower::~ScopedNestableTaskAllower() {
|
||||
sequence_manager_->SetTaskExecutionAllowed(old_state_);
|
||||
}
|
||||
|
||||
bool MessageLoopCurrent::operator==(const MessageLoopCurrent& other) const {
|
||||
return current_ == other.current_;
|
||||
}
|
||||
|
||||
#if !defined(OS_NACL)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MessageLoopCurrentForUI
|
||||
|
||||
// static
|
||||
MessageLoopCurrentForUI MessageLoopCurrentForUI::Get() {
|
||||
auto* sequence_manager = GetCurrentSequenceManagerImpl();
|
||||
DCHECK(sequence_manager);
|
||||
#if defined(OS_ANDROID)
|
||||
DCHECK(sequence_manager->IsType(MessagePumpType::UI) ||
|
||||
sequence_manager->IsType(MessagePumpType::JAVA));
|
||||
#else // defined(OS_ANDROID)
|
||||
DCHECK(sequence_manager->IsType(MessagePumpType::UI));
|
||||
#endif // defined(OS_ANDROID)
|
||||
return MessageLoopCurrentForUI(sequence_manager);
|
||||
}
|
||||
|
||||
// static
|
||||
bool MessageLoopCurrentForUI::IsSet() {
|
||||
sequence_manager::internal::SequenceManagerImpl* sequence_manager =
|
||||
GetCurrentSequenceManagerImpl();
|
||||
return sequence_manager &&
|
||||
#if defined(OS_ANDROID)
|
||||
(sequence_manager->IsType(MessagePumpType::UI) ||
|
||||
sequence_manager->IsType(MessagePumpType::JAVA));
|
||||
#else // defined(OS_ANDROID)
|
||||
sequence_manager->IsType(MessagePumpType::UI);
|
||||
#endif // defined(OS_ANDROID)
|
||||
}
|
||||
|
||||
MessagePumpForUI* MessageLoopCurrentForUI::GetMessagePumpForUI() const {
|
||||
return static_cast<MessagePumpForUI*>(current_->GetMessagePump());
|
||||
}
|
||||
|
||||
#if defined(USE_OZONE) && !defined(OS_FUCHSIA) && !defined(OS_WIN)
|
||||
bool MessageLoopCurrentForUI::WatchFileDescriptor(
|
||||
int fd,
|
||||
bool persistent,
|
||||
MessagePumpForUI::Mode mode,
|
||||
MessagePumpForUI::FdWatchController* controller,
|
||||
MessagePumpForUI::FdWatcher* delegate) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForUI()->WatchFileDescriptor(fd, persistent, mode,
|
||||
controller, delegate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(OS_IOS)
|
||||
void MessageLoopCurrentForUI::Attach() {
|
||||
current_->AttachToMessagePump();
|
||||
}
|
||||
#endif // defined(OS_IOS)
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
void MessageLoopCurrentForUI::Abort() {
|
||||
GetMessagePumpForUI()->Abort();
|
||||
}
|
||||
#endif // defined(OS_ANDROID)
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void MessageLoopCurrentForUI::AddMessagePumpObserver(
|
||||
MessagePumpForUI::Observer* observer) {
|
||||
GetMessagePumpForUI()->AddObserver(observer);
|
||||
}
|
||||
|
||||
void MessageLoopCurrentForUI::RemoveMessagePumpObserver(
|
||||
MessagePumpForUI::Observer* observer) {
|
||||
GetMessagePumpForUI()->RemoveObserver(observer);
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
#endif // !defined(OS_NACL)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// MessageLoopCurrentForIO
|
||||
|
||||
// static
|
||||
MessageLoopCurrentForIO MessageLoopCurrentForIO::Get() {
|
||||
auto* sequence_manager = GetCurrentSequenceManagerImpl();
|
||||
DCHECK(sequence_manager);
|
||||
DCHECK(sequence_manager->IsType(MessagePumpType::IO));
|
||||
return MessageLoopCurrentForIO(sequence_manager);
|
||||
}
|
||||
|
||||
// static
|
||||
bool MessageLoopCurrentForIO::IsSet() {
|
||||
auto* sequence_manager = GetCurrentSequenceManagerImpl();
|
||||
return sequence_manager && sequence_manager->IsType(MessagePumpType::IO);
|
||||
}
|
||||
|
||||
MessagePumpForIO* MessageLoopCurrentForIO::GetMessagePumpForIO() const {
|
||||
return static_cast<MessagePumpForIO*>(current_->GetMessagePump());
|
||||
}
|
||||
|
||||
#if !defined(OS_NACL_SFI)
|
||||
|
||||
#if defined(OS_WIN)
|
||||
HRESULT MessageLoopCurrentForIO::RegisterIOHandler(
|
||||
HANDLE file,
|
||||
MessagePumpForIO::IOHandler* handler) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForIO()->RegisterIOHandler(file, handler);
|
||||
}
|
||||
|
||||
bool MessageLoopCurrentForIO::RegisterJobObject(
|
||||
HANDLE job,
|
||||
MessagePumpForIO::IOHandler* handler) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForIO()->RegisterJobObject(job, handler);
|
||||
}
|
||||
|
||||
bool MessageLoopCurrentForIO::WaitForIOCompletion(
|
||||
DWORD timeout,
|
||||
MessagePumpForIO::IOHandler* filter) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForIO()->WaitForIOCompletion(timeout, filter);
|
||||
}
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
bool MessageLoopCurrentForIO::WatchFileDescriptor(
|
||||
int fd,
|
||||
bool persistent,
|
||||
MessagePumpForIO::Mode mode,
|
||||
MessagePumpForIO::FdWatchController* controller,
|
||||
MessagePumpForIO::FdWatcher* delegate) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForIO()->WatchFileDescriptor(fd, persistent, mode,
|
||||
controller, delegate);
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
#if defined(OS_MACOSX) && !defined(OS_IOS)
|
||||
bool MessageLoopCurrentForIO::WatchMachReceivePort(
|
||||
mach_port_t port,
|
||||
MessagePumpForIO::MachPortWatchController* controller,
|
||||
MessagePumpForIO::MachPortWatcher* delegate) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForIO()->WatchMachReceivePort(port, controller,
|
||||
delegate);
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // !defined(OS_NACL_SFI)
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
// Additional watch API for native platform resources.
|
||||
bool MessageLoopCurrentForIO::WatchZxHandle(
|
||||
zx_handle_t handle,
|
||||
bool persistent,
|
||||
zx_signals_t signals,
|
||||
MessagePumpForIO::ZxHandleWatchController* controller,
|
||||
MessagePumpForIO::ZxHandleWatcher* delegate) {
|
||||
DCHECK(current_->IsBoundToCurrentThread());
|
||||
return GetMessagePumpForIO()->WatchZxHandle(handle, persistent, signals,
|
||||
controller, delegate);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,305 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_
|
||||
|
||||
#include <ostream>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/scoped_refptr.h"
|
||||
#include "base/message_loop/message_pump_for_io.h"
|
||||
#include "base/message_loop/message_pump_for_ui.h"
|
||||
#include "base/pending_task.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/task/task_observer.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace web {
|
||||
class WebTaskEnvironment;
|
||||
}
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace sequence_manager {
|
||||
namespace internal {
|
||||
class SequenceManagerImpl;
|
||||
}
|
||||
} // namespace sequence_manager
|
||||
|
||||
// MessageLoopCurrent is a proxy to the public interface of the MessageLoop
|
||||
// bound to the thread it's obtained on.
|
||||
//
|
||||
// MessageLoopCurrent(ForUI|ForIO) is available statically through
|
||||
// MessageLoopCurrent(ForUI|ForIO)::Get() on threads that have a matching
|
||||
// MessageLoop instance. APIs intended for all consumers on the thread should be
|
||||
// on MessageLoopCurrent(ForUI|ForIO), while APIs intended for the owner of the
|
||||
// instance should be on MessageLoop(ForUI|ForIO).
|
||||
//
|
||||
// Why: Historically MessageLoop::current() gave access to the full MessageLoop
|
||||
// API, preventing both addition of powerful owner-only APIs as well as making
|
||||
// it harder to remove callers of deprecated APIs (that need to stick around for
|
||||
// a few owner-only use cases and re-accrue callers after cleanup per remaining
|
||||
// publicly available).
|
||||
//
|
||||
// As such, many methods below are flagged as deprecated and should be removed
|
||||
// (or moved back to MessageLoop) once all static callers have been migrated.
|
||||
class BASE_EXPORT MessageLoopCurrent {
|
||||
public:
|
||||
// MessageLoopCurrent is effectively just a disguised pointer and is fine to
|
||||
// copy/move around.
|
||||
MessageLoopCurrent(const MessageLoopCurrent& other) = default;
|
||||
MessageLoopCurrent(MessageLoopCurrent&& other) = default;
|
||||
MessageLoopCurrent& operator=(const MessageLoopCurrent& other) = default;
|
||||
|
||||
bool operator==(const MessageLoopCurrent& other) const;
|
||||
|
||||
// Returns a proxy object to interact with the MessageLoop running the
|
||||
// current thread. It must only be used on the thread it was obtained.
|
||||
static MessageLoopCurrent Get();
|
||||
|
||||
// Return an empty MessageLoopCurrent. No methods should be called on this
|
||||
// object.
|
||||
static MessageLoopCurrent GetNull();
|
||||
|
||||
// Returns true if the current thread is running a MessageLoop. Prefer this to
|
||||
// verifying the boolean value of Get() (so that Get() can ultimately DCHECK
|
||||
// it's only invoked when IsSet()).
|
||||
static bool IsSet();
|
||||
|
||||
// Allow MessageLoopCurrent to be used like a pointer to support the many
|
||||
// callsites that used MessageLoop::current() that way when it was a
|
||||
// MessageLoop*.
|
||||
MessageLoopCurrent* operator->() { return this; }
|
||||
explicit operator bool() const { return !!current_; }
|
||||
|
||||
// A DestructionObserver is notified when the current MessageLoop is being
|
||||
// destroyed. These observers are notified prior to MessageLoop::current()
|
||||
// being changed to return NULL. This gives interested parties the chance to
|
||||
// do final cleanup that depends on the MessageLoop.
|
||||
//
|
||||
// NOTE: Any tasks posted to the MessageLoop during this notification will
|
||||
// not be run. Instead, they will be deleted.
|
||||
//
|
||||
// Deprecation note: Prefer SequenceLocalStorageSlot<std::unique_ptr<Foo>> to
|
||||
// DestructionObserver to bind an object's lifetime to the current
|
||||
// thread/sequence.
|
||||
class BASE_EXPORT DestructionObserver {
|
||||
public:
|
||||
virtual void WillDestroyCurrentMessageLoop() = 0;
|
||||
|
||||
protected:
|
||||
virtual ~DestructionObserver() = default;
|
||||
};
|
||||
|
||||
// Add a DestructionObserver, which will start receiving notifications
|
||||
// immediately.
|
||||
void AddDestructionObserver(DestructionObserver* destruction_observer);
|
||||
|
||||
// Remove a DestructionObserver. It is safe to call this method while a
|
||||
// DestructionObserver is receiving a notification callback.
|
||||
void RemoveDestructionObserver(DestructionObserver* destruction_observer);
|
||||
|
||||
// Forwards to MessageLoop::SetTaskRunner().
|
||||
// DEPRECATED(https://crbug.com/825327): only owners of the MessageLoop
|
||||
// instance should replace its TaskRunner.
|
||||
void SetTaskRunner(scoped_refptr<SingleThreadTaskRunner> task_runner);
|
||||
|
||||
// Forwards to MessageLoop::(Add|Remove)TaskObserver.
|
||||
// DEPRECATED(https://crbug.com/825327): only owners of the MessageLoop
|
||||
// instance should add task observers on it.
|
||||
void AddTaskObserver(TaskObserver* task_observer);
|
||||
void RemoveTaskObserver(TaskObserver* task_observer);
|
||||
|
||||
// When this functionality is enabled, the queue time will be recorded for
|
||||
// posted tasks.
|
||||
void SetAddQueueTimeToTasks(bool enable);
|
||||
|
||||
// Enables or disables the recursive task processing. This happens in the case
|
||||
// of recursive message loops. Some unwanted message loops may occur when
|
||||
// using common controls or printer functions. By default, recursive task
|
||||
// processing is disabled.
|
||||
//
|
||||
// Please use |ScopedNestableTaskAllower| instead of calling these methods
|
||||
// directly. In general, nestable message loops are to be avoided. They are
|
||||
// dangerous and difficult to get right, so please use with extreme caution.
|
||||
//
|
||||
// The specific case where tasks get queued is:
|
||||
// - The thread is running a message loop.
|
||||
// - It receives a task #1 and executes it.
|
||||
// - The task #1 implicitly starts a message loop, like a MessageBox in the
|
||||
// unit test. This can also be StartDoc or GetSaveFileName.
|
||||
// - The thread receives a task #2 before or while in this second message
|
||||
// loop.
|
||||
// - With NestableTasksAllowed set to true, the task #2 will run right away.
|
||||
// Otherwise, it will get executed right after task #1 completes at "thread
|
||||
// message loop level".
|
||||
//
|
||||
// DEPRECATED(https://crbug.com/750779): Use RunLoop::Type on the relevant
|
||||
// RunLoop instead of these methods.
|
||||
// TODO(gab): Migrate usage and delete these methods.
|
||||
void SetNestableTasksAllowed(bool allowed);
|
||||
bool NestableTasksAllowed() const;
|
||||
|
||||
// Enables nestable tasks on the current MessageLoop while in scope.
|
||||
// DEPRECATED(https://crbug.com/750779): This should not be used when the
|
||||
// nested loop is driven by RunLoop (use RunLoop::Type::kNestableTasksAllowed
|
||||
// instead). It can however still be useful in a few scenarios where re-
|
||||
// entrancy is caused by a native message loop.
|
||||
// TODO(gab): Remove usage of this class alongside RunLoop and rename it to
|
||||
// ScopedApplicationTasksAllowedInNativeNestedLoop(?) for remaining use cases.
|
||||
class BASE_EXPORT ScopedNestableTaskAllower {
|
||||
public:
|
||||
ScopedNestableTaskAllower();
|
||||
~ScopedNestableTaskAllower();
|
||||
|
||||
private:
|
||||
sequence_manager::internal::SequenceManagerImpl* const sequence_manager_;
|
||||
const bool old_state_;
|
||||
};
|
||||
|
||||
// Returns true if this is the active MessageLoop for the current thread.
|
||||
bool IsBoundToCurrentThread() const;
|
||||
|
||||
// Returns true if the message loop is idle (ignoring delayed tasks). This is
|
||||
// the same condition which triggers DoWork() to return false: i.e.
|
||||
// out of tasks which can be processed at the current run-level -- there might
|
||||
// be deferred non-nestable tasks remaining if currently in a nested run
|
||||
// level.
|
||||
bool IsIdleForTesting();
|
||||
|
||||
protected:
|
||||
explicit MessageLoopCurrent(
|
||||
sequence_manager::internal::SequenceManagerImpl* sequence_manager)
|
||||
: current_(sequence_manager) {}
|
||||
|
||||
static sequence_manager::internal::SequenceManagerImpl*
|
||||
GetCurrentSequenceManagerImpl();
|
||||
|
||||
friend class MessagePumpLibeventTest;
|
||||
friend class ScheduleWorkTest;
|
||||
friend class Thread;
|
||||
friend class sequence_manager::internal::SequenceManagerImpl;
|
||||
friend class MessageLoopTaskRunnerTest;
|
||||
friend class web::WebTaskEnvironment;
|
||||
|
||||
sequence_manager::internal::SequenceManagerImpl* current_;
|
||||
};
|
||||
|
||||
#if !defined(OS_NACL)
|
||||
|
||||
// ForUI extension of MessageLoopCurrent.
|
||||
class BASE_EXPORT MessageLoopCurrentForUI : public MessageLoopCurrent {
|
||||
public:
|
||||
// Returns an interface for the MessageLoopForUI of the current thread.
|
||||
// Asserts that IsSet().
|
||||
static MessageLoopCurrentForUI Get();
|
||||
|
||||
// Returns true if the current thread is running a MessageLoopForUI.
|
||||
static bool IsSet();
|
||||
|
||||
MessageLoopCurrentForUI* operator->() { return this; }
|
||||
|
||||
#if defined(USE_OZONE) && !defined(OS_FUCHSIA) && !defined(OS_WIN)
|
||||
static_assert(
|
||||
std::is_base_of<WatchableIOMessagePumpPosix, MessagePumpForUI>::value,
|
||||
"MessageLoopCurrentForUI::WatchFileDescriptor is supported only"
|
||||
"by MessagePumpLibevent and MessagePumpGlib implementations.");
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
MessagePumpForUI::Mode mode,
|
||||
MessagePumpForUI::FdWatchController* controller,
|
||||
MessagePumpForUI::FdWatcher* delegate);
|
||||
#endif
|
||||
|
||||
#if defined(OS_IOS)
|
||||
// Forwards to MessageLoopForUI::Attach().
|
||||
// TODO(https://crbug.com/825327): Plumb the actual MessageLoopForUI* to
|
||||
// callers and remove ability to access this method from
|
||||
// MessageLoopCurrentForUI.
|
||||
void Attach();
|
||||
#endif
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// Forwards to MessageLoopForUI::Abort().
|
||||
// TODO(https://crbug.com/825327): Plumb the actual MessageLoopForUI* to
|
||||
// callers and remove ability to access this method from
|
||||
// MessageLoopCurrentForUI.
|
||||
void Abort();
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
void AddMessagePumpObserver(MessagePumpForUI::Observer* observer);
|
||||
void RemoveMessagePumpObserver(MessagePumpForUI::Observer* observer);
|
||||
#endif
|
||||
|
||||
private:
|
||||
explicit MessageLoopCurrentForUI(
|
||||
sequence_manager::internal::SequenceManagerImpl* current)
|
||||
: MessageLoopCurrent(current) {}
|
||||
|
||||
MessagePumpForUI* GetMessagePumpForUI() const;
|
||||
};
|
||||
|
||||
#endif // !defined(OS_NACL)
|
||||
|
||||
// ForIO extension of MessageLoopCurrent.
|
||||
class BASE_EXPORT MessageLoopCurrentForIO : public MessageLoopCurrent {
|
||||
public:
|
||||
// Returns an interface for the MessageLoopForIO of the current thread.
|
||||
// Asserts that IsSet().
|
||||
static MessageLoopCurrentForIO Get();
|
||||
|
||||
// Returns true if the current thread is running a MessageLoopForIO.
|
||||
static bool IsSet();
|
||||
|
||||
MessageLoopCurrentForIO* operator->() { return this; }
|
||||
|
||||
#if !defined(OS_NACL_SFI)
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Please see MessagePumpWin for definitions of these methods.
|
||||
HRESULT RegisterIOHandler(HANDLE file, MessagePumpForIO::IOHandler* handler);
|
||||
bool RegisterJobObject(HANDLE job, MessagePumpForIO::IOHandler* handler);
|
||||
bool WaitForIOCompletion(DWORD timeout, MessagePumpForIO::IOHandler* filter);
|
||||
#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
|
||||
// Please see WatchableIOMessagePumpPosix for definition.
|
||||
// Prefer base::FileDescriptorWatcher for non-critical IO.
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
MessagePumpForIO::Mode mode,
|
||||
MessagePumpForIO::FdWatchController* controller,
|
||||
MessagePumpForIO::FdWatcher* delegate);
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
#if defined(OS_MACOSX) && !defined(OS_IOS)
|
||||
bool WatchMachReceivePort(
|
||||
mach_port_t port,
|
||||
MessagePumpForIO::MachPortWatchController* controller,
|
||||
MessagePumpForIO::MachPortWatcher* delegate);
|
||||
#endif
|
||||
|
||||
#if defined(OS_FUCHSIA)
|
||||
// Additional watch API for native platform resources.
|
||||
bool WatchZxHandle(zx_handle_t handle,
|
||||
bool persistent,
|
||||
zx_signals_t signals,
|
||||
MessagePumpForIO::ZxHandleWatchController* controller,
|
||||
MessagePumpForIO::ZxHandleWatcher* delegate);
|
||||
#endif // defined(OS_FUCHSIA)
|
||||
|
||||
#endif // !defined(OS_NACL_SFI)
|
||||
|
||||
private:
|
||||
explicit MessageLoopCurrentForIO(
|
||||
sequence_manager::internal::SequenceManagerImpl* current)
|
||||
: MessageLoopCurrent(current) {}
|
||||
|
||||
MessagePumpForIO* GetMessagePumpForIO() const;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump.h"
|
||||
|
||||
#include "base/message_loop/message_pump_default.h"
|
||||
#include "base/message_loop/message_pump_for_io.h"
|
||||
#include "base/message_loop/message_pump_for_ui.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include "base/message_loop/message_pump_mac.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
MessagePump::MessagePumpFactory* message_pump_for_ui_factory_ = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
MessagePump::MessagePump() = default;
|
||||
|
||||
MessagePump::~MessagePump() = default;
|
||||
|
||||
void MessagePump::SetTimerSlack(TimerSlack) {
|
||||
}
|
||||
|
||||
// static
|
||||
void MessagePump::OverrideMessagePumpForUIFactory(MessagePumpFactory* factory) {
|
||||
DCHECK(!message_pump_for_ui_factory_);
|
||||
message_pump_for_ui_factory_ = factory;
|
||||
}
|
||||
|
||||
// static
|
||||
bool MessagePump::IsMessagePumpForUIFactoryOveridden() {
|
||||
return message_pump_for_ui_factory_ != nullptr;
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<MessagePump> MessagePump::Create(MessagePumpType type) {
|
||||
switch (type) {
|
||||
case MessagePumpType::UI:
|
||||
if (message_pump_for_ui_factory_)
|
||||
return message_pump_for_ui_factory_();
|
||||
#if defined(OS_IOS) || defined(OS_MACOSX)
|
||||
return MessagePumpMac::Create();
|
||||
#elif defined(OS_NACL) || defined(OS_AIX)
|
||||
// Currently NaCl and AIX don't have a UI MessagePump.
|
||||
// TODO(abarth): Figure out if we need this.
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
#else
|
||||
return std::make_unique<MessagePumpForUI>();
|
||||
#endif
|
||||
|
||||
case MessagePumpType::IO:
|
||||
return std::make_unique<MessagePumpForIO>();
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
case MessagePumpType::JAVA:
|
||||
return std::make_unique<MessagePumpForUI>();
|
||||
#endif
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
case MessagePumpType::NS_RUNLOOP:
|
||||
return std::make_unique<MessagePumpNSRunLoop>();
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
case MessagePumpType::UI_WITH_WM_QUIT_SUPPORT: {
|
||||
auto pump = std::make_unique<MessagePumpForUI>();
|
||||
pump->EnableWmQuit();
|
||||
return pump;
|
||||
}
|
||||
#endif // defined(OS_WIN)
|
||||
|
||||
case MessagePumpType::CUSTOM:
|
||||
NOTREACHED();
|
||||
return nullptr;
|
||||
|
||||
case MessagePumpType::DEFAULT:
|
||||
#if defined(OS_IOS)
|
||||
// On iOS, a native runloop is always required to pump system work.
|
||||
return std::make_unique<MessagePumpCFRunLoop>();
|
||||
#else
|
||||
return std::make_unique<MessagePumpDefault>();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
189
TMessagesProj/jni/voip/webrtc/base/message_loop/message_pump.h
Normal file
189
TMessagesProj/jni/voip/webrtc/base/message_loop/message_pump.h
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/message_loop/message_pump_type.h"
|
||||
#include "base/message_loop/timer_slack.h"
|
||||
#include "base/sequence_checker.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class TimeTicks;
|
||||
|
||||
class BASE_EXPORT MessagePump {
|
||||
public:
|
||||
using MessagePumpFactory = std::unique_ptr<MessagePump>();
|
||||
// Uses the given base::MessagePumpFactory to override the default MessagePump
|
||||
// implementation for 'MessagePumpType::UI'. May only be called once.
|
||||
static void OverrideMessagePumpForUIFactory(MessagePumpFactory* factory);
|
||||
|
||||
// Returns true if the MessagePumpForUI has been overidden.
|
||||
static bool IsMessagePumpForUIFactoryOveridden();
|
||||
|
||||
// Creates the default MessagePump based on |type|. Caller owns return value.
|
||||
static std::unique_ptr<MessagePump> Create(MessagePumpType type);
|
||||
|
||||
// Please see the comments above the Run method for an illustration of how
|
||||
// these delegate methods are used.
|
||||
class BASE_EXPORT Delegate {
|
||||
public:
|
||||
virtual ~Delegate() = default;
|
||||
|
||||
// Called before a unit of work internal to the message pump is executed.
|
||||
// This allows reports about individual units of work to be produced.
|
||||
// The unit of work ends when BeforeDoInternalWork() is called again, or
|
||||
// when BeforeWait(), DoSomeWork(), or DoIdleWork() is called.
|
||||
// TODO(crbug.com/851163): Place calls for all platforms.
|
||||
virtual void BeforeDoInternalWork() = 0;
|
||||
|
||||
// Called before the message pump starts waiting for work.
|
||||
// This indicates the end of the current unit of work, which is required
|
||||
// to produce reports about individual units of work.
|
||||
virtual void BeforeWait() = 0;
|
||||
|
||||
struct NextWorkInfo {
|
||||
// Helper to extract a TimeDelta for pumps that need a
|
||||
// timeout-till-next-task.
|
||||
TimeDelta remaining_delay() const {
|
||||
DCHECK(!delayed_run_time.is_null() && !delayed_run_time.is_max());
|
||||
DCHECK_GE(TimeTicks::Now(), recent_now);
|
||||
return delayed_run_time - recent_now;
|
||||
}
|
||||
|
||||
// Helper to verify if the next task is ready right away.
|
||||
bool is_immediate() const { return delayed_run_time.is_null(); }
|
||||
|
||||
// The next PendingTask's |delayed_run_time|. is_null() if there's extra
|
||||
// work to run immediately. is_max() if there are no more immediate nor
|
||||
// delayed tasks.
|
||||
TimeTicks delayed_run_time;
|
||||
|
||||
// A recent view of TimeTicks::Now(). Only valid if |next_task_run_time|
|
||||
// isn't null nor max. MessagePump impls should use remaining_delay()
|
||||
// instead of resampling Now() if they wish to sleep for a TimeDelta.
|
||||
TimeTicks recent_now;
|
||||
};
|
||||
|
||||
// Executes an immediate task or a ripe delayed task. Returns information
|
||||
// about when DoSomeWork() should be called again. If the returned
|
||||
// NextWorkInfo is_immediate(), DoSomeWork() must be invoked again shortly.
|
||||
// Else, DoSomeWork() must be invoked at |NextWorkInfo::delayed_run_time| or
|
||||
// when ScheduleWork() is invoked, whichever comes first. Redundant/spurious
|
||||
// invocations of DoSomeWork() outside of those requirements are tolerated.
|
||||
// DoIdleWork() will not be called so long as this returns a NextWorkInfo
|
||||
// which is_immediate().
|
||||
virtual NextWorkInfo DoSomeWork() = 0;
|
||||
|
||||
// Called from within Run just before the message pump goes to sleep.
|
||||
// Returns true to indicate that idle work was done. Returning false means
|
||||
// the pump will now wait.
|
||||
virtual bool DoIdleWork() = 0;
|
||||
};
|
||||
|
||||
MessagePump();
|
||||
virtual ~MessagePump();
|
||||
|
||||
// The Run method is called to enter the message pump's run loop.
|
||||
//
|
||||
// Within the method, the message pump is responsible for processing native
|
||||
// messages as well as for giving cycles to the delegate periodically. The
|
||||
// message pump should take care to mix delegate callbacks with native message
|
||||
// processing so neither type of event starves the other of cycles. Each call
|
||||
// to a delegate function or DoInternalWork() is considered the beginning of a
|
||||
// new "unit of work".
|
||||
//
|
||||
// The anatomy of a typical run loop:
|
||||
//
|
||||
// for (;;) {
|
||||
// bool did_internal_work = DoInternalWork();
|
||||
// if (should_quit_)
|
||||
// break;
|
||||
//
|
||||
// Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
|
||||
// if (should_quit_)
|
||||
// break;
|
||||
//
|
||||
// if (did_internal_work || next_work_info.is_immediate())
|
||||
// continue;
|
||||
//
|
||||
// bool did_idle_work = delegate_->DoIdleWork();
|
||||
// if (should_quit_)
|
||||
// break;
|
||||
//
|
||||
// if (did_idle_work)
|
||||
// continue;
|
||||
//
|
||||
// WaitForWork();
|
||||
// }
|
||||
//
|
||||
|
||||
// Here, DoInternalWork is some private method of the message pump that is
|
||||
// responsible for dispatching the next UI message or notifying the next IO
|
||||
// completion (for example). WaitForWork is a private method that simply
|
||||
// blocks until there is more work of any type to do.
|
||||
//
|
||||
// Notice that the run loop cycles between calling DoInternalWork and
|
||||
// DoSomeWork methods. This helps ensure that none of these work queues starve
|
||||
// the others. This is important for message pumps that are used to drive
|
||||
// animations, for example.
|
||||
//
|
||||
// Notice also that after each callout to foreign code, the run loop checks to
|
||||
// see if it should quit. The Quit method is responsible for setting this
|
||||
// flag. No further work is done once the quit flag is set.
|
||||
//
|
||||
// NOTE 1: Run may be called reentrantly from any of the callouts to foreign
|
||||
// code (internal work, DoSomeWork, DoIdleWork). As a result, DoSomeWork and
|
||||
// DoIdleWork must be reentrant.
|
||||
//
|
||||
// NOTE 2: Run implementations must arrange for DoSomeWork to be invoked as
|
||||
// expected if a callout to foreign code enters a message pump outside their
|
||||
// control. For example, the MessageBox API on Windows pumps UI messages. If
|
||||
// the MessageBox API is called (indirectly) from within Run, it is expected
|
||||
// that DoSomeWork will be invoked from within that call in response to
|
||||
// ScheduleWork or as requested by the last NextWorkInfo returned by
|
||||
// DoSomeWork. The MessagePump::Delegate may then elect to do nested work or
|
||||
// not depending on its policy in that context. Regardless of that decision
|
||||
// (and return value of the nested DoSomeWork() call), DoSomeWork() will be
|
||||
// invoked again when the nested loop unwinds.
|
||||
virtual void Run(Delegate* delegate) = 0;
|
||||
|
||||
// Quit immediately from the most recently entered run loop. This method may
|
||||
// only be used on the thread that called Run.
|
||||
virtual void Quit() = 0;
|
||||
|
||||
// Schedule a DoSomeWork callback to happen reasonably soon. Does nothing if
|
||||
// a DoSomeWork callback is already scheduled. Once this call is made,
|
||||
// DoSomeWork is guaranteed to be called repeatedly at least until it returns
|
||||
// a non-immediate NextWorkInfo. This call can be expensive and callers should
|
||||
// attempt not to invoke it again before a non-immediate NextWorkInfo was
|
||||
// returned from DoSomeWork(). Thread-safe (and callers should avoid holding a
|
||||
// Lock at all cost while making this call as some platforms' priority
|
||||
// boosting features have been observed to cause the caller to get descheduled
|
||||
// : https://crbug.com/890978).
|
||||
virtual void ScheduleWork() = 0;
|
||||
|
||||
// Schedule a DoSomeWork callback to happen at the specified time, cancelling
|
||||
// any pending callback scheduled by this method. This method may only be used
|
||||
// on the thread that called Run.
|
||||
//
|
||||
// It isn't necessary to call this during normal execution, as the pump wakes
|
||||
// up as requested by the return value of DoSomeWork().
|
||||
// TODO(crbug.com/885371): Determine if this must be called to ensure that
|
||||
// delayed tasks run when a message pump outside the control of Run is
|
||||
// entered.
|
||||
virtual void ScheduleDelayedWork(const TimeTicks& delayed_work_time) = 0;
|
||||
|
||||
// Sets the timer slack to the specified value.
|
||||
virtual void SetTimerSlack(TimerSlack timer_slack);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_H_
|
||||
|
|
@ -0,0 +1,376 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_android.h"
|
||||
|
||||
#include <android/looper.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <jni.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <utility>
|
||||
|
||||
#include "base/android/jni_android.h"
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "base/callback_helpers.h"
|
||||
#include "base/lazy_instance.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
// Android stripped sys/timerfd.h out of their platform headers, so we have to
|
||||
// use syscall to make use of timerfd. Once the min API level is 20, we can
|
||||
// directly use timerfd.h.
|
||||
#ifndef __NR_timerfd_create
|
||||
#error "Unable to find syscall for __NR_timerfd_create"
|
||||
#endif
|
||||
|
||||
#ifndef TFD_TIMER_ABSTIME
|
||||
#define TFD_TIMER_ABSTIME (1 << 0)
|
||||
#endif
|
||||
|
||||
using base::android::JavaParamRef;
|
||||
using base::android::ScopedJavaLocalRef;
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
// See sys/timerfd.h
|
||||
int timerfd_create(int clockid, int flags) {
|
||||
return syscall(__NR_timerfd_create, clockid, flags);
|
||||
}
|
||||
|
||||
// See sys/timerfd.h
|
||||
int timerfd_settime(int ufc,
|
||||
int flags,
|
||||
const struct itimerspec* utmr,
|
||||
struct itimerspec* otmr) {
|
||||
return syscall(__NR_timerfd_settime, ufc, flags, utmr, otmr);
|
||||
}
|
||||
|
||||
// https://crbug.com/873588. The stack may not be aligned when the ALooper calls
|
||||
// into our code due to the inconsistent ABI on older Android OS versions.
|
||||
#if defined(ARCH_CPU_X86)
|
||||
#define STACK_ALIGN __attribute__((force_align_arg_pointer))
|
||||
#else
|
||||
#define STACK_ALIGN
|
||||
#endif
|
||||
|
||||
STACK_ALIGN int NonDelayedLooperCallback(int fd, int events, void* data) {
|
||||
if (events & ALOOPER_EVENT_HANGUP)
|
||||
return 0;
|
||||
|
||||
DCHECK(events & ALOOPER_EVENT_INPUT);
|
||||
MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
|
||||
pump->OnNonDelayedLooperCallback();
|
||||
return 1; // continue listening for events
|
||||
}
|
||||
|
||||
STACK_ALIGN int DelayedLooperCallback(int fd, int events, void* data) {
|
||||
if (events & ALOOPER_EVENT_HANGUP)
|
||||
return 0;
|
||||
|
||||
DCHECK(events & ALOOPER_EVENT_INPUT);
|
||||
MessagePumpForUI* pump = reinterpret_cast<MessagePumpForUI*>(data);
|
||||
pump->OnDelayedLooperCallback();
|
||||
return 1; // continue listening for events
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MessagePumpForUI::MessagePumpForUI()
|
||||
: env_(base::android::AttachCurrentThread()) {
|
||||
// The Android native ALooper uses epoll to poll our file descriptors and wake
|
||||
// us up. We use a simple level-triggered eventfd to signal that non-delayed
|
||||
// work is available, and a timerfd to signal when delayed work is ready to
|
||||
// be run.
|
||||
non_delayed_fd_ = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
|
||||
CHECK_NE(non_delayed_fd_, -1);
|
||||
DCHECK_EQ(TimeTicks::GetClock(), TimeTicks::Clock::LINUX_CLOCK_MONOTONIC);
|
||||
|
||||
// We can't create the timerfd with TFD_NONBLOCK | TFD_CLOEXEC as we can't
|
||||
// include timerfd.h. See comments above on __NR_timerfd_create. It looks like
|
||||
// they're just aliases to O_NONBLOCK and O_CLOEXEC anyways, so this should be
|
||||
// fine.
|
||||
delayed_fd_ = timerfd_create(CLOCK_MONOTONIC, O_NONBLOCK | O_CLOEXEC);
|
||||
CHECK_NE(delayed_fd_, -1);
|
||||
|
||||
looper_ = ALooper_prepare(0);
|
||||
DCHECK(looper_);
|
||||
// Add a reference to the looper so it isn't deleted on us.
|
||||
ALooper_acquire(looper_);
|
||||
ALooper_addFd(looper_, non_delayed_fd_, 0, ALOOPER_EVENT_INPUT,
|
||||
&NonDelayedLooperCallback, reinterpret_cast<void*>(this));
|
||||
ALooper_addFd(looper_, delayed_fd_, 0, ALOOPER_EVENT_INPUT,
|
||||
&DelayedLooperCallback, reinterpret_cast<void*>(this));
|
||||
}
|
||||
|
||||
MessagePumpForUI::~MessagePumpForUI() {
|
||||
DCHECK_EQ(ALooper_forThread(), looper_);
|
||||
ALooper_removeFd(looper_, non_delayed_fd_);
|
||||
ALooper_removeFd(looper_, delayed_fd_);
|
||||
ALooper_release(looper_);
|
||||
looper_ = nullptr;
|
||||
|
||||
close(non_delayed_fd_);
|
||||
close(delayed_fd_);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::OnDelayedLooperCallback() {
|
||||
// There may be non-Chromium callbacks on the same ALooper which may have left
|
||||
// a pending exception set, and ALooper does not check for this between
|
||||
// callbacks. Check here, and if there's already an exception, just skip this
|
||||
// iteration without clearing the fd. If the exception ends up being non-fatal
|
||||
// then we'll just get called again on the next polling iteration.
|
||||
if (base::android::HasException(env_))
|
||||
return;
|
||||
|
||||
// ALooper_pollOnce may call this after Quit() if OnNonDelayedLooperCallback()
|
||||
// resulted in Quit() in the same round.
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
// Clear the fd.
|
||||
uint64_t value;
|
||||
int ret = read(delayed_fd_, &value, sizeof(value));
|
||||
|
||||
// TODO(mthiesse): Figure out how it's possible to hit EAGAIN here.
|
||||
// According to http://man7.org/linux/man-pages/man2/timerfd_create.2.html
|
||||
// EAGAIN only happens if no timer has expired. Also according to the man page
|
||||
// poll only returns readable when a timer has expired. So this function will
|
||||
// only be called when a timer has expired, but reading reveals no timer has
|
||||
// expired...
|
||||
// Quit() and ScheduleDelayedWork() are the only other functions that touch
|
||||
// the timerfd, and they both run on the same thread as this callback, so
|
||||
// there are no obvious timing or multi-threading related issues.
|
||||
DPCHECK(ret >= 0 || errno == EAGAIN);
|
||||
|
||||
delayed_scheduled_time_.reset();
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = delegate_->DoSomeWork();
|
||||
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
if (next_work_info.is_immediate()) {
|
||||
ScheduleWork();
|
||||
return;
|
||||
}
|
||||
|
||||
DoIdleWork();
|
||||
if (!next_work_info.delayed_run_time.is_max())
|
||||
ScheduleDelayedWork(next_work_info.delayed_run_time);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::OnNonDelayedLooperCallback() {
|
||||
// There may be non-Chromium callbacks on the same ALooper which may have left
|
||||
// a pending exception set, and ALooper does not check for this between
|
||||
// callbacks. Check here, and if there's already an exception, just skip this
|
||||
// iteration without clearing the fd. If the exception ends up being non-fatal
|
||||
// then we'll just get called again on the next polling iteration.
|
||||
if (base::android::HasException(env_))
|
||||
return;
|
||||
|
||||
// ALooper_pollOnce may call this after Quit() if OnDelayedLooperCallback()
|
||||
// resulted in Quit() in the same round.
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
// A bit added to the |non_delayed_fd_| to keep it signaled when we yield to
|
||||
// native tasks below.
|
||||
constexpr uint64_t kTryNativeTasksBeforeIdleBit = uint64_t(1) << 32;
|
||||
|
||||
// We're about to process all the work requested by ScheduleWork().
|
||||
// MessagePump users are expected to do their best not to invoke
|
||||
// ScheduleWork() again before DoSomeWork() returns a non-immediate
|
||||
// NextWorkInfo below. Hence, capturing the file descriptor's value now and
|
||||
// resetting its contents to 0 should be okay. The value currently stored
|
||||
// should be greater than 0 since work having been scheduled is the reason
|
||||
// we're here. See http://man7.org/linux/man-pages/man2/eventfd.2.html
|
||||
uint64_t pre_work_value = 0;
|
||||
int ret = read(non_delayed_fd_, &pre_work_value, sizeof(pre_work_value));
|
||||
DPCHECK(ret >= 0);
|
||||
DCHECK_GT(pre_work_value, 0U);
|
||||
|
||||
// Note: We can't skip DoSomeWork() even if
|
||||
// |pre_work_value == kTryNativeTasksBeforeIdleBit| here (i.e. no additional
|
||||
// ScheduleWork() since yielding to native) as delayed tasks might have come
|
||||
// in and we need to re-sample |next_work_info|.
|
||||
|
||||
// Runs all application tasks scheduled to run.
|
||||
Delegate::NextWorkInfo next_work_info;
|
||||
do {
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
next_work_info = delegate_->DoSomeWork();
|
||||
} while (next_work_info.is_immediate());
|
||||
|
||||
// Do not resignal |non_delayed_fd_| if we're quitting (this pump doesn't
|
||||
// allow nesting so needing to resume in an outer loop is not an issue
|
||||
// either).
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
// Before declaring this loop idle, yield to native tasks and arrange to be
|
||||
// called again (unless we're already in that second call).
|
||||
if (pre_work_value != kTryNativeTasksBeforeIdleBit) {
|
||||
// Note: This write() is racing with potential ScheduleWork() calls. This is
|
||||
// fine as write() is adding this bit, not overwriting the existing value,
|
||||
// and as such racing ScheduleWork() calls would merely add 1 to the lower
|
||||
// bits and we would find |pre_work_value != kTryNativeTasksBeforeIdleBit|
|
||||
// in the next cycle again, retrying this.
|
||||
ret = write(non_delayed_fd_, &kTryNativeTasksBeforeIdleBit,
|
||||
sizeof(kTryNativeTasksBeforeIdleBit));
|
||||
DPCHECK(ret >= 0);
|
||||
return;
|
||||
}
|
||||
|
||||
// We yielded to native tasks already and they didn't generate a
|
||||
// ScheduleWork() request so we can declare idleness. It's possible for a
|
||||
// ScheduleWork() request to come in racily while this method unwinds, this is
|
||||
// fine and will merely result in it being re-invoked shortly after it
|
||||
// returns.
|
||||
// TODO(scheduler-dev): this doesn't account for tasks that don't ever call
|
||||
// SchedulerWork() but still keep the system non-idle (e.g., the Java Handler
|
||||
// API). It would be better to add an API to query the presence of native
|
||||
// tasks instead of relying on yielding once + kTryNativeTasksBeforeIdleBit.
|
||||
DCHECK_EQ(pre_work_value, kTryNativeTasksBeforeIdleBit);
|
||||
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
// At this point, the java looper might not be idle - it's impossible to know
|
||||
// pre-Android-M, so we may end up doing Idle work while java tasks are still
|
||||
// queued up. Note that this won't cause us to fail to run java tasks using
|
||||
// QuitWhenIdle, as the JavaHandlerThread will finish running all currently
|
||||
// scheduled tasks before it quits. Also note that we can't just add an idle
|
||||
// callback to the java looper, as that will fire even if application tasks
|
||||
// are still queued up.
|
||||
DoIdleWork();
|
||||
if (!next_work_info.delayed_run_time.is_max())
|
||||
ScheduleDelayedWork(next_work_info.delayed_run_time);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::DoIdleWork() {
|
||||
if (delegate_->DoIdleWork()) {
|
||||
// If DoIdleWork() resulted in any work, we're not idle yet. We need to pump
|
||||
// the loop here because we may in fact be idle after doing idle work
|
||||
// without any new tasks being queued.
|
||||
ScheduleWork();
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::Run(Delegate* delegate) {
|
||||
DCHECK(IsTestImplementation());
|
||||
// This function is only called in tests. We manually pump the native looper
|
||||
// which won't run any java tasks.
|
||||
quit_ = false;
|
||||
|
||||
SetDelegate(delegate);
|
||||
|
||||
// Pump the loop once in case we're starting off idle as ALooper_pollOnce will
|
||||
// never return in that case.
|
||||
ScheduleWork();
|
||||
while (true) {
|
||||
// Waits for either the delayed, or non-delayed fds to be signalled, calling
|
||||
// either OnDelayedLooperCallback, or OnNonDelayedLooperCallback,
|
||||
// respectively. This uses Android's Looper implementation, which is based
|
||||
// off of epoll.
|
||||
ALooper_pollOnce(-1, nullptr, nullptr, nullptr);
|
||||
if (quit_)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::Attach(Delegate* delegate) {
|
||||
DCHECK(!quit_);
|
||||
|
||||
// Since the Looper is controlled by the UI thread or JavaHandlerThread, we
|
||||
// can't use Run() like we do on other platforms or we would prevent Java
|
||||
// tasks from running. Instead we create and initialize a run loop here, then
|
||||
// return control back to the Looper.
|
||||
|
||||
SetDelegate(delegate);
|
||||
run_loop_ = std::make_unique<RunLoop>();
|
||||
// Since the RunLoop was just created above, BeforeRun should be guaranteed to
|
||||
// return true (it only returns false if the RunLoop has been Quit already).
|
||||
if (!run_loop_->BeforeRun())
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
void MessagePumpForUI::Quit() {
|
||||
if (quit_)
|
||||
return;
|
||||
|
||||
quit_ = true;
|
||||
|
||||
int64_t value;
|
||||
// Clear any pending timer.
|
||||
read(delayed_fd_, &value, sizeof(value));
|
||||
// Clear the eventfd.
|
||||
read(non_delayed_fd_, &value, sizeof(value));
|
||||
|
||||
if (run_loop_) {
|
||||
run_loop_->AfterRun();
|
||||
run_loop_ = nullptr;
|
||||
}
|
||||
if (on_quit_callback_) {
|
||||
std::move(on_quit_callback_).Run();
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::ScheduleWork() {
|
||||
// Write (add) 1 to the eventfd. This tells the Looper to wake up and call our
|
||||
// callback, allowing us to run tasks. This also allows us to detect, when we
|
||||
// clear the fd, whether additional work was scheduled after we finished
|
||||
// performing work, but before we cleared the fd, as we'll read back >=2
|
||||
// instead of 1 in that case.
|
||||
// See the eventfd man pages
|
||||
// (http://man7.org/linux/man-pages/man2/eventfd.2.html) for details on how
|
||||
// the read and write APIs for this file descriptor work, specifically without
|
||||
// EFD_SEMAPHORE.
|
||||
uint64_t value = 1;
|
||||
int ret = write(non_delayed_fd_, &value, sizeof(value));
|
||||
DPCHECK(ret >= 0);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
|
||||
if (ShouldQuit())
|
||||
return;
|
||||
|
||||
if (delayed_scheduled_time_ && *delayed_scheduled_time_ == delayed_work_time)
|
||||
return;
|
||||
|
||||
DCHECK(!delayed_work_time.is_null());
|
||||
delayed_scheduled_time_ = delayed_work_time;
|
||||
int64_t nanos = delayed_work_time.since_origin().InNanoseconds();
|
||||
struct itimerspec ts;
|
||||
ts.it_interval.tv_sec = 0; // Don't repeat.
|
||||
ts.it_interval.tv_nsec = 0;
|
||||
ts.it_value.tv_sec = nanos / TimeTicks::kNanosecondsPerSecond;
|
||||
ts.it_value.tv_nsec = nanos % TimeTicks::kNanosecondsPerSecond;
|
||||
|
||||
int ret = timerfd_settime(delayed_fd_, TFD_TIMER_ABSTIME, &ts, nullptr);
|
||||
DPCHECK(ret >= 0);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::QuitWhenIdle(base::OnceClosure callback) {
|
||||
DCHECK(!on_quit_callback_);
|
||||
DCHECK(run_loop_);
|
||||
on_quit_callback_ = std::move(callback);
|
||||
run_loop_->QuitWhenIdle();
|
||||
// Pump the loop in case we're already idle.
|
||||
ScheduleWork();
|
||||
}
|
||||
|
||||
bool MessagePumpForUI::IsTestImplementation() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_
|
||||
|
||||
#include <jni.h>
|
||||
#include <memory>
|
||||
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "base/base_export.h"
|
||||
#include "base/callback.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
struct ALooper;
|
||||
|
||||
namespace base {
|
||||
|
||||
class RunLoop;
|
||||
|
||||
// This class implements a MessagePump needed for TYPE_UI MessageLoops on
|
||||
// OS_ANDROID platform.
|
||||
class BASE_EXPORT MessagePumpForUI : public MessagePump {
|
||||
public:
|
||||
MessagePumpForUI();
|
||||
~MessagePumpForUI() override;
|
||||
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
// Attaches |delegate| to this native MessagePump. |delegate| will from then
|
||||
// on be invoked by the native loop to process application tasks.
|
||||
virtual void Attach(Delegate* delegate);
|
||||
|
||||
// We call Abort when there is a pending JNI exception, meaning that the
|
||||
// current thread will crash when we return to Java.
|
||||
// We can't call any JNI-methods before returning to Java as we would then
|
||||
// cause a native crash (instead of the original Java crash).
|
||||
void Abort() { should_abort_ = true; }
|
||||
bool IsAborted() { return should_abort_; }
|
||||
bool ShouldQuit() const { return should_abort_ || quit_; }
|
||||
|
||||
// Tells the RunLoop to quit when idle, calling the callback when it's safe
|
||||
// for the Thread to stop.
|
||||
void QuitWhenIdle(base::OnceClosure callback);
|
||||
|
||||
// These functions are only public so that the looper callbacks can call them,
|
||||
// and should not be called from outside this class.
|
||||
void OnDelayedLooperCallback();
|
||||
void OnNonDelayedLooperCallback();
|
||||
|
||||
protected:
|
||||
void SetDelegate(Delegate* delegate) { delegate_ = delegate; }
|
||||
virtual bool IsTestImplementation() const;
|
||||
|
||||
private:
|
||||
void DoIdleWork();
|
||||
|
||||
// Unlike other platforms, we don't control the message loop as it's
|
||||
// controlled by the Android Looper, so we can't run a RunLoop to keep the
|
||||
// Thread this pump belongs to alive. However, threads are expected to have an
|
||||
// active run loop, so we manage a RunLoop internally here, starting/stopping
|
||||
// it as necessary.
|
||||
std::unique_ptr<RunLoop> run_loop_;
|
||||
|
||||
// See Abort().
|
||||
bool should_abort_ = false;
|
||||
|
||||
// Whether this message pump is quitting, or has quit.
|
||||
bool quit_ = false;
|
||||
|
||||
// The MessageLoop::Delegate for this pump.
|
||||
Delegate* delegate_ = nullptr;
|
||||
|
||||
// The time at which we are currently scheduled to wake up and perform a
|
||||
// delayed task. This avoids redundantly scheduling |delayed_fd_| with the
|
||||
// same timeout when subsequent work phases all go idle on the same pending
|
||||
// delayed task; nullopt if no wakeup is currently scheduled.
|
||||
Optional<TimeTicks> delayed_scheduled_time_;
|
||||
|
||||
// If set, a callback to fire when the message pump is quit.
|
||||
base::OnceClosure on_quit_callback_;
|
||||
|
||||
// The file descriptor used to signal that non-delayed work is available.
|
||||
int non_delayed_fd_;
|
||||
|
||||
// The file descriptor used to signal that delayed work is available.
|
||||
int delayed_fd_;
|
||||
|
||||
// The Android Looper for this thread.
|
||||
ALooper* looper_ = nullptr;
|
||||
|
||||
// The JNIEnv* for this thread, used to check for pending exceptions.
|
||||
JNIEnv* env_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpForUI);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_ANDROID_H_
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_default.h"
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/threading/thread_restrictions.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include <mach/thread_policy.h>
|
||||
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_mach_port.h"
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
MessagePumpDefault::MessagePumpDefault()
|
||||
: keep_running_(true),
|
||||
event_(WaitableEvent::ResetPolicy::AUTOMATIC,
|
||||
WaitableEvent::InitialState::NOT_SIGNALED) {
|
||||
event_.declare_only_used_while_idle();
|
||||
}
|
||||
|
||||
MessagePumpDefault::~MessagePumpDefault() = default;
|
||||
|
||||
void MessagePumpDefault::Run(Delegate* delegate) {
|
||||
AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
|
||||
|
||||
for (;;) {
|
||||
#if defined(OS_MACOSX)
|
||||
mac::ScopedNSAutoreleasePool autorelease_pool;
|
||||
#endif
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
|
||||
bool has_more_immediate_work = next_work_info.is_immediate();
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (has_more_immediate_work)
|
||||
continue;
|
||||
|
||||
has_more_immediate_work = delegate->DoIdleWork();
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (has_more_immediate_work)
|
||||
continue;
|
||||
|
||||
if (next_work_info.delayed_run_time.is_max()) {
|
||||
event_.Wait();
|
||||
} else {
|
||||
event_.TimedWait(next_work_info.remaining_delay());
|
||||
}
|
||||
// Since event_ is auto-reset, we don't need to do anything special here
|
||||
// other than service each delegate method.
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpDefault::Quit() {
|
||||
keep_running_ = false;
|
||||
}
|
||||
|
||||
void MessagePumpDefault::ScheduleWork() {
|
||||
// Since this can be called on any thread, we need to ensure that our Run
|
||||
// loop wakes up.
|
||||
event_.Signal();
|
||||
}
|
||||
|
||||
void MessagePumpDefault::ScheduleDelayedWork(
|
||||
const TimeTicks& delayed_work_time) {
|
||||
// Since this is always called from the same thread as Run(), there is nothing
|
||||
// to do as the loop is already running. It will wait in Run() with the
|
||||
// correct timeout when it's out of immediate tasks.
|
||||
// TODO(gab): Consider removing ScheduleDelayedWork() when all pumps function
|
||||
// this way (bit.ly/merge-message-pump-do-work).
|
||||
}
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
void MessagePumpDefault::SetTimerSlack(TimerSlack timer_slack) {
|
||||
thread_latency_qos_policy_data_t policy{};
|
||||
policy.thread_latency_qos_tier = timer_slack == TIMER_SLACK_MAXIMUM
|
||||
? LATENCY_QOS_TIER_3
|
||||
: LATENCY_QOS_TIER_UNSPECIFIED;
|
||||
mac::ScopedMachSendRight thread_port(mach_thread_self());
|
||||
kern_return_t kr =
|
||||
thread_policy_set(thread_port.get(), THREAD_LATENCY_QOS_POLICY,
|
||||
reinterpret_cast<thread_policy_t>(&policy),
|
||||
THREAD_LATENCY_QOS_POLICY_COUNT);
|
||||
MACH_DVLOG_IF(1, kr != KERN_SUCCESS, kr) << "thread_policy_set";
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class BASE_EXPORT MessagePumpDefault : public MessagePump {
|
||||
public:
|
||||
MessagePumpDefault();
|
||||
~MessagePumpDefault() override;
|
||||
|
||||
// MessagePump methods:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
#if defined(OS_MACOSX)
|
||||
void SetTimerSlack(TimerSlack timer_slack) override;
|
||||
#endif
|
||||
|
||||
private:
|
||||
// This flag is set to false when Run should return.
|
||||
bool keep_running_;
|
||||
|
||||
// Used to sleep until there is more work to do.
|
||||
WaitableEvent event_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpDefault);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_DEFAULT_H_
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_IO_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_IO_H_
|
||||
|
||||
// This header is a forwarding header to coalesce the various platform specific
|
||||
// types representing MessagePumpForIO.
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/message_loop/message_pump_win.h"
|
||||
#elif defined(OS_IOS)
|
||||
#include "base/message_loop/message_pump_io_ios.h"
|
||||
#elif defined(OS_MACOSX)
|
||||
#include "base/message_loop/message_pump_kqueue.h"
|
||||
#elif defined(OS_NACL_SFI)
|
||||
#include "base/message_loop/message_pump_default.h"
|
||||
#elif defined(OS_FUCHSIA)
|
||||
#include "base/message_loop/message_pump_fuchsia.h"
|
||||
#elif defined(OS_POSIX)
|
||||
#include "base/message_loop/message_pump_libevent.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Windows defines it as-is.
|
||||
using MessagePumpForIO = MessagePumpForIO;
|
||||
#elif defined(OS_IOS)
|
||||
using MessagePumpForIO = MessagePumpIOSForIO;
|
||||
#elif defined(OS_MACOSX)
|
||||
using MessagePumpForIO = MessagePumpKqueue;
|
||||
#elif defined(OS_NACL_SFI)
|
||||
using MessagePumpForIO = MessagePumpDefault;
|
||||
#elif defined(OS_FUCHSIA)
|
||||
using MessagePumpForIO = MessagePumpFuchsia;
|
||||
#elif defined(OS_POSIX)
|
||||
using MessagePumpForIO = MessagePumpLibevent;
|
||||
#else
|
||||
#error Platform does not define MessagePumpForIO
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_IO_H_
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_UI_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_UI_H_
|
||||
|
||||
// This header is a forwarding header to coalesce the various platform specific
|
||||
// implementations of MessagePumpForUI.
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/message_loop/message_pump_win.h"
|
||||
#elif defined(OS_ANDROID)
|
||||
#include "base/message_loop/message_pump_android.h"
|
||||
#elif defined(OS_MACOSX)
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#elif defined(OS_NACL) || defined(OS_AIX)
|
||||
// No MessagePumpForUI, see below.
|
||||
#elif defined(USE_GLIB)
|
||||
#include "base/message_loop/message_pump_glib.h"
|
||||
#elif defined(OS_LINUX) || defined(OS_BSD)
|
||||
#include "base/message_loop/message_pump_libevent.h"
|
||||
#elif defined(OS_FUCHSIA)
|
||||
#include "base/message_loop/message_pump_fuchsia.h"
|
||||
#endif
|
||||
|
||||
namespace base {
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// Windows defines it as-is.
|
||||
using MessagePumpForUI = MessagePumpForUI;
|
||||
#elif defined(OS_ANDROID)
|
||||
// Android defines it as-is.
|
||||
using MessagePumpForUI = MessagePumpForUI;
|
||||
#elif defined(OS_MACOSX)
|
||||
// MessagePumpForUI isn't bound to a specific impl on Mac. While each impl can
|
||||
// be represented by a plain MessagePump: MessagePumpMac::Create() must be used
|
||||
// to instantiate the right impl.
|
||||
using MessagePumpForUI = MessagePump;
|
||||
#elif defined(OS_NACL) || defined(OS_AIX)
|
||||
// Currently NaCl and AIX don't have a MessagePumpForUI.
|
||||
// TODO(abarth): Figure out if we need this.
|
||||
#elif defined(USE_GLIB)
|
||||
using MessagePumpForUI = MessagePumpGlib;
|
||||
#elif defined(OS_LINUX) || defined(OS_BSD)
|
||||
using MessagePumpForUI = MessagePumpLibevent;
|
||||
#elif defined(OS_FUCHSIA)
|
||||
using MessagePumpForUI = MessagePumpFuchsia;
|
||||
#else
|
||||
#error Platform does not define MessagePumpForUI
|
||||
#endif
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FOR_UI_H_
|
||||
|
|
@ -0,0 +1,320 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_fuchsia.h"
|
||||
|
||||
#include <lib/async-loop/cpp/loop.h>
|
||||
#include <lib/async-loop/default.h>
|
||||
#include <lib/fdio/io.h>
|
||||
#include <lib/fdio/unsafe.h>
|
||||
#include <lib/zx/time.h>
|
||||
#include <zircon/status.h>
|
||||
#include <zircon/syscalls.h>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/fuchsia/fuchsia_logging.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
MessagePumpFuchsia::ZxHandleWatchController::ZxHandleWatchController(
|
||||
const Location& from_here)
|
||||
: async_wait_t({}), created_from_location_(from_here) {}
|
||||
|
||||
MessagePumpFuchsia::ZxHandleWatchController::~ZxHandleWatchController() {
|
||||
if (!StopWatchingZxHandle())
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
bool MessagePumpFuchsia::ZxHandleWatchController::WaitBegin() {
|
||||
DCHECK(!handler);
|
||||
async_wait_t::handler = &HandleSignal;
|
||||
|
||||
zx_status_t status =
|
||||
async_begin_wait(weak_pump_->async_loop_->dispatcher(), this);
|
||||
if (status != ZX_OK) {
|
||||
ZX_DLOG(ERROR, status) << "async_begin_wait():"
|
||||
<< created_from_location_.ToString();
|
||||
async_wait_t::handler = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpFuchsia::ZxHandleWatchController::StopWatchingZxHandle() {
|
||||
if (was_stopped_) {
|
||||
DCHECK(!*was_stopped_);
|
||||
*was_stopped_ = true;
|
||||
|
||||
// |was_stopped_| points at a value stored on the stack, which will go out
|
||||
// of scope. MessagePumpFuchsia::Run() will reset it only if the value is
|
||||
// false. So we need to reset this pointer here as well, to make sure it's
|
||||
// not used again.
|
||||
was_stopped_ = nullptr;
|
||||
}
|
||||
|
||||
// If the pump is gone then there is nothing to cancel.
|
||||
if (!weak_pump_)
|
||||
return true;
|
||||
|
||||
// |handler| is set when waiting for a signal.
|
||||
if (!handler)
|
||||
return true;
|
||||
|
||||
async_wait_t::handler = nullptr;
|
||||
|
||||
zx_status_t result =
|
||||
async_cancel_wait(weak_pump_->async_loop_->dispatcher(), this);
|
||||
ZX_DLOG_IF(ERROR, result != ZX_OK, result)
|
||||
<< "async_cancel_wait(): " << created_from_location_.ToString();
|
||||
return result == ZX_OK;
|
||||
}
|
||||
|
||||
// static
|
||||
void MessagePumpFuchsia::ZxHandleWatchController::HandleSignal(
|
||||
async_dispatcher_t* async,
|
||||
async_wait_t* wait,
|
||||
zx_status_t status,
|
||||
const zx_packet_signal_t* signal) {
|
||||
TRACE_EVENT0("toplevel", "ZxHandleSignal");
|
||||
|
||||
ZxHandleWatchController* controller =
|
||||
static_cast<ZxHandleWatchController*>(wait);
|
||||
DCHECK_EQ(controller->handler, &HandleSignal);
|
||||
|
||||
if (status != ZX_OK) {
|
||||
ZX_DLOG(WARNING, status) << "async wait failed: "
|
||||
<< controller->created_from_location_.ToString();
|
||||
return;
|
||||
}
|
||||
|
||||
controller->handler = nullptr;
|
||||
|
||||
// In the case of a persistent Watch, the Watch may be stopped and
|
||||
// potentially deleted by the caller within the callback, in which case
|
||||
// |controller| should not be accessed again, and we mustn't continue the
|
||||
// watch. We check for this with a bool on the stack, which the Watch
|
||||
// receives a pointer to.
|
||||
bool was_stopped = false;
|
||||
controller->was_stopped_ = &was_stopped;
|
||||
|
||||
controller->watcher_->OnZxHandleSignalled(wait->object, signal->observed);
|
||||
|
||||
if (was_stopped)
|
||||
return;
|
||||
|
||||
controller->was_stopped_ = nullptr;
|
||||
|
||||
if (controller->persistent_)
|
||||
controller->WaitBegin();
|
||||
}
|
||||
|
||||
void MessagePumpFuchsia::FdWatchController::OnZxHandleSignalled(
|
||||
zx_handle_t handle,
|
||||
zx_signals_t signals) {
|
||||
uint32_t events;
|
||||
fdio_unsafe_wait_end(io_, signals, &events);
|
||||
|
||||
// |events| can include other spurious things, in particular, that an fd
|
||||
// is writable, when we only asked to know when it was readable. In that
|
||||
// case, we don't want to call both the CanWrite and CanRead callback,
|
||||
// when the caller asked for only, for example, readable callbacks. So,
|
||||
// mask with the events that we actually wanted to know about.
|
||||
events &= desired_events_;
|
||||
DCHECK_NE(0u, events);
|
||||
|
||||
// Each |watcher_| callback we invoke may stop or delete |this|. The pump has
|
||||
// set |was_stopped_| to point to a safe location on the calling stack, so we
|
||||
// can use that to detect being stopped mid-callback and avoid doing further
|
||||
// work that would touch |this|.
|
||||
bool* was_stopped = was_stopped_;
|
||||
if (events & FDIO_EVT_WRITABLE)
|
||||
watcher_->OnFileCanWriteWithoutBlocking(fd_);
|
||||
if (!*was_stopped && (events & FDIO_EVT_READABLE))
|
||||
watcher_->OnFileCanReadWithoutBlocking(fd_);
|
||||
|
||||
// Don't add additional work here without checking |*was_stopped_| again.
|
||||
}
|
||||
|
||||
MessagePumpFuchsia::FdWatchController::FdWatchController(
|
||||
const Location& from_here)
|
||||
: FdWatchControllerInterface(from_here),
|
||||
ZxHandleWatchController(from_here) {}
|
||||
|
||||
MessagePumpFuchsia::FdWatchController::~FdWatchController() {
|
||||
if (!StopWatchingFileDescriptor())
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
bool MessagePumpFuchsia::FdWatchController::WaitBegin() {
|
||||
// Refresh the |handle_| and |desired_signals_| from the mxio for the fd.
|
||||
// Some types of fdio map read/write events to different signals depending on
|
||||
// their current state, so we must do this every time we begin to wait.
|
||||
fdio_unsafe_wait_begin(io_, desired_events_, &object, &trigger);
|
||||
if (async_wait_t::object == ZX_HANDLE_INVALID) {
|
||||
DLOG(ERROR) << "fdio_wait_begin failed: "
|
||||
<< ZxHandleWatchController::created_from_location_.ToString();
|
||||
return false;
|
||||
}
|
||||
|
||||
return MessagePumpFuchsia::ZxHandleWatchController::WaitBegin();
|
||||
}
|
||||
|
||||
bool MessagePumpFuchsia::FdWatchController::StopWatchingFileDescriptor() {
|
||||
bool success = StopWatchingZxHandle();
|
||||
if (io_) {
|
||||
fdio_unsafe_release(io_);
|
||||
io_ = nullptr;
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
MessagePumpFuchsia::MessagePumpFuchsia()
|
||||
: async_loop_(new async::Loop(&kAsyncLoopConfigAttachToCurrentThread)),
|
||||
weak_factory_(this) {}
|
||||
MessagePumpFuchsia::~MessagePumpFuchsia() = default;
|
||||
|
||||
bool MessagePumpFuchsia::WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate) {
|
||||
DCHECK_GE(fd, 0);
|
||||
DCHECK(controller);
|
||||
DCHECK(delegate);
|
||||
|
||||
if (!controller->StopWatchingFileDescriptor())
|
||||
NOTREACHED();
|
||||
|
||||
controller->fd_ = fd;
|
||||
controller->watcher_ = delegate;
|
||||
|
||||
DCHECK(!controller->io_);
|
||||
controller->io_ = fdio_unsafe_fd_to_io(fd);
|
||||
if (!controller->io_) {
|
||||
DLOG(ERROR) << "Failed to get IO for FD";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (mode) {
|
||||
case WATCH_READ:
|
||||
controller->desired_events_ = FDIO_EVT_READABLE;
|
||||
break;
|
||||
case WATCH_WRITE:
|
||||
controller->desired_events_ = FDIO_EVT_WRITABLE;
|
||||
break;
|
||||
case WATCH_READ_WRITE:
|
||||
controller->desired_events_ = FDIO_EVT_READABLE | FDIO_EVT_WRITABLE;
|
||||
break;
|
||||
default:
|
||||
NOTREACHED() << "unexpected mode: " << mode;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Pass dummy |handle| and |signals| values to WatchZxHandle(). The real
|
||||
// values will be populated by FdWatchController::WaitBegin(), before actually
|
||||
// starting the wait operation.
|
||||
return WatchZxHandle(ZX_HANDLE_INVALID, persistent, 1, controller,
|
||||
controller);
|
||||
}
|
||||
|
||||
bool MessagePumpFuchsia::WatchZxHandle(zx_handle_t handle,
|
||||
bool persistent,
|
||||
zx_signals_t signals,
|
||||
ZxHandleWatchController* controller,
|
||||
ZxHandleWatcher* delegate) {
|
||||
DCHECK_NE(0u, signals);
|
||||
DCHECK(controller);
|
||||
DCHECK(delegate);
|
||||
DCHECK(handle == ZX_HANDLE_INVALID ||
|
||||
controller->async_wait_t::object == ZX_HANDLE_INVALID ||
|
||||
handle == controller->async_wait_t::object);
|
||||
|
||||
if (!controller->StopWatchingZxHandle())
|
||||
NOTREACHED();
|
||||
|
||||
controller->async_wait_t::object = handle;
|
||||
controller->persistent_ = persistent;
|
||||
controller->async_wait_t::trigger = signals;
|
||||
controller->watcher_ = delegate;
|
||||
|
||||
controller->weak_pump_ = weak_factory_.GetWeakPtr();
|
||||
|
||||
return controller->WaitBegin();
|
||||
}
|
||||
|
||||
bool MessagePumpFuchsia::HandleIoEventsUntil(zx_time_t deadline) {
|
||||
zx_status_t status = async_loop_->Run(zx::time(deadline), /*once=*/true);
|
||||
switch (status) {
|
||||
// Return true if some tasks or events were dispatched or if the dispatcher
|
||||
// was stopped by ScheduleWork().
|
||||
case ZX_OK:
|
||||
return true;
|
||||
|
||||
case ZX_ERR_CANCELED:
|
||||
async_loop_->ResetQuit();
|
||||
return true;
|
||||
|
||||
case ZX_ERR_TIMED_OUT:
|
||||
return false;
|
||||
|
||||
default:
|
||||
ZX_DLOG(DCHECK, status) << "unexpected wait status";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpFuchsia::Run(Delegate* delegate) {
|
||||
AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
|
||||
|
||||
for (;;) {
|
||||
const Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
const bool did_handle_io_event = HandleIoEventsUntil(/*deadline=*/0);
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
bool attempt_more_work =
|
||||
next_work_info.is_immediate() || did_handle_io_event;
|
||||
if (attempt_more_work)
|
||||
continue;
|
||||
|
||||
attempt_more_work = delegate->DoIdleWork();
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (attempt_more_work)
|
||||
continue;
|
||||
|
||||
zx_time_t deadline = next_work_info.delayed_run_time.is_max()
|
||||
? ZX_TIME_INFINITE
|
||||
: next_work_info.delayed_run_time.ToZxTime();
|
||||
|
||||
HandleIoEventsUntil(deadline);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpFuchsia::Quit() {
|
||||
keep_running_ = false;
|
||||
}
|
||||
|
||||
void MessagePumpFuchsia::ScheduleWork() {
|
||||
// Stop async_loop to let MessagePumpFuchsia::Run() handle message loop tasks.
|
||||
async_loop_->Quit();
|
||||
}
|
||||
|
||||
void MessagePumpFuchsia::ScheduleDelayedWork(
|
||||
const TimeTicks& delayed_work_time) {
|
||||
// Since this is always called from the same thread as Run(), there is nothing
|
||||
// to do as the loop is already running. It will wait in Run() with the
|
||||
// correct timeout when it's out of immediate tasks.
|
||||
// TODO(https://crbug.com/885371): Consider removing ScheduleDelayedWork()
|
||||
// when all pumps function this way (bit.ly/merge-message-pump-do-work).
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
// Copyright 2017 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
|
||||
|
||||
#include <lib/async/wait.h>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/location.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/message_loop/watchable_io_message_pump_posix.h"
|
||||
|
||||
typedef struct fdio fdio_t;
|
||||
|
||||
namespace async {
|
||||
class Loop;
|
||||
} // namespace async
|
||||
|
||||
namespace base {
|
||||
|
||||
class BASE_EXPORT MessagePumpFuchsia : public MessagePump,
|
||||
public WatchableIOMessagePumpPosix {
|
||||
public:
|
||||
// Implemented by callers to receive notifications of handle & fd events.
|
||||
class ZxHandleWatcher {
|
||||
public:
|
||||
virtual void OnZxHandleSignalled(zx_handle_t handle,
|
||||
zx_signals_t signals) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~ZxHandleWatcher() {}
|
||||
};
|
||||
|
||||
// Manages an active watch on an zx_handle_t.
|
||||
class ZxHandleWatchController : public async_wait_t {
|
||||
public:
|
||||
explicit ZxHandleWatchController(const Location& from_here);
|
||||
// Deleting the Controller implicitly calls StopWatchingZxHandle.
|
||||
virtual ~ZxHandleWatchController();
|
||||
|
||||
// Stop watching the handle, always safe to call. No-op if there's nothing
|
||||
// to do.
|
||||
bool StopWatchingZxHandle();
|
||||
|
||||
const Location& created_from_location() { return created_from_location_; }
|
||||
|
||||
protected:
|
||||
friend class MessagePumpFuchsia;
|
||||
|
||||
virtual bool WaitBegin();
|
||||
|
||||
static void HandleSignal(async_dispatcher_t* async,
|
||||
async_wait_t* wait,
|
||||
zx_status_t status,
|
||||
const zx_packet_signal_t* signal);
|
||||
|
||||
const Location created_from_location_;
|
||||
|
||||
// This bool is used by the pump when invoking the ZxHandleWatcher callback,
|
||||
// and by the FdHandleWatchController when invoking read & write callbacks,
|
||||
// to cope with the possibility of the caller deleting the *Watcher within
|
||||
// the callback. The pump sets |was_stopped_| to a location on the stack,
|
||||
// and the Watcher writes to it, if set, when deleted, allowing the pump
|
||||
// to check the value on the stack to short-cut any post-callback work.
|
||||
bool* was_stopped_ = nullptr;
|
||||
|
||||
// Set directly from the inputs to WatchFileDescriptor.
|
||||
ZxHandleWatcher* watcher_ = nullptr;
|
||||
|
||||
// Used to safely access resources owned by the associated message pump.
|
||||
WeakPtr<MessagePumpFuchsia> weak_pump_;
|
||||
|
||||
// A watch may be marked as persistent, which means it remains active even
|
||||
// after triggering.
|
||||
bool persistent_ = false;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ZxHandleWatchController);
|
||||
};
|
||||
|
||||
class FdWatchController : public FdWatchControllerInterface,
|
||||
public ZxHandleWatchController,
|
||||
public ZxHandleWatcher {
|
||||
public:
|
||||
explicit FdWatchController(const Location& from_here);
|
||||
~FdWatchController() override;
|
||||
|
||||
// FdWatchControllerInterface:
|
||||
bool StopWatchingFileDescriptor() override;
|
||||
|
||||
private:
|
||||
friend class MessagePumpFuchsia;
|
||||
|
||||
// Determines the desires signals, and begins waiting on the handle.
|
||||
bool WaitBegin() override;
|
||||
|
||||
// ZxHandleWatcher interface.
|
||||
void OnZxHandleSignalled(zx_handle_t handle, zx_signals_t signals) override;
|
||||
|
||||
// Set directly from the inputs to WatchFileDescriptor.
|
||||
FdWatcher* watcher_ = nullptr;
|
||||
int fd_ = -1;
|
||||
uint32_t desired_events_ = 0;
|
||||
|
||||
// Set by WatchFileDescriptor to hold a reference to the descriptor's mxio.
|
||||
fdio_t* io_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdWatchController);
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
WATCH_READ = 1 << 0,
|
||||
WATCH_WRITE = 1 << 1,
|
||||
WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
|
||||
};
|
||||
|
||||
MessagePumpFuchsia();
|
||||
~MessagePumpFuchsia() override;
|
||||
|
||||
bool WatchZxHandle(zx_handle_t handle,
|
||||
bool persistent,
|
||||
zx_signals_t signals,
|
||||
ZxHandleWatchController* controller,
|
||||
ZxHandleWatcher* delegate);
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate);
|
||||
|
||||
// MessagePump implementation:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
private:
|
||||
// Handles IO events by running |async_dispatcher_| until |deadline|. Returns
|
||||
// true if any events were received or if ScheduleWork() was called.
|
||||
bool HandleIoEventsUntil(zx_time_t deadline);
|
||||
|
||||
// This flag is set to false when Run should return.
|
||||
bool keep_running_ = true;
|
||||
|
||||
std::unique_ptr<async::Loop> async_loop_;
|
||||
|
||||
base::WeakPtrFactory<MessagePumpFuchsia> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpFuchsia);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_FUCHSIA_H_
|
||||
|
|
@ -0,0 +1,515 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_glib.h"
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <math.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/synchronization/lock.h"
|
||||
#include "base/threading/platform_thread.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
// Priorities of event sources are important to let everything be processed.
|
||||
// In particular, GTK event source should have the highest priority (because
|
||||
// UI events come from it), then Wayland events (the ones coming from the FD
|
||||
// watcher), and the lowest priority is GLib events (our base message pump).
|
||||
//
|
||||
// The g_source API uses ints to denote priorities, and the lower is its value,
|
||||
// the higher is the priority (i.e., they are ordered backwards).
|
||||
constexpr int kPriorityWork = G_PRIORITY_DEFAULT_IDLE;
|
||||
constexpr int kPriorityFdWatch = G_PRIORITY_DEFAULT_IDLE - 10;
|
||||
|
||||
// See the explanation above.
|
||||
static_assert(G_PRIORITY_DEFAULT < kPriorityFdWatch &&
|
||||
kPriorityFdWatch < kPriorityWork,
|
||||
"Wrong priorities are set for event sources!");
|
||||
|
||||
// Return a timeout suitable for the glib loop according to |next_task_time|, -1
|
||||
// to block forever, 0 to return right away, or a timeout in milliseconds from
|
||||
// now.
|
||||
int GetTimeIntervalMilliseconds(TimeTicks next_task_time) {
|
||||
if (next_task_time.is_null())
|
||||
return 0;
|
||||
else if (next_task_time.is_max())
|
||||
return -1;
|
||||
|
||||
auto timeout_ms =
|
||||
(next_task_time - TimeTicks::Now()).InMillisecondsRoundedUp();
|
||||
|
||||
return timeout_ms < 0 ? 0 : saturated_cast<int>(timeout_ms);
|
||||
}
|
||||
|
||||
// A brief refresher on GLib:
|
||||
// GLib sources have four callbacks: Prepare, Check, Dispatch and Finalize.
|
||||
// On each iteration of the GLib pump, it calls each source's Prepare function.
|
||||
// This function should return TRUE if it wants GLib to call its Dispatch, and
|
||||
// FALSE otherwise. It can also set a timeout in this case for the next time
|
||||
// Prepare should be called again (it may be called sooner).
|
||||
// After the Prepare calls, GLib does a poll to check for events from the
|
||||
// system. File descriptors can be attached to the sources. The poll may block
|
||||
// if none of the Prepare calls returned TRUE. It will block indefinitely, or
|
||||
// by the minimum time returned by a source in Prepare.
|
||||
// After the poll, GLib calls Check for each source that returned FALSE
|
||||
// from Prepare. The return value of Check has the same meaning as for Prepare,
|
||||
// making Check a second chance to tell GLib we are ready for Dispatch.
|
||||
// Finally, GLib calls Dispatch for each source that is ready. If Dispatch
|
||||
// returns FALSE, GLib will destroy the source. Dispatch calls may be recursive
|
||||
// (i.e., you can call Run from them), but Prepare and Check cannot.
|
||||
// Finalize is called when the source is destroyed.
|
||||
// NOTE: It is common for subsystems to want to process pending events while
|
||||
// doing intensive work, for example the flash plugin. They usually use the
|
||||
// following pattern (recommended by the GTK docs):
|
||||
// while (gtk_events_pending()) {
|
||||
// gtk_main_iteration();
|
||||
// }
|
||||
//
|
||||
// gtk_events_pending just calls g_main_context_pending, which does the
|
||||
// following:
|
||||
// - Call prepare on all the sources.
|
||||
// - Do the poll with a timeout of 0 (not blocking).
|
||||
// - Call check on all the sources.
|
||||
// - *Does not* call dispatch on the sources.
|
||||
// - Return true if any of prepare() or check() returned true.
|
||||
//
|
||||
// gtk_main_iteration just calls g_main_context_iteration, which does the whole
|
||||
// thing, respecting the timeout for the poll (and block, although it is to if
|
||||
// gtk_events_pending returned true), and call dispatch.
|
||||
//
|
||||
// Thus it is important to only return true from prepare or check if we
|
||||
// actually have events or work to do. We also need to make sure we keep
|
||||
// internal state consistent so that if prepare/check return true when called
|
||||
// from gtk_events_pending, they will still return true when called right
|
||||
// after, from gtk_main_iteration.
|
||||
//
|
||||
// For the GLib pump we try to follow the Windows UI pump model:
|
||||
// - Whenever we receive a wakeup event or the timer for delayed work expires,
|
||||
// we run DoSomeWork. That part will also run in the other event pumps.
|
||||
// - We also run DoSomeWork, and possibly DoIdleWork, in the main loop,
|
||||
// around event handling.
|
||||
|
||||
struct WorkSource : public GSource {
|
||||
MessagePumpGlib* pump;
|
||||
};
|
||||
|
||||
gboolean WorkSourcePrepare(GSource* source,
|
||||
gint* timeout_ms) {
|
||||
*timeout_ms = static_cast<WorkSource*>(source)->pump->HandlePrepare();
|
||||
// We always return FALSE, so that our timeout is honored. If we were
|
||||
// to return TRUE, the timeout would be considered to be 0 and the poll
|
||||
// would never block. Once the poll is finished, Check will be called.
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean WorkSourceCheck(GSource* source) {
|
||||
// Only return TRUE if Dispatch should be called.
|
||||
return static_cast<WorkSource*>(source)->pump->HandleCheck();
|
||||
}
|
||||
|
||||
gboolean WorkSourceDispatch(GSource* source,
|
||||
GSourceFunc unused_func,
|
||||
gpointer unused_data) {
|
||||
static_cast<WorkSource*>(source)->pump->HandleDispatch();
|
||||
// Always return TRUE so our source stays registered.
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
// I wish these could be const, but g_source_new wants non-const.
|
||||
GSourceFuncs WorkSourceFuncs = {WorkSourcePrepare, WorkSourceCheck,
|
||||
WorkSourceDispatch, nullptr};
|
||||
|
||||
// The following is used to make sure we only run the MessagePumpGlib on one
|
||||
// thread. X only has one message pump so we can only have one UI loop per
|
||||
// process.
|
||||
#ifndef NDEBUG
|
||||
|
||||
// Tracks the pump the most recent pump that has been run.
|
||||
struct ThreadInfo {
|
||||
// The pump.
|
||||
MessagePumpGlib* pump;
|
||||
|
||||
// ID of the thread the pump was run on.
|
||||
PlatformThreadId thread_id;
|
||||
};
|
||||
|
||||
// Used for accesing |thread_info|.
|
||||
Lock& GetThreadInfoLock() {
|
||||
static NoDestructor<Lock> thread_info_lock;
|
||||
return *thread_info_lock;
|
||||
}
|
||||
|
||||
// If non-null it means a MessagePumpGlib exists and has been Run. This is
|
||||
// destroyed when the MessagePump is destroyed.
|
||||
ThreadInfo* g_thread_info = nullptr;
|
||||
|
||||
void CheckThread(MessagePumpGlib* pump) {
|
||||
AutoLock auto_lock(GetThreadInfoLock());
|
||||
if (!g_thread_info) {
|
||||
g_thread_info = new ThreadInfo;
|
||||
g_thread_info->pump = pump;
|
||||
g_thread_info->thread_id = PlatformThread::CurrentId();
|
||||
}
|
||||
DCHECK_EQ(g_thread_info->thread_id, PlatformThread::CurrentId())
|
||||
<< "Running MessagePumpGlib on two different threads; "
|
||||
"this is unsupported by GLib!";
|
||||
}
|
||||
|
||||
void PumpDestroyed(MessagePumpGlib* pump) {
|
||||
AutoLock auto_lock(GetThreadInfoLock());
|
||||
if (g_thread_info && g_thread_info->pump == pump) {
|
||||
delete g_thread_info;
|
||||
g_thread_info = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
struct FdWatchSource : public GSource {
|
||||
MessagePumpGlib* pump;
|
||||
MessagePumpGlib::FdWatchController* controller;
|
||||
};
|
||||
|
||||
gboolean FdWatchSourcePrepare(GSource* source, gint* timeout_ms) {
|
||||
*timeout_ms = -1;
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
gboolean FdWatchSourceCheck(GSource* gsource) {
|
||||
auto* source = static_cast<FdWatchSource*>(gsource);
|
||||
return source->pump->HandleFdWatchCheck(source->controller) ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
gboolean FdWatchSourceDispatch(GSource* gsource,
|
||||
GSourceFunc unused_func,
|
||||
gpointer unused_data) {
|
||||
auto* source = static_cast<FdWatchSource*>(gsource);
|
||||
source->pump->HandleFdWatchDispatch(source->controller);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
GSourceFuncs g_fd_watch_source_funcs = {
|
||||
FdWatchSourcePrepare, FdWatchSourceCheck, FdWatchSourceDispatch, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct MessagePumpGlib::RunState {
|
||||
Delegate* delegate;
|
||||
|
||||
// Used to flag that the current Run() invocation should return ASAP.
|
||||
bool should_quit;
|
||||
|
||||
// Used to count how many Run() invocations are on the stack.
|
||||
int run_depth;
|
||||
|
||||
// The information of the next task available at this run-level. Stored in
|
||||
// RunState because different set of tasks can be accessible at various
|
||||
// run-levels (e.g. non-nestable tasks).
|
||||
Delegate::NextWorkInfo next_work_info;
|
||||
};
|
||||
|
||||
MessagePumpGlib::MessagePumpGlib()
|
||||
: state_(nullptr),
|
||||
context_(g_main_context_default()),
|
||||
wakeup_gpollfd_(new GPollFD) {
|
||||
// Create our wakeup pipe, which is used to flag when work was scheduled.
|
||||
int fds[2];
|
||||
int ret = pipe(fds);
|
||||
DCHECK_EQ(ret, 0);
|
||||
(void)ret; // Prevent warning in release mode.
|
||||
|
||||
wakeup_pipe_read_ = fds[0];
|
||||
wakeup_pipe_write_ = fds[1];
|
||||
wakeup_gpollfd_->fd = wakeup_pipe_read_;
|
||||
wakeup_gpollfd_->events = G_IO_IN;
|
||||
|
||||
work_source_ = g_source_new(&WorkSourceFuncs, sizeof(WorkSource));
|
||||
static_cast<WorkSource*>(work_source_)->pump = this;
|
||||
g_source_add_poll(work_source_, wakeup_gpollfd_.get());
|
||||
g_source_set_priority(work_source_, kPriorityWork);
|
||||
// This is needed to allow Run calls inside Dispatch.
|
||||
g_source_set_can_recurse(work_source_, TRUE);
|
||||
g_source_attach(work_source_, context_);
|
||||
}
|
||||
|
||||
MessagePumpGlib::~MessagePumpGlib() {
|
||||
#ifndef NDEBUG
|
||||
PumpDestroyed(this);
|
||||
#endif
|
||||
g_source_destroy(work_source_);
|
||||
g_source_unref(work_source_);
|
||||
close(wakeup_pipe_read_);
|
||||
close(wakeup_pipe_write_);
|
||||
}
|
||||
|
||||
MessagePumpGlib::FdWatchController::FdWatchController(const Location& location)
|
||||
: FdWatchControllerInterface(location) {}
|
||||
|
||||
MessagePumpGlib::FdWatchController::~FdWatchController() {
|
||||
if (IsInitialized()) {
|
||||
CHECK(StopWatchingFileDescriptor());
|
||||
}
|
||||
if (was_destroyed_) {
|
||||
DCHECK(!*was_destroyed_);
|
||||
*was_destroyed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::FdWatchController::StopWatchingFileDescriptor() {
|
||||
if (!IsInitialized())
|
||||
return false;
|
||||
|
||||
g_source_destroy(source_);
|
||||
g_source_unref(source_);
|
||||
source_ = nullptr;
|
||||
watcher_ = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::FdWatchController::IsInitialized() const {
|
||||
return !!source_;
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::FdWatchController::InitOrUpdate(int fd,
|
||||
int mode,
|
||||
FdWatcher* watcher) {
|
||||
gushort event_flags = 0;
|
||||
if (mode & WATCH_READ) {
|
||||
event_flags |= G_IO_IN;
|
||||
}
|
||||
if (mode & WATCH_WRITE) {
|
||||
event_flags |= G_IO_OUT;
|
||||
}
|
||||
|
||||
if (!IsInitialized()) {
|
||||
poll_fd_ = std::make_unique<GPollFD>();
|
||||
poll_fd_->fd = fd;
|
||||
} else {
|
||||
if (poll_fd_->fd != fd)
|
||||
return false;
|
||||
// Combine old/new event masks.
|
||||
event_flags |= poll_fd_->events;
|
||||
// Destroy previous source
|
||||
bool stopped = StopWatchingFileDescriptor();
|
||||
DCHECK(stopped);
|
||||
}
|
||||
poll_fd_->events = event_flags;
|
||||
poll_fd_->revents = 0;
|
||||
|
||||
source_ = g_source_new(&g_fd_watch_source_funcs, sizeof(FdWatchSource));
|
||||
DCHECK(source_);
|
||||
g_source_add_poll(source_, poll_fd_.get());
|
||||
g_source_set_can_recurse(source_, TRUE);
|
||||
g_source_set_callback(source_, nullptr, nullptr, nullptr);
|
||||
g_source_set_priority(source_, kPriorityFdWatch);
|
||||
|
||||
watcher_ = watcher;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::FdWatchController::Attach(MessagePumpGlib* pump) {
|
||||
DCHECK(pump);
|
||||
if (!IsInitialized()) {
|
||||
return false;
|
||||
}
|
||||
auto* source = static_cast<FdWatchSource*>(source_);
|
||||
source->controller = this;
|
||||
source->pump = pump;
|
||||
g_source_attach(source_, pump->context_);
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessagePumpGlib::FdWatchController::NotifyCanRead() {
|
||||
if (!watcher_)
|
||||
return;
|
||||
DCHECK(poll_fd_);
|
||||
watcher_->OnFileCanReadWithoutBlocking(poll_fd_->fd);
|
||||
}
|
||||
|
||||
void MessagePumpGlib::FdWatchController::NotifyCanWrite() {
|
||||
if (!watcher_)
|
||||
return;
|
||||
DCHECK(poll_fd_);
|
||||
watcher_->OnFileCanWriteWithoutBlocking(poll_fd_->fd);
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* watcher) {
|
||||
DCHECK_GE(fd, 0);
|
||||
DCHECK(controller);
|
||||
DCHECK(watcher);
|
||||
DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
|
||||
// WatchFileDescriptor should be called on the pump thread. It is not
|
||||
// threadsafe, so the watcher may never be registered.
|
||||
DCHECK_CALLED_ON_VALID_THREAD(watch_fd_caller_checker_);
|
||||
|
||||
if (!controller->InitOrUpdate(fd, mode, watcher)) {
|
||||
DPLOG(ERROR) << "FdWatchController init failed (fd=" << fd << ")";
|
||||
return false;
|
||||
}
|
||||
return controller->Attach(this);
|
||||
}
|
||||
|
||||
// Return the timeout we want passed to poll.
|
||||
int MessagePumpGlib::HandlePrepare() {
|
||||
// |state_| may be null during tests.
|
||||
if (!state_)
|
||||
return 0;
|
||||
|
||||
return GetTimeIntervalMilliseconds(state_->next_work_info.delayed_run_time);
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::HandleCheck() {
|
||||
if (!state_) // state_ may be null during tests.
|
||||
return false;
|
||||
|
||||
// We usually have a single message on the wakeup pipe, since we are only
|
||||
// signaled when the queue went from empty to non-empty, but there can be
|
||||
// two messages if a task posted a task, hence we read at most two bytes.
|
||||
// The glib poll will tell us whether there was data, so this read
|
||||
// shouldn't block.
|
||||
if (wakeup_gpollfd_->revents & G_IO_IN) {
|
||||
char msg[2];
|
||||
const int num_bytes = HANDLE_EINTR(read(wakeup_pipe_read_, msg, 2));
|
||||
if (num_bytes < 1) {
|
||||
NOTREACHED() << "Error reading from the wakeup pipe.";
|
||||
}
|
||||
DCHECK((num_bytes == 1 && msg[0] == '!') ||
|
||||
(num_bytes == 2 && msg[0] == '!' && msg[1] == '!'));
|
||||
// Since we ate the message, we need to record that we have immediate work,
|
||||
// because HandleCheck() may be called without HandleDispatch being called
|
||||
// afterwards.
|
||||
state_->next_work_info = {TimeTicks()};
|
||||
return true;
|
||||
}
|
||||
|
||||
// As described in the summary at the top : Check is a second-chance to
|
||||
// Prepare, verify whether we have work ready again.
|
||||
if (GetTimeIntervalMilliseconds(state_->next_work_info.delayed_run_time) ==
|
||||
0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessagePumpGlib::HandleDispatch() {
|
||||
state_->next_work_info = state_->delegate->DoSomeWork();
|
||||
}
|
||||
|
||||
void MessagePumpGlib::Run(Delegate* delegate) {
|
||||
#ifndef NDEBUG
|
||||
CheckThread(this);
|
||||
#endif
|
||||
|
||||
RunState state;
|
||||
state.delegate = delegate;
|
||||
state.should_quit = false;
|
||||
state.run_depth = state_ ? state_->run_depth + 1 : 1;
|
||||
|
||||
RunState* previous_state = state_;
|
||||
state_ = &state;
|
||||
|
||||
// We really only do a single task for each iteration of the loop. If we
|
||||
// have done something, assume there is likely something more to do. This
|
||||
// will mean that we don't block on the message pump until there was nothing
|
||||
// more to do. We also set this to true to make sure not to block on the
|
||||
// first iteration of the loop, so RunUntilIdle() works correctly.
|
||||
bool more_work_is_plausible = true;
|
||||
|
||||
// We run our own loop instead of using g_main_loop_quit in one of the
|
||||
// callbacks. This is so we only quit our own loops, and we don't quit
|
||||
// nested loops run by others. TODO(deanm): Is this what we want?
|
||||
for (;;) {
|
||||
// Don't block if we think we have more work to do.
|
||||
bool block = !more_work_is_plausible;
|
||||
|
||||
more_work_is_plausible = g_main_context_iteration(context_, block);
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
state_->next_work_info = state_->delegate->DoSomeWork();
|
||||
more_work_is_plausible |= state_->next_work_info.is_immediate();
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
if (more_work_is_plausible)
|
||||
continue;
|
||||
|
||||
more_work_is_plausible = state_->delegate->DoIdleWork();
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
}
|
||||
|
||||
state_ = previous_state;
|
||||
}
|
||||
|
||||
void MessagePumpGlib::Quit() {
|
||||
if (state_) {
|
||||
state_->should_quit = true;
|
||||
} else {
|
||||
NOTREACHED() << "Quit called outside Run!";
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpGlib::ScheduleWork() {
|
||||
// This can be called on any thread, so we don't want to touch any state
|
||||
// variables as we would then need locks all over. This ensures that if
|
||||
// we are sleeping in a poll that we will wake up.
|
||||
char msg = '!';
|
||||
if (HANDLE_EINTR(write(wakeup_pipe_write_, &msg, 1)) != 1) {
|
||||
NOTREACHED() << "Could not write to the UI message loop wakeup pipe!";
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpGlib::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
|
||||
// We need to wake up the loop in case the poll timeout needs to be
|
||||
// adjusted. This will cause us to try to do work, but that's OK.
|
||||
ScheduleWork();
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::HandleFdWatchCheck(FdWatchController* controller) {
|
||||
DCHECK(controller);
|
||||
gushort flags = controller->poll_fd_->revents;
|
||||
return (flags & G_IO_IN) || (flags & G_IO_OUT);
|
||||
}
|
||||
|
||||
void MessagePumpGlib::HandleFdWatchDispatch(FdWatchController* controller) {
|
||||
DCHECK(controller);
|
||||
DCHECK(controller->poll_fd_);
|
||||
gushort flags = controller->poll_fd_->revents;
|
||||
if ((flags & G_IO_IN) && (flags & G_IO_OUT)) {
|
||||
// Both callbacks will be called. It is necessary to check that
|
||||
// |controller| is not destroyed.
|
||||
bool controller_was_destroyed = false;
|
||||
controller->was_destroyed_ = &controller_was_destroyed;
|
||||
controller->NotifyCanWrite();
|
||||
if (!controller_was_destroyed)
|
||||
controller->NotifyCanRead();
|
||||
if (!controller_was_destroyed)
|
||||
controller->was_destroyed_ = nullptr;
|
||||
} else if (flags & G_IO_IN) {
|
||||
controller->NotifyCanRead();
|
||||
} else if (flags & G_IO_OUT) {
|
||||
controller->NotifyCanWrite();
|
||||
}
|
||||
}
|
||||
|
||||
bool MessagePumpGlib::ShouldQuit() const {
|
||||
CHECK(state_);
|
||||
return state_->should_quit;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/message_loop/watchable_io_message_pump_posix.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "base/time/time.h"
|
||||
|
||||
typedef struct _GMainContext GMainContext;
|
||||
typedef struct _GPollFD GPollFD;
|
||||
typedef struct _GSource GSource;
|
||||
|
||||
namespace base {
|
||||
|
||||
// This class implements a base MessagePump needed for TYPE_UI MessageLoops on
|
||||
// platforms using GLib.
|
||||
class BASE_EXPORT MessagePumpGlib : public MessagePump,
|
||||
public WatchableIOMessagePumpPosix {
|
||||
public:
|
||||
class FdWatchController : public FdWatchControllerInterface {
|
||||
public:
|
||||
explicit FdWatchController(const Location& from_here);
|
||||
~FdWatchController() override;
|
||||
|
||||
// FdWatchControllerInterface:
|
||||
bool StopWatchingFileDescriptor() override;
|
||||
|
||||
private:
|
||||
friend class MessagePumpGlib;
|
||||
friend class MessagePumpGLibFdWatchTest;
|
||||
|
||||
// FdWatchController instances can be reused (unless fd changes), so we
|
||||
// need to keep track of initialization status and taking it into account
|
||||
// when setting up a fd watching. Please refer to
|
||||
// WatchableIOMessagePumpPosix docs for more details. This is called by
|
||||
// WatchFileDescriptor() and sets up a GSource for the input parameters.
|
||||
// The source is not attached here, so the events will not be fired until
|
||||
// Attach() is called.
|
||||
bool InitOrUpdate(int fd, int mode, FdWatcher* watcher);
|
||||
// Returns the current initialization status.
|
||||
bool IsInitialized() const;
|
||||
|
||||
// Tries to attach the internal GSource instance to the |pump|'s
|
||||
// GMainContext, so IO events start to be dispatched. Returns false if
|
||||
// |this| is not correctly initialized, otherwise returns true.
|
||||
bool Attach(MessagePumpGlib* pump);
|
||||
|
||||
// Forward read and write events to |watcher_|. It is a no-op if watcher_
|
||||
// is null, which can happen when controller is suddenly stopped through
|
||||
// StopWatchingFileDescriptor().
|
||||
void NotifyCanRead();
|
||||
void NotifyCanWrite();
|
||||
|
||||
FdWatcher* watcher_ = nullptr;
|
||||
GSource* source_ = nullptr;
|
||||
std::unique_ptr<GPollFD> poll_fd_;
|
||||
// If this pointer is non-null, the pointee is set to true in the
|
||||
// destructor.
|
||||
bool* was_destroyed_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdWatchController);
|
||||
};
|
||||
|
||||
MessagePumpGlib();
|
||||
~MessagePumpGlib() override;
|
||||
|
||||
// Part of WatchableIOMessagePumpPosix interface.
|
||||
// Please refer to WatchableIOMessagePumpPosix docs for more details.
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate);
|
||||
|
||||
// Internal methods used for processing the pump callbacks. They are public
|
||||
// for simplicity but should not be used directly. HandlePrepare is called
|
||||
// during the prepare step of glib, and returns a timeout that will be passed
|
||||
// to the poll. HandleCheck is called after the poll has completed, and
|
||||
// returns whether or not HandleDispatch should be called. HandleDispatch is
|
||||
// called if HandleCheck returned true.
|
||||
int HandlePrepare();
|
||||
bool HandleCheck();
|
||||
void HandleDispatch();
|
||||
|
||||
// Overridden from MessagePump:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
// Internal methods used for processing the FdWatchSource callbacks. As for
|
||||
// main pump callbacks, they are public for simplicity but should not be used
|
||||
// directly.
|
||||
bool HandleFdWatchCheck(FdWatchController* controller);
|
||||
void HandleFdWatchDispatch(FdWatchController* controller);
|
||||
|
||||
private:
|
||||
bool ShouldQuit() const;
|
||||
|
||||
// We may make recursive calls to Run, so we save state that needs to be
|
||||
// separate between them in this structure type.
|
||||
struct RunState;
|
||||
|
||||
RunState* state_;
|
||||
|
||||
// This is a GLib structure that we can add event sources to. We use the
|
||||
// default GLib context, which is the one to which all GTK events are
|
||||
// dispatched.
|
||||
GMainContext* context_;
|
||||
|
||||
// The work source. It is shared by all calls to Run and destroyed when
|
||||
// the message pump is destroyed.
|
||||
GSource* work_source_;
|
||||
|
||||
// We use a wakeup pipe to make sure we'll get out of the glib polling phase
|
||||
// when another thread has scheduled us to do some work. There is a glib
|
||||
// mechanism g_main_context_wakeup, but this won't guarantee that our event's
|
||||
// Dispatch() will be called.
|
||||
int wakeup_pipe_read_;
|
||||
int wakeup_pipe_write_;
|
||||
// Use a unique_ptr to avoid needing the definition of GPollFD in the header.
|
||||
std::unique_ptr<GPollFD> wakeup_gpollfd_;
|
||||
|
||||
THREAD_CHECKER(watch_fd_caller_checker_);
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpGlib);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_GLIB_H_
|
||||
|
|
@ -0,0 +1,182 @@
|
|||
// Copyright 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_io_ios.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
MessagePumpIOSForIO::FdWatchController::FdWatchController(
|
||||
const Location& from_here)
|
||||
: FdWatchControllerInterface(from_here) {}
|
||||
|
||||
MessagePumpIOSForIO::FdWatchController::~FdWatchController() {
|
||||
StopWatchingFileDescriptor();
|
||||
}
|
||||
|
||||
bool MessagePumpIOSForIO::FdWatchController::StopWatchingFileDescriptor() {
|
||||
if (fdref_ == NULL)
|
||||
return true;
|
||||
|
||||
CFFileDescriptorDisableCallBacks(fdref_.get(), callback_types_);
|
||||
if (pump_)
|
||||
pump_->RemoveRunLoopSource(fd_source_);
|
||||
fd_source_.reset();
|
||||
fdref_.reset();
|
||||
callback_types_ = 0;
|
||||
pump_.reset();
|
||||
watcher_ = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessagePumpIOSForIO::FdWatchController::Init(CFFileDescriptorRef fdref,
|
||||
CFOptionFlags callback_types,
|
||||
CFRunLoopSourceRef fd_source,
|
||||
bool is_persistent) {
|
||||
DCHECK(fdref);
|
||||
DCHECK(!fdref_.is_valid());
|
||||
|
||||
is_persistent_ = is_persistent;
|
||||
fdref_.reset(fdref);
|
||||
callback_types_ = callback_types;
|
||||
fd_source_.reset(fd_source);
|
||||
}
|
||||
|
||||
void MessagePumpIOSForIO::FdWatchController::OnFileCanReadWithoutBlocking(
|
||||
int fd,
|
||||
MessagePumpIOSForIO* pump) {
|
||||
DCHECK(callback_types_ & kCFFileDescriptorReadCallBack);
|
||||
watcher_->OnFileCanReadWithoutBlocking(fd);
|
||||
}
|
||||
|
||||
void MessagePumpIOSForIO::FdWatchController::OnFileCanWriteWithoutBlocking(
|
||||
int fd,
|
||||
MessagePumpIOSForIO* pump) {
|
||||
DCHECK(callback_types_ & kCFFileDescriptorWriteCallBack);
|
||||
watcher_->OnFileCanWriteWithoutBlocking(fd);
|
||||
}
|
||||
|
||||
MessagePumpIOSForIO::MessagePumpIOSForIO() : weak_factory_(this) {
|
||||
}
|
||||
|
||||
MessagePumpIOSForIO::~MessagePumpIOSForIO() {
|
||||
}
|
||||
|
||||
bool MessagePumpIOSForIO::WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate) {
|
||||
DCHECK_GE(fd, 0);
|
||||
DCHECK(controller);
|
||||
DCHECK(delegate);
|
||||
DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
|
||||
|
||||
// WatchFileDescriptor should be called on the pump thread. It is not
|
||||
// threadsafe, and your watcher may never be registered.
|
||||
DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
|
||||
|
||||
CFFileDescriptorContext source_context = {0};
|
||||
source_context.info = controller;
|
||||
|
||||
CFOptionFlags callback_types = 0;
|
||||
if (mode & WATCH_READ) {
|
||||
callback_types |= kCFFileDescriptorReadCallBack;
|
||||
}
|
||||
if (mode & WATCH_WRITE) {
|
||||
callback_types |= kCFFileDescriptorWriteCallBack;
|
||||
}
|
||||
|
||||
CFFileDescriptorRef fdref = controller->fdref_.get();
|
||||
if (fdref == NULL) {
|
||||
base::ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
|
||||
CFFileDescriptorCreate(
|
||||
kCFAllocatorDefault, fd, false, HandleFdIOEvent, &source_context));
|
||||
if (scoped_fdref == NULL) {
|
||||
NOTREACHED() << "CFFileDescriptorCreate failed";
|
||||
return false;
|
||||
}
|
||||
|
||||
CFFileDescriptorEnableCallBacks(scoped_fdref, callback_types);
|
||||
|
||||
// TODO(wtc): what should the 'order' argument be?
|
||||
base::ScopedCFTypeRef<CFRunLoopSourceRef> scoped_fd_source(
|
||||
CFFileDescriptorCreateRunLoopSource(
|
||||
kCFAllocatorDefault, scoped_fdref, 0));
|
||||
if (scoped_fd_source == NULL) {
|
||||
NOTREACHED() << "CFFileDescriptorCreateRunLoopSource failed";
|
||||
return false;
|
||||
}
|
||||
CFRunLoopAddSource(run_loop(), scoped_fd_source, kCFRunLoopCommonModes);
|
||||
|
||||
// Transfer ownership of scoped_fdref and fd_source to controller.
|
||||
controller->Init(scoped_fdref.release(), callback_types,
|
||||
scoped_fd_source.release(), persistent);
|
||||
} else {
|
||||
// It's illegal to use this function to listen on 2 separate fds with the
|
||||
// same |controller|.
|
||||
if (CFFileDescriptorGetNativeDescriptor(fdref) != fd) {
|
||||
NOTREACHED() << "FDs don't match: "
|
||||
<< CFFileDescriptorGetNativeDescriptor(fdref)
|
||||
<< " != " << fd;
|
||||
return false;
|
||||
}
|
||||
if (persistent != controller->is_persistent_) {
|
||||
NOTREACHED() << "persistent doesn't match";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Combine old/new event masks.
|
||||
CFFileDescriptorDisableCallBacks(fdref, controller->callback_types_);
|
||||
controller->callback_types_ |= callback_types;
|
||||
CFFileDescriptorEnableCallBacks(fdref, controller->callback_types_);
|
||||
}
|
||||
|
||||
controller->set_watcher(delegate);
|
||||
controller->set_pump(weak_factory_.GetWeakPtr());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessagePumpIOSForIO::RemoveRunLoopSource(CFRunLoopSourceRef source) {
|
||||
CFRunLoopRemoveSource(run_loop(), source, kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
// static
|
||||
void MessagePumpIOSForIO::HandleFdIOEvent(CFFileDescriptorRef fdref,
|
||||
CFOptionFlags callback_types,
|
||||
void* context) {
|
||||
FdWatchController* controller = static_cast<FdWatchController*>(context);
|
||||
DCHECK_EQ(fdref, controller->fdref_.get());
|
||||
|
||||
// Ensure that |fdref| will remain live for the duration of this function
|
||||
// call even if |controller| is deleted or |StopWatchingFileDescriptor()| is
|
||||
// called, either of which will cause |fdref| to be released.
|
||||
ScopedCFTypeRef<CFFileDescriptorRef> scoped_fdref(
|
||||
fdref, base::scoped_policy::RETAIN);
|
||||
|
||||
int fd = CFFileDescriptorGetNativeDescriptor(fdref);
|
||||
MessagePumpIOSForIO* pump = controller->pump().get();
|
||||
DCHECK(pump);
|
||||
if (callback_types & kCFFileDescriptorWriteCallBack)
|
||||
controller->OnFileCanWriteWithoutBlocking(fd, pump);
|
||||
|
||||
// Perform the read callback only if the file descriptor has not been
|
||||
// invalidated in the write callback. As |FdWatchController| invalidates
|
||||
// its file descriptor on destruction, the file descriptor being valid also
|
||||
// guarantees that |controller| has not been deleted.
|
||||
if (callback_types & kCFFileDescriptorReadCallBack &&
|
||||
CFFileDescriptorIsValid(fdref)) {
|
||||
DCHECK_EQ(fdref, controller->fdref_.get());
|
||||
controller->OnFileCanReadWithoutBlocking(fd, pump);
|
||||
}
|
||||
|
||||
// Re-enable callbacks after the read/write if the file descriptor is still
|
||||
// valid and the controller is persistent.
|
||||
if (CFFileDescriptorIsValid(fdref) && controller->is_persistent_) {
|
||||
DCHECK_EQ(fdref, controller->fdref_.get());
|
||||
CFFileDescriptorEnableCallBacks(fdref, callback_types);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// Copyright 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/mac/scoped_cffiledescriptorref.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/message_pump_mac.h"
|
||||
#include "base/message_loop/watchable_io_message_pump_posix.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// This file introduces a class to monitor sockets and issue callbacks when
|
||||
// sockets are ready for I/O on iOS.
|
||||
class BASE_EXPORT MessagePumpIOSForIO : public MessagePumpNSRunLoop,
|
||||
public WatchableIOMessagePumpPosix {
|
||||
public:
|
||||
class FdWatchController : public FdWatchControllerInterface {
|
||||
public:
|
||||
explicit FdWatchController(const Location& from_here);
|
||||
|
||||
// Implicitly calls StopWatchingFileDescriptor.
|
||||
~FdWatchController() override;
|
||||
|
||||
// FdWatchControllerInterface:
|
||||
bool StopWatchingFileDescriptor() override;
|
||||
|
||||
private:
|
||||
friend class MessagePumpIOSForIO;
|
||||
friend class MessagePumpIOSForIOTest;
|
||||
|
||||
// Called by MessagePumpIOSForIO, ownership of |fdref| and |fd_source|
|
||||
// is transferred to this object.
|
||||
void Init(CFFileDescriptorRef fdref,
|
||||
CFOptionFlags callback_types,
|
||||
CFRunLoopSourceRef fd_source,
|
||||
bool is_persistent);
|
||||
|
||||
void set_pump(base::WeakPtr<MessagePumpIOSForIO> pump) { pump_ = pump; }
|
||||
const base::WeakPtr<MessagePumpIOSForIO>& pump() const { return pump_; }
|
||||
|
||||
void set_watcher(FdWatcher* watcher) { watcher_ = watcher; }
|
||||
|
||||
void OnFileCanReadWithoutBlocking(int fd, MessagePumpIOSForIO* pump);
|
||||
void OnFileCanWriteWithoutBlocking(int fd, MessagePumpIOSForIO* pump);
|
||||
|
||||
bool is_persistent_ = false; // false if this event is one-shot.
|
||||
base::mac::ScopedCFFileDescriptorRef fdref_;
|
||||
CFOptionFlags callback_types_ = 0;
|
||||
base::ScopedCFTypeRef<CFRunLoopSourceRef> fd_source_;
|
||||
base::WeakPtr<MessagePumpIOSForIO> pump_;
|
||||
FdWatcher* watcher_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdWatchController);
|
||||
};
|
||||
|
||||
MessagePumpIOSForIO();
|
||||
~MessagePumpIOSForIO() override;
|
||||
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate);
|
||||
|
||||
void RemoveRunLoopSource(CFRunLoopSourceRef source);
|
||||
|
||||
private:
|
||||
friend class MessagePumpIOSForIOTest;
|
||||
|
||||
static void HandleFdIOEvent(CFFileDescriptorRef fdref,
|
||||
CFOptionFlags callback_types,
|
||||
void* context);
|
||||
|
||||
ThreadChecker watch_file_descriptor_caller_checker_;
|
||||
|
||||
base::WeakPtrFactory<MessagePumpIOSForIO> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpIOSForIO);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_IO_IOS_H_
|
||||
|
|
@ -0,0 +1,456 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_kqueue.h"
|
||||
|
||||
#include <sys/errno.h>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/mach_logging.h"
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
// Prior to macOS 10.12, a kqueue could not watch individual Mach ports, only
|
||||
// port sets. MessagePumpKqueue will directly use Mach ports in the kqueue if
|
||||
// it is possible.
|
||||
bool KqueueNeedsPortSet() {
|
||||
static bool kqueue_needs_port_set = mac::IsAtMostOS10_11();
|
||||
return kqueue_needs_port_set;
|
||||
}
|
||||
|
||||
int ChangeOneEvent(const ScopedFD& kqueue, kevent64_s* event) {
|
||||
return HANDLE_EINTR(kevent64(kqueue.get(), event, 1, nullptr, 0, 0, nullptr));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MessagePumpKqueue::FdWatchController::FdWatchController(
|
||||
const Location& from_here)
|
||||
: FdWatchControllerInterface(from_here) {}
|
||||
|
||||
MessagePumpKqueue::FdWatchController::~FdWatchController() {
|
||||
StopWatchingFileDescriptor();
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::FdWatchController::StopWatchingFileDescriptor() {
|
||||
if (!pump_)
|
||||
return true;
|
||||
return pump_->StopWatchingFileDescriptor(this);
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::FdWatchController::Init(WeakPtr<MessagePumpKqueue> pump,
|
||||
int fd,
|
||||
int mode,
|
||||
FdWatcher* watcher) {
|
||||
DCHECK_NE(fd, -1);
|
||||
DCHECK(!watcher_);
|
||||
DCHECK(watcher);
|
||||
DCHECK(pump);
|
||||
fd_ = fd;
|
||||
mode_ = mode;
|
||||
watcher_ = watcher;
|
||||
pump_ = pump;
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::FdWatchController::Reset() {
|
||||
fd_ = -1;
|
||||
mode_ = 0;
|
||||
watcher_ = nullptr;
|
||||
pump_ = nullptr;
|
||||
}
|
||||
|
||||
MessagePumpKqueue::MachPortWatchController::MachPortWatchController(
|
||||
const Location& from_here)
|
||||
: from_here_(from_here) {}
|
||||
|
||||
MessagePumpKqueue::MachPortWatchController::~MachPortWatchController() {
|
||||
StopWatchingMachPort();
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::MachPortWatchController::StopWatchingMachPort() {
|
||||
if (!pump_)
|
||||
return true;
|
||||
return pump_->StopWatchingMachPort(this);
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::MachPortWatchController::Init(
|
||||
WeakPtr<MessagePumpKqueue> pump,
|
||||
mach_port_t port,
|
||||
MachPortWatcher* watcher) {
|
||||
DCHECK(!watcher_);
|
||||
DCHECK(watcher);
|
||||
DCHECK(pump);
|
||||
port_ = port;
|
||||
watcher_ = watcher;
|
||||
pump_ = pump;
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::MachPortWatchController::Reset() {
|
||||
port_ = MACH_PORT_NULL;
|
||||
watcher_ = nullptr;
|
||||
pump_ = nullptr;
|
||||
}
|
||||
|
||||
MessagePumpKqueue::MessagePumpKqueue()
|
||||
: kqueue_(kqueue()), weak_factory_(this) {
|
||||
PCHECK(kqueue_.is_valid()) << "kqueue";
|
||||
|
||||
// Create a Mach port that will be used to wake up the pump by sending
|
||||
// a message in response to ScheduleWork(). This is significantly faster than
|
||||
// using an EVFILT_USER event, especially when triggered across threads.
|
||||
kern_return_t kr = mach_port_allocate(
|
||||
mach_task_self(), MACH_PORT_RIGHT_RECEIVE,
|
||||
base::mac::ScopedMachReceiveRight::Receiver(wakeup_).get());
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_allocate";
|
||||
|
||||
kevent64_s event{};
|
||||
if (KqueueNeedsPortSet()) {
|
||||
kr = mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_PORT_SET,
|
||||
mac::ScopedMachPortSet::Receiver(port_set_).get());
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_allocate PORT_SET";
|
||||
|
||||
kr = mach_port_insert_member(mach_task_self(), wakeup_.get(),
|
||||
port_set_.get());
|
||||
MACH_CHECK(kr == KERN_SUCCESS, kr) << "mach_port_insert_member";
|
||||
|
||||
event.ident = port_set_.get();
|
||||
event.filter = EVFILT_MACHPORT;
|
||||
event.flags = EV_ADD;
|
||||
} else {
|
||||
// When not using a port set, the wakeup port event can be specified to
|
||||
// directly receive the Mach message as part of the kevent64() syscall.
|
||||
// This is not done when using a port set, since that would potentially
|
||||
// receive client MachPortWatchers' messages.
|
||||
event.ident = wakeup_.get();
|
||||
event.filter = EVFILT_MACHPORT;
|
||||
event.flags = EV_ADD;
|
||||
event.fflags = MACH_RCV_MSG;
|
||||
event.ext[0] = reinterpret_cast<uint64_t>(&wakeup_buffer_);
|
||||
event.ext[1] = sizeof(wakeup_buffer_);
|
||||
}
|
||||
|
||||
int rv = ChangeOneEvent(kqueue_, &event);
|
||||
PCHECK(rv == 0) << "kevent64";
|
||||
}
|
||||
|
||||
MessagePumpKqueue::~MessagePumpKqueue() {}
|
||||
|
||||
void MessagePumpKqueue::Run(Delegate* delegate) {
|
||||
AutoReset<bool> reset_keep_running(&keep_running_, true);
|
||||
|
||||
while (keep_running_) {
|
||||
mac::ScopedNSAutoreleasePool pool;
|
||||
|
||||
bool do_more_work = DoInternalWork(nullptr);
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
|
||||
do_more_work |= next_work_info.is_immediate();
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (do_more_work)
|
||||
continue;
|
||||
|
||||
do_more_work |= delegate->DoIdleWork();
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (do_more_work)
|
||||
continue;
|
||||
|
||||
DoInternalWork(&next_work_info);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::Quit() {
|
||||
keep_running_ = false;
|
||||
ScheduleWork();
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::ScheduleWork() {
|
||||
mach_msg_empty_send_t message{};
|
||||
message.header.msgh_size = sizeof(message);
|
||||
message.header.msgh_bits =
|
||||
MACH_MSGH_BITS_REMOTE(MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
message.header.msgh_remote_port = wakeup_.get();
|
||||
kern_return_t kr = mach_msg_send(&message.header);
|
||||
if (kr != KERN_SUCCESS) {
|
||||
// If ScheduleWork() is being called by other threads faster than the pump
|
||||
// can dispatch work, the kernel message queue for the wakeup port can fill
|
||||
// up (this happens under base_perftests, for example). The kernel does
|
||||
// return a SEND_ONCE right in the case of failure, which must be destroyed
|
||||
// to avoid leaking.
|
||||
MACH_DLOG_IF(ERROR, (kr & ~MACH_MSG_IPC_SPACE) != MACH_SEND_NO_BUFFER, kr)
|
||||
<< "mach_msg_send";
|
||||
mach_msg_destroy(&message.header);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpKqueue::ScheduleDelayedWork(
|
||||
const TimeTicks& delayed_work_time) {
|
||||
// Nothing to do. This MessagePump uses DoSomeWork().
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::WatchMachReceivePort(
|
||||
mach_port_t port,
|
||||
MachPortWatchController* controller,
|
||||
MachPortWatcher* delegate) {
|
||||
DCHECK(port != MACH_PORT_NULL);
|
||||
DCHECK(controller);
|
||||
DCHECK(delegate);
|
||||
|
||||
if (controller->port() != MACH_PORT_NULL) {
|
||||
DLOG(ERROR)
|
||||
<< "Cannot use the same MachPortWatchController while it is active";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (KqueueNeedsPortSet()) {
|
||||
kern_return_t kr =
|
||||
mach_port_insert_member(mach_task_self(), port, port_set_.get());
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_port_insert_member";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
kevent64_s event{};
|
||||
event.ident = port;
|
||||
event.filter = EVFILT_MACHPORT;
|
||||
event.flags = EV_ADD;
|
||||
int rv = ChangeOneEvent(kqueue_, &event);
|
||||
if (rv < 0) {
|
||||
DPLOG(ERROR) << "kevent64";
|
||||
return false;
|
||||
}
|
||||
++event_count_;
|
||||
}
|
||||
|
||||
controller->Init(weak_factory_.GetWeakPtr(), port, delegate);
|
||||
port_controllers_.AddWithID(controller, port);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate) {
|
||||
DCHECK_GE(fd, 0);
|
||||
DCHECK(controller);
|
||||
DCHECK(delegate);
|
||||
DCHECK_NE(mode & Mode::WATCH_READ_WRITE, 0);
|
||||
|
||||
if (controller->fd() != -1 && controller->fd() != fd) {
|
||||
DLOG(ERROR) << "Cannot use the same FdWatchController on two different FDs";
|
||||
return false;
|
||||
}
|
||||
StopWatchingFileDescriptor(controller);
|
||||
|
||||
std::vector<kevent64_s> events;
|
||||
|
||||
kevent64_s base_event{};
|
||||
base_event.ident = fd;
|
||||
base_event.flags = EV_ADD | (!persistent ? EV_ONESHOT : 0);
|
||||
|
||||
if (mode & Mode::WATCH_READ) {
|
||||
base_event.filter = EVFILT_READ;
|
||||
base_event.udata = fd_controllers_.Add(controller);
|
||||
events.push_back(base_event);
|
||||
}
|
||||
if (mode & Mode::WATCH_WRITE) {
|
||||
base_event.filter = EVFILT_WRITE;
|
||||
base_event.udata = fd_controllers_.Add(controller);
|
||||
events.push_back(base_event);
|
||||
}
|
||||
|
||||
int rv = HANDLE_EINTR(kevent64(kqueue_.get(), events.data(), events.size(),
|
||||
nullptr, 0, 0, nullptr));
|
||||
if (rv < 0) {
|
||||
DPLOG(ERROR) << "WatchFileDescriptor kevent64";
|
||||
return false;
|
||||
}
|
||||
|
||||
event_count_ += events.size();
|
||||
controller->Init(weak_factory_.GetWeakPtr(), fd, mode, delegate);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::StopWatchingMachPort(
|
||||
MachPortWatchController* controller) {
|
||||
mach_port_t port = controller->port();
|
||||
controller->Reset();
|
||||
port_controllers_.Remove(port);
|
||||
|
||||
if (KqueueNeedsPortSet()) {
|
||||
kern_return_t kr =
|
||||
mach_port_extract_member(mach_task_self(), port, port_set_.get());
|
||||
if (kr != KERN_SUCCESS) {
|
||||
MACH_LOG(ERROR, kr) << "mach_port_extract_member";
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
kevent64_s event{};
|
||||
event.ident = port;
|
||||
event.filter = EVFILT_MACHPORT;
|
||||
event.flags = EV_DELETE;
|
||||
--event_count_;
|
||||
int rv = ChangeOneEvent(kqueue_, &event);
|
||||
if (rv < 0) {
|
||||
DPLOG(ERROR) << "kevent64";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::StopWatchingFileDescriptor(
|
||||
FdWatchController* controller) {
|
||||
int fd = controller->fd();
|
||||
int mode = controller->mode();
|
||||
controller->Reset();
|
||||
|
||||
if (fd == -1)
|
||||
return true;
|
||||
|
||||
std::vector<kevent64_s> events;
|
||||
|
||||
kevent64_s base_event{};
|
||||
base_event.ident = fd;
|
||||
base_event.flags = EV_DELETE;
|
||||
|
||||
if (mode & Mode::WATCH_READ) {
|
||||
base_event.filter = EVFILT_READ;
|
||||
events.push_back(base_event);
|
||||
}
|
||||
if (mode & Mode::WATCH_WRITE) {
|
||||
base_event.filter = EVFILT_WRITE;
|
||||
events.push_back(base_event);
|
||||
}
|
||||
|
||||
int rv = HANDLE_EINTR(kevent64(kqueue_.get(), events.data(), events.size(),
|
||||
nullptr, 0, 0, nullptr));
|
||||
DPLOG_IF(ERROR, rv < 0) << "StopWatchingFileDescriptor kevent64";
|
||||
|
||||
// The keys for the IDMap aren't recorded anywhere (they're attached to the
|
||||
// kevent object in the kernel), so locate the entries by controller pointer.
|
||||
for (auto it = IDMap<FdWatchController*>::iterator(&fd_controllers_);
|
||||
!it.IsAtEnd(); it.Advance()) {
|
||||
if (it.GetCurrentValue() == controller) {
|
||||
fd_controllers_.Remove(it.GetCurrentKey());
|
||||
}
|
||||
}
|
||||
|
||||
event_count_ -= events.size();
|
||||
|
||||
return rv >= 0;
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::DoInternalWork(Delegate::NextWorkInfo* next_work_info) {
|
||||
if (events_.size() < event_count_) {
|
||||
events_.resize(event_count_);
|
||||
}
|
||||
|
||||
bool poll = next_work_info == nullptr;
|
||||
int flags = poll ? KEVENT_FLAG_IMMEDIATE : 0;
|
||||
bool indefinite =
|
||||
next_work_info != nullptr && next_work_info->delayed_run_time.is_max();
|
||||
|
||||
int rv = 0;
|
||||
do {
|
||||
timespec timeout{};
|
||||
if (!indefinite && !poll) {
|
||||
if (rv != 0) {
|
||||
// The wait was interrupted and made |next_work_info|'s view of
|
||||
// TimeTicks::Now() stale. Refresh it before doing another wait.
|
||||
next_work_info->recent_now = TimeTicks::Now();
|
||||
}
|
||||
timeout = next_work_info->remaining_delay().ToTimeSpec();
|
||||
}
|
||||
// This does not use HANDLE_EINTR, since retrying the syscall requires
|
||||
// adjusting the timeout to account for time already waited.
|
||||
rv = kevent64(kqueue_.get(), nullptr, 0, events_.data(), events_.size(),
|
||||
flags, indefinite ? nullptr : &timeout);
|
||||
} while (rv < 0 && errno == EINTR);
|
||||
|
||||
PCHECK(rv >= 0) << "kevent64";
|
||||
return ProcessEvents(rv);
|
||||
}
|
||||
|
||||
bool MessagePumpKqueue::ProcessEvents(int count) {
|
||||
bool did_work = false;
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
auto* event = &events_[i];
|
||||
if (event->filter == EVFILT_READ || event->filter == EVFILT_WRITE) {
|
||||
did_work = true;
|
||||
|
||||
FdWatchController* controller = fd_controllers_.Lookup(event->udata);
|
||||
if (!controller) {
|
||||
// The controller was removed by some other work callout before
|
||||
// this event could be processed.
|
||||
continue;
|
||||
}
|
||||
FdWatcher* delegate = controller->watcher();
|
||||
|
||||
if (event->flags & EV_ONESHOT) {
|
||||
// If this was a one-shot event, the Controller needs to stop tracking
|
||||
// the descriptor, so it is not double-removed when it is told to stop
|
||||
// watching.
|
||||
controller->Reset();
|
||||
fd_controllers_.Remove(event->udata);
|
||||
--event_count_;
|
||||
}
|
||||
|
||||
if (event->filter == EVFILT_READ) {
|
||||
delegate->OnFileCanReadWithoutBlocking(event->ident);
|
||||
} else if (event->filter == EVFILT_WRITE) {
|
||||
delegate->OnFileCanWriteWithoutBlocking(event->ident);
|
||||
}
|
||||
} else if (event->filter == EVFILT_MACHPORT) {
|
||||
mach_port_t port = KqueueNeedsPortSet() ? event->data : event->ident;
|
||||
|
||||
if (port == wakeup_.get()) {
|
||||
// The wakeup event has been received, do not treat this as "doing
|
||||
// work", this just wakes up the pump.
|
||||
if (KqueueNeedsPortSet()) {
|
||||
// When using the kqueue directly, the message can be received
|
||||
// straight into a buffer that was created when adding the event.
|
||||
// But when using a port set, the message must be drained manually.
|
||||
wakeup_buffer_.header.msgh_local_port = port;
|
||||
wakeup_buffer_.header.msgh_size = sizeof(wakeup_buffer_);
|
||||
kern_return_t kr = mach_msg_receive(&wakeup_buffer_.header);
|
||||
MACH_LOG_IF(ERROR, kr != KERN_SUCCESS, kr)
|
||||
<< "mach_msg_receive wakeup";
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
did_work = true;
|
||||
|
||||
MachPortWatchController* controller = port_controllers_.Lookup(port);
|
||||
// The controller could have been removed by some other work callout
|
||||
// before this event could be processed.
|
||||
if (controller) {
|
||||
controller->watcher()->OnMachMessageReceived(port);
|
||||
}
|
||||
} else {
|
||||
NOTREACHED() << "Unexpected event for filter " << event->filter;
|
||||
}
|
||||
}
|
||||
|
||||
return did_work;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/event.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "base/containers/id_map.h"
|
||||
#include "base/files/scoped_file.h"
|
||||
#include "base/location.h"
|
||||
#include "base/mac/scoped_mach_port.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/message_loop/watchable_io_message_pump_posix.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// MessagePumpKqueue is used on macOS to drive an IO MessageLoop that is
|
||||
// capable of watching both POSIX file descriptors and Mach ports.
|
||||
class BASE_EXPORT MessagePumpKqueue : public MessagePump,
|
||||
public WatchableIOMessagePumpPosix {
|
||||
public:
|
||||
class FdWatchController : public FdWatchControllerInterface {
|
||||
public:
|
||||
explicit FdWatchController(const Location& from_here);
|
||||
~FdWatchController() override;
|
||||
|
||||
// FdWatchControllerInterface:
|
||||
bool StopWatchingFileDescriptor() override;
|
||||
|
||||
protected:
|
||||
friend class MessagePumpKqueue;
|
||||
|
||||
void Init(WeakPtr<MessagePumpKqueue> pump,
|
||||
int fd,
|
||||
int mode,
|
||||
FdWatcher* watcher);
|
||||
void Reset();
|
||||
|
||||
int fd() { return fd_; }
|
||||
int mode() { return mode_; }
|
||||
FdWatcher* watcher() { return watcher_; }
|
||||
|
||||
private:
|
||||
int fd_ = -1;
|
||||
int mode_ = 0;
|
||||
FdWatcher* watcher_ = nullptr;
|
||||
WeakPtr<MessagePumpKqueue> pump_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdWatchController);
|
||||
};
|
||||
|
||||
// Delegate interface that provides notifications of Mach message receive
|
||||
// events.
|
||||
class MachPortWatcher {
|
||||
public:
|
||||
virtual ~MachPortWatcher() {}
|
||||
virtual void OnMachMessageReceived(mach_port_t port) = 0;
|
||||
};
|
||||
|
||||
// Controller interface that is used to stop receiving events for an
|
||||
// installed MachPortWatcher.
|
||||
class MachPortWatchController {
|
||||
public:
|
||||
explicit MachPortWatchController(const Location& from_here);
|
||||
~MachPortWatchController();
|
||||
|
||||
bool StopWatchingMachPort();
|
||||
|
||||
protected:
|
||||
friend class MessagePumpKqueue;
|
||||
|
||||
void Init(WeakPtr<MessagePumpKqueue> pump,
|
||||
mach_port_t port,
|
||||
MachPortWatcher* watcher);
|
||||
void Reset();
|
||||
|
||||
mach_port_t port() { return port_; }
|
||||
MachPortWatcher* watcher() { return watcher_; }
|
||||
|
||||
private:
|
||||
mach_port_t port_ = MACH_PORT_NULL;
|
||||
MachPortWatcher* watcher_ = nullptr;
|
||||
WeakPtr<MessagePumpKqueue> pump_;
|
||||
const Location from_here_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MachPortWatchController);
|
||||
};
|
||||
|
||||
MessagePumpKqueue();
|
||||
~MessagePumpKqueue() override;
|
||||
|
||||
// MessagePump:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
// Begins watching the Mach receive right named by |port|. The |controller|
|
||||
// can be used to stop watching for incoming messages, and new message
|
||||
// notifications are delivered to the |delegate|. Returns true if the watch
|
||||
// was successfully set-up and false on error.
|
||||
bool WatchMachReceivePort(mach_port_t port,
|
||||
MachPortWatchController* controller,
|
||||
MachPortWatcher* delegate);
|
||||
|
||||
// WatchableIOMessagePumpPosix:
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate);
|
||||
|
||||
private:
|
||||
// Called by the watch controller implementations to stop watching the
|
||||
// respective types of handles.
|
||||
bool StopWatchingMachPort(MachPortWatchController* controller);
|
||||
bool StopWatchingFileDescriptor(FdWatchController* controller);
|
||||
|
||||
// Checks the |kqueue_| for events. If |next_work_info| is null, then the
|
||||
// kqueue will be polled for events. If it is non-null, it will wait for the
|
||||
// amount of time specified by the NextWorkInfo or until an event is
|
||||
// triggered. Returns whether any events were dispatched, with the events
|
||||
// stored in |events_|.
|
||||
bool DoInternalWork(Delegate::NextWorkInfo* next_work_info);
|
||||
|
||||
// Called by DoInternalWork() to dispatch the user events stored in |events_|
|
||||
// that were triggered. |count| is the number of events to process. Returns
|
||||
// true if work was done, or false if no work was done.
|
||||
bool ProcessEvents(int count);
|
||||
|
||||
// Receive right to which an empty Mach message is sent to wake up the pump
|
||||
// in response to ScheduleWork().
|
||||
mac::ScopedMachReceiveRight wakeup_;
|
||||
// Scratch buffer that is used to receive the message sent to |wakeup_|.
|
||||
mach_msg_empty_rcv_t wakeup_buffer_;
|
||||
|
||||
// A Mach port set used to watch ports from WatchMachReceivePort(). This is
|
||||
// only used on macOS <10.12, where kqueues cannot watch ports directly.
|
||||
mac::ScopedMachPortSet port_set_;
|
||||
|
||||
// Watch controllers for FDs. IDs are generated by the map and are stored in
|
||||
// the kevent64_s::udata field.
|
||||
IDMap<FdWatchController*> fd_controllers_;
|
||||
|
||||
// Watch controllers for Mach ports. IDs are the port being watched.
|
||||
IDMap<MachPortWatchController*> port_controllers_;
|
||||
|
||||
// The kqueue that drives the pump.
|
||||
ScopedFD kqueue_;
|
||||
|
||||
// Whether the pump has been Quit() or not.
|
||||
bool keep_running_ = true;
|
||||
|
||||
// The number of events scheduled on the |kqueue_|. There is always at least
|
||||
// 1, for the |wakeup_| port (or |port_set_|).
|
||||
size_t event_count_ = 1;
|
||||
// Buffer used by DoInternalWork() to be notified of triggered events. This
|
||||
// is always at least |event_count_|-sized.
|
||||
std::vector<kevent64_s> events_{event_count_};
|
||||
|
||||
WeakPtrFactory<MessagePumpKqueue> weak_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpKqueue);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_KQUEUE_H_
|
||||
|
|
@ -0,0 +1,362 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_libevent.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/posix/eintr_wrapper.h"
|
||||
#include "base/third_party/libevent/event.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
#endif
|
||||
|
||||
// Lifecycle of struct event
|
||||
// Libevent uses two main data structures:
|
||||
// struct event_base (of which there is one per message pump), and
|
||||
// struct event (of which there is roughly one per socket).
|
||||
// The socket's struct event is created in
|
||||
// MessagePumpLibevent::WatchFileDescriptor(),
|
||||
// is owned by the FdWatchController, and is destroyed in
|
||||
// StopWatchingFileDescriptor().
|
||||
// It is moved into and out of lists in struct event_base by
|
||||
// the libevent functions event_add() and event_del().
|
||||
|
||||
namespace base {
|
||||
|
||||
MessagePumpLibevent::FdWatchController::FdWatchController(
|
||||
const Location& from_here)
|
||||
: FdWatchControllerInterface(from_here) {}
|
||||
|
||||
MessagePumpLibevent::FdWatchController::~FdWatchController() {
|
||||
if (event_) {
|
||||
CHECK(StopWatchingFileDescriptor());
|
||||
}
|
||||
if (was_destroyed_) {
|
||||
DCHECK(!*was_destroyed_);
|
||||
*was_destroyed_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool MessagePumpLibevent::FdWatchController::StopWatchingFileDescriptor() {
|
||||
std::unique_ptr<event> e = ReleaseEvent();
|
||||
if (!e)
|
||||
return true;
|
||||
|
||||
// event_del() is a no-op if the event isn't active.
|
||||
int rv = event_del(e.get());
|
||||
pump_ = nullptr;
|
||||
watcher_ = nullptr;
|
||||
return (rv == 0);
|
||||
}
|
||||
|
||||
void MessagePumpLibevent::FdWatchController::Init(std::unique_ptr<event> e) {
|
||||
DCHECK(e);
|
||||
DCHECK(!event_);
|
||||
|
||||
event_ = std::move(e);
|
||||
}
|
||||
|
||||
std::unique_ptr<event> MessagePumpLibevent::FdWatchController::ReleaseEvent() {
|
||||
return std::move(event_);
|
||||
}
|
||||
|
||||
void MessagePumpLibevent::FdWatchController::OnFileCanReadWithoutBlocking(
|
||||
int fd,
|
||||
MessagePumpLibevent* pump) {
|
||||
// Since OnFileCanWriteWithoutBlocking() gets called first, it can stop
|
||||
// watching the file descriptor.
|
||||
if (!watcher_)
|
||||
return;
|
||||
watcher_->OnFileCanReadWithoutBlocking(fd);
|
||||
}
|
||||
|
||||
void MessagePumpLibevent::FdWatchController::OnFileCanWriteWithoutBlocking(
|
||||
int fd,
|
||||
MessagePumpLibevent* pump) {
|
||||
DCHECK(watcher_);
|
||||
watcher_->OnFileCanWriteWithoutBlocking(fd);
|
||||
}
|
||||
|
||||
MessagePumpLibevent::MessagePumpLibevent()
|
||||
: keep_running_(true),
|
||||
in_run_(false),
|
||||
processed_io_events_(false),
|
||||
event_base_(event_base_new()),
|
||||
wakeup_pipe_in_(-1),
|
||||
wakeup_pipe_out_(-1) {
|
||||
if (!Init())
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
MessagePumpLibevent::~MessagePumpLibevent() {
|
||||
DCHECK(wakeup_event_);
|
||||
DCHECK(event_base_);
|
||||
event_del(wakeup_event_);
|
||||
delete wakeup_event_;
|
||||
if (wakeup_pipe_in_ >= 0) {
|
||||
if (IGNORE_EINTR(close(wakeup_pipe_in_)) < 0)
|
||||
DPLOG(ERROR) << "close";
|
||||
}
|
||||
if (wakeup_pipe_out_ >= 0) {
|
||||
if (IGNORE_EINTR(close(wakeup_pipe_out_)) < 0)
|
||||
DPLOG(ERROR) << "close";
|
||||
}
|
||||
event_base_free(event_base_);
|
||||
}
|
||||
|
||||
bool MessagePumpLibevent::WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate) {
|
||||
DCHECK_GE(fd, 0);
|
||||
DCHECK(controller);
|
||||
DCHECK(delegate);
|
||||
DCHECK(mode == WATCH_READ || mode == WATCH_WRITE || mode == WATCH_READ_WRITE);
|
||||
// WatchFileDescriptor should be called on the pump thread. It is not
|
||||
// threadsafe, and your watcher may never be registered.
|
||||
DCHECK(watch_file_descriptor_caller_checker_.CalledOnValidThread());
|
||||
|
||||
TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
|
||||
"MessagePumpLibevent::WatchFileDescriptor",
|
||||
reinterpret_cast<uintptr_t>(controller) ^ fd,
|
||||
TRACE_EVENT_FLAG_FLOW_OUT, "fd", fd);
|
||||
|
||||
int event_mask = persistent ? EV_PERSIST : 0;
|
||||
if (mode & WATCH_READ) {
|
||||
event_mask |= EV_READ;
|
||||
}
|
||||
if (mode & WATCH_WRITE) {
|
||||
event_mask |= EV_WRITE;
|
||||
}
|
||||
|
||||
std::unique_ptr<event> evt(controller->ReleaseEvent());
|
||||
if (!evt) {
|
||||
// Ownership is transferred to the controller.
|
||||
evt.reset(new event);
|
||||
} else {
|
||||
// Make sure we don't pick up any funky internal libevent masks.
|
||||
int old_interest_mask = evt->ev_events & (EV_READ | EV_WRITE | EV_PERSIST);
|
||||
|
||||
// Combine old/new event masks.
|
||||
event_mask |= old_interest_mask;
|
||||
|
||||
// Must disarm the event before we can reuse it.
|
||||
event_del(evt.get());
|
||||
|
||||
// It's illegal to use this function to listen on 2 separate fds with the
|
||||
// same |controller|.
|
||||
if (EVENT_FD(evt.get()) != fd) {
|
||||
NOTREACHED() << "FDs don't match" << EVENT_FD(evt.get()) << "!=" << fd;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Set current interest mask and message pump for this event.
|
||||
event_set(evt.get(), fd, event_mask, OnLibeventNotification, controller);
|
||||
|
||||
// Tell libevent which message pump this socket will belong to when we add it.
|
||||
if (event_base_set(event_base_, evt.get())) {
|
||||
DPLOG(ERROR) << "event_base_set(fd=" << EVENT_FD(evt.get()) << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add this socket to the list of monitored sockets.
|
||||
if (event_add(evt.get(), nullptr)) {
|
||||
DPLOG(ERROR) << "event_add failed(fd=" << EVENT_FD(evt.get()) << ")";
|
||||
return false;
|
||||
}
|
||||
|
||||
controller->Init(std::move(evt));
|
||||
controller->set_watcher(delegate);
|
||||
controller->set_pump(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Tell libevent to break out of inner loop.
|
||||
static void timer_callback(int fd, short events, void* context) {
|
||||
event_base_loopbreak((struct event_base*)context);
|
||||
}
|
||||
|
||||
// Reentrant!
|
||||
void MessagePumpLibevent::Run(Delegate* delegate) {
|
||||
AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
|
||||
AutoReset<bool> auto_reset_in_run(&in_run_, true);
|
||||
|
||||
// event_base_loopexit() + EVLOOP_ONCE is leaky, see http://crbug.com/25641.
|
||||
// Instead, make our own timer and reuse it on each call to event_base_loop().
|
||||
std::unique_ptr<event> timer_event(new event);
|
||||
|
||||
for (;;) {
|
||||
#if defined(OS_MACOSX)
|
||||
mac::ScopedNSAutoreleasePool autorelease_pool;
|
||||
#endif
|
||||
// Do some work and see if the next task is ready right away.
|
||||
Delegate::NextWorkInfo next_work_info = delegate->DoSomeWork();
|
||||
bool immediate_work_available = next_work_info.is_immediate();
|
||||
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
// Process native events if any are ready. Do not block waiting for more.
|
||||
delegate->BeforeDoInternalWork();
|
||||
event_base_loop(event_base_, EVLOOP_NONBLOCK);
|
||||
|
||||
bool attempt_more_work = immediate_work_available || processed_io_events_;
|
||||
processed_io_events_ = false;
|
||||
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (attempt_more_work)
|
||||
continue;
|
||||
|
||||
attempt_more_work = delegate->DoIdleWork();
|
||||
|
||||
if (!keep_running_)
|
||||
break;
|
||||
|
||||
if (attempt_more_work)
|
||||
continue;
|
||||
|
||||
bool did_set_timer = false;
|
||||
|
||||
// If there is delayed work.
|
||||
DCHECK(!next_work_info.delayed_run_time.is_null());
|
||||
if (!next_work_info.delayed_run_time.is_max()) {
|
||||
const TimeDelta delay = next_work_info.remaining_delay();
|
||||
|
||||
// Setup a timer to break out of the event loop at the right time.
|
||||
struct timeval poll_tv;
|
||||
poll_tv.tv_sec = delay.InSeconds();
|
||||
poll_tv.tv_usec = delay.InMicroseconds() % Time::kMicrosecondsPerSecond;
|
||||
event_set(timer_event.get(), -1, 0, timer_callback, event_base_);
|
||||
event_base_set(event_base_, timer_event.get());
|
||||
event_add(timer_event.get(), &poll_tv);
|
||||
|
||||
did_set_timer = true;
|
||||
}
|
||||
|
||||
// Block waiting for events and process all available upon waking up. This
|
||||
// is conditionally interrupted to look for more work if we are aware of a
|
||||
// delayed task that will need servicing.
|
||||
delegate->BeforeWait();
|
||||
event_base_loop(event_base_, EVLOOP_ONCE);
|
||||
|
||||
// We previously setup a timer to break out the event loop to look for more
|
||||
// work. Now that we're here delete the event.
|
||||
if (did_set_timer) {
|
||||
event_del(timer_event.get());
|
||||
}
|
||||
|
||||
if (!keep_running_)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpLibevent::Quit() {
|
||||
DCHECK(in_run_) << "Quit was called outside of Run!";
|
||||
// Tell both libevent and Run that they should break out of their loops.
|
||||
keep_running_ = false;
|
||||
ScheduleWork();
|
||||
}
|
||||
|
||||
void MessagePumpLibevent::ScheduleWork() {
|
||||
// Tell libevent (in a threadsafe way) that it should break out of its loop.
|
||||
char buf = 0;
|
||||
int nwrite = HANDLE_EINTR(write(wakeup_pipe_in_, &buf, 1));
|
||||
DPCHECK(nwrite == 1 || errno == EAGAIN) << "nwrite:" << nwrite;
|
||||
}
|
||||
|
||||
void MessagePumpLibevent::ScheduleDelayedWork(
|
||||
const TimeTicks& delayed_work_time) {
|
||||
// We know that we can't be blocked on Run()'s |timer_event| right now since
|
||||
// this method can only be called on the same thread as Run(). Hence we have
|
||||
// nothing to do here, this thread will sleep in Run() with the correct
|
||||
// timeout when it's out of immediate tasks.
|
||||
}
|
||||
|
||||
bool MessagePumpLibevent::Init() {
|
||||
int fds[2];
|
||||
if (!CreateLocalNonBlockingPipe(fds)) {
|
||||
DPLOG(ERROR) << "pipe creation failed";
|
||||
return false;
|
||||
}
|
||||
wakeup_pipe_out_ = fds[0];
|
||||
wakeup_pipe_in_ = fds[1];
|
||||
|
||||
wakeup_event_ = new event;
|
||||
event_set(wakeup_event_, wakeup_pipe_out_, EV_READ | EV_PERSIST,
|
||||
OnWakeup, this);
|
||||
event_base_set(event_base_, wakeup_event_);
|
||||
|
||||
if (event_add(wakeup_event_, nullptr))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// static
|
||||
void MessagePumpLibevent::OnLibeventNotification(int fd,
|
||||
short flags,
|
||||
void* context) {
|
||||
FdWatchController* controller = static_cast<FdWatchController*>(context);
|
||||
DCHECK(controller);
|
||||
TRACE_EVENT0("toplevel", "OnLibevent");
|
||||
TRACE_EVENT_WITH_FLOW1(TRACE_DISABLED_BY_DEFAULT("toplevel.flow"),
|
||||
"MessagePumpLibevent::OnLibeventNotification",
|
||||
reinterpret_cast<uintptr_t>(controller) ^ fd,
|
||||
TRACE_EVENT_FLAG_FLOW_IN | TRACE_EVENT_FLAG_FLOW_OUT,
|
||||
"fd", fd);
|
||||
|
||||
TRACE_HEAP_PROFILER_API_SCOPED_TASK_EXECUTION heap_profiler_scope(
|
||||
controller->created_from_location().file_name());
|
||||
|
||||
MessagePumpLibevent* pump = controller->pump();
|
||||
pump->processed_io_events_ = true;
|
||||
|
||||
if ((flags & (EV_READ | EV_WRITE)) == (EV_READ | EV_WRITE)) {
|
||||
// Both callbacks will be called. It is necessary to check that |controller|
|
||||
// is not destroyed.
|
||||
bool controller_was_destroyed = false;
|
||||
controller->was_destroyed_ = &controller_was_destroyed;
|
||||
controller->OnFileCanWriteWithoutBlocking(fd, pump);
|
||||
if (!controller_was_destroyed)
|
||||
controller->OnFileCanReadWithoutBlocking(fd, pump);
|
||||
if (!controller_was_destroyed)
|
||||
controller->was_destroyed_ = nullptr;
|
||||
} else if (flags & EV_WRITE) {
|
||||
controller->OnFileCanWriteWithoutBlocking(fd, pump);
|
||||
} else if (flags & EV_READ) {
|
||||
controller->OnFileCanReadWithoutBlocking(fd, pump);
|
||||
}
|
||||
}
|
||||
|
||||
// Called if a byte is received on the wakeup pipe.
|
||||
// static
|
||||
void MessagePumpLibevent::OnWakeup(int socket, short flags, void* context) {
|
||||
MessagePumpLibevent* that = static_cast<MessagePumpLibevent*>(context);
|
||||
DCHECK(that->wakeup_pipe_out_ == socket);
|
||||
|
||||
// Remove and discard the wakeup byte.
|
||||
char buf;
|
||||
int nread = HANDLE_EINTR(read(socket, &buf, 1));
|
||||
DCHECK_EQ(nread, 1);
|
||||
that->processed_io_events_ = true;
|
||||
// Tell libevent to break out of inner loop.
|
||||
event_base_loopbreak(that->event_base_);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/compiler_specific.h"
|
||||
#include "base/macros.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/message_loop/watchable_io_message_pump_posix.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
|
||||
// Declare structs we need from libevent.h rather than including it
|
||||
struct event_base;
|
||||
struct event;
|
||||
|
||||
namespace base {
|
||||
|
||||
// Class to monitor sockets and issue callbacks when sockets are ready for I/O
|
||||
// TODO(dkegel): add support for background file IO somehow
|
||||
class BASE_EXPORT MessagePumpLibevent : public MessagePump,
|
||||
public WatchableIOMessagePumpPosix {
|
||||
public:
|
||||
class FdWatchController : public FdWatchControllerInterface {
|
||||
public:
|
||||
explicit FdWatchController(const Location& from_here);
|
||||
|
||||
// Implicitly calls StopWatchingFileDescriptor.
|
||||
~FdWatchController() override;
|
||||
|
||||
// FdWatchControllerInterface:
|
||||
bool StopWatchingFileDescriptor() override;
|
||||
|
||||
private:
|
||||
friend class MessagePumpLibevent;
|
||||
friend class MessagePumpLibeventTest;
|
||||
|
||||
// Called by MessagePumpLibevent.
|
||||
void Init(std::unique_ptr<event> e);
|
||||
|
||||
// Used by MessagePumpLibevent to take ownership of |event_|.
|
||||
std::unique_ptr<event> ReleaseEvent();
|
||||
|
||||
void set_pump(MessagePumpLibevent* pump) { pump_ = pump; }
|
||||
MessagePumpLibevent* pump() const { return pump_; }
|
||||
|
||||
void set_watcher(FdWatcher* watcher) { watcher_ = watcher; }
|
||||
|
||||
void OnFileCanReadWithoutBlocking(int fd, MessagePumpLibevent* pump);
|
||||
void OnFileCanWriteWithoutBlocking(int fd, MessagePumpLibevent* pump);
|
||||
|
||||
std::unique_ptr<event> event_;
|
||||
MessagePumpLibevent* pump_ = nullptr;
|
||||
FdWatcher* watcher_ = nullptr;
|
||||
// If this pointer is non-NULL, the pointee is set to true in the
|
||||
// destructor.
|
||||
bool* was_destroyed_ = nullptr;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdWatchController);
|
||||
};
|
||||
|
||||
MessagePumpLibevent();
|
||||
~MessagePumpLibevent() override;
|
||||
|
||||
bool WatchFileDescriptor(int fd,
|
||||
bool persistent,
|
||||
int mode,
|
||||
FdWatchController* controller,
|
||||
FdWatcher* delegate);
|
||||
|
||||
// MessagePump methods:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
private:
|
||||
friend class MessagePumpLibeventTest;
|
||||
|
||||
// Risky part of constructor. Returns true on success.
|
||||
bool Init();
|
||||
|
||||
// Called by libevent to tell us a registered FD can be read/written to.
|
||||
static void OnLibeventNotification(int fd, short flags, void* context);
|
||||
|
||||
// Unix pipe used to implement ScheduleWork()
|
||||
// ... callback; called by libevent inside Run() when pipe is ready to read
|
||||
static void OnWakeup(int socket, short flags, void* context);
|
||||
|
||||
// This flag is set to false when Run should return.
|
||||
bool keep_running_;
|
||||
|
||||
// This flag is set when inside Run.
|
||||
bool in_run_;
|
||||
|
||||
// This flag is set if libevent has processed I/O events.
|
||||
bool processed_io_events_;
|
||||
|
||||
// Libevent dispatcher. Watches all sockets registered with it, and sends
|
||||
// readiness callbacks when a socket is ready for I/O.
|
||||
event_base* event_base_;
|
||||
|
||||
// ... write end; ScheduleWork() writes a single byte to it
|
||||
int wakeup_pipe_in_;
|
||||
// ... read end; OnWakeup reads it and then breaks Run() out of its sleep
|
||||
int wakeup_pipe_out_;
|
||||
// ... libevent wrapper for read end
|
||||
event* wakeup_event_;
|
||||
|
||||
ThreadChecker watch_file_descriptor_caller_checker_;
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpLibevent);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_LIBEVENT_H_
|
||||
|
|
@ -0,0 +1,445 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// The basis for all native run loops on the Mac is the CFRunLoop. It can be
|
||||
// used directly, it can be used as the driving force behind the similar
|
||||
// Foundation NSRunLoop, and it can be used to implement higher-level event
|
||||
// loops such as the NSApplication event loop.
|
||||
//
|
||||
// This file introduces a basic CFRunLoop-based implementation of the
|
||||
// MessagePump interface called CFRunLoopBase. CFRunLoopBase contains all
|
||||
// of the machinery necessary to dispatch events to a delegate, but does not
|
||||
// implement the specific run loop. Concrete subclasses must provide their
|
||||
// own DoRun and DoQuit implementations.
|
||||
//
|
||||
// A concrete subclass that just runs a CFRunLoop loop is provided in
|
||||
// MessagePumpCFRunLoop. For an NSRunLoop, the similar MessagePumpNSRunLoop
|
||||
// is provided.
|
||||
//
|
||||
// For the application's event loop, an implementation based on AppKit's
|
||||
// NSApplication event system is provided in MessagePumpNSApplication.
|
||||
//
|
||||
// Typically, MessagePumpNSApplication only makes sense on a Cocoa
|
||||
// application's main thread. If a CFRunLoop-based message pump is needed on
|
||||
// any other thread, one of the other concrete subclasses is preferable.
|
||||
// MessagePumpMac::Create is defined, which returns a new NSApplication-based
|
||||
// or NSRunLoop-based MessagePump subclass depending on which thread it is
|
||||
// called on.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
|
||||
|
||||
#include "base/message_loop/message_pump.h"
|
||||
|
||||
|
||||
#include <CoreFoundation/CoreFoundation.h>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/message_loop/timer_slack.h"
|
||||
#include "base/optional.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if defined(__OBJC__)
|
||||
#if defined(OS_IOS)
|
||||
#import <Foundation/Foundation.h>
|
||||
#else
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
// Clients must subclass NSApplication and implement this protocol if they use
|
||||
// MessagePumpMac.
|
||||
@protocol CrAppProtocol
|
||||
// Must return true if -[NSApplication sendEvent:] is currently on the stack.
|
||||
// See the comment for |CreateAutoreleasePool()| in the cc file for why this is
|
||||
// necessary.
|
||||
- (BOOL)isHandlingSendEvent;
|
||||
@end
|
||||
#endif // !defined(OS_IOS)
|
||||
#endif // defined(__OBJC__)
|
||||
|
||||
namespace base {
|
||||
|
||||
class RunLoop;
|
||||
class TimeTicks;
|
||||
|
||||
// AutoreleasePoolType is a proxy type for autorelease pools. Its definition
|
||||
// depends on the translation unit (TU) in which this header appears. In pure
|
||||
// C++ TUs, it is defined as a forward C++ class declaration (that is never
|
||||
// defined), because autorelease pools are an Objective-C concept. In Automatic
|
||||
// Reference Counting (ARC) Objective-C TUs, it is similarly defined as a
|
||||
// forward C++ class declaration, because clang will not allow the type
|
||||
// "NSAutoreleasePool" in such TUs. Finally, in Manual Retain Release (MRR)
|
||||
// Objective-C TUs, it is a type alias for NSAutoreleasePool. In all cases, a
|
||||
// method that takes or returns an NSAutoreleasePool* can use
|
||||
// AutoreleasePoolType* instead.
|
||||
#if !defined(__OBJC__) || __has_feature(objc_arc)
|
||||
class AutoreleasePoolType;
|
||||
#else // !defined(__OBJC__) || __has_feature(objc_arc)
|
||||
typedef NSAutoreleasePool AutoreleasePoolType;
|
||||
#endif // !defined(__OBJC__) || __has_feature(objc_arc)
|
||||
|
||||
class BASE_EXPORT MessagePumpCFRunLoopBase : public MessagePump {
|
||||
public:
|
||||
// MessagePump:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
void SetTimerSlack(TimerSlack timer_slack) override;
|
||||
|
||||
#if defined(OS_IOS)
|
||||
// Some iOS message pumps do not support calling |Run()| to spin the main
|
||||
// message loop directly. Instead, call |Attach()| to set up a delegate, then
|
||||
// |Detach()| before destroying the message pump. These methods do nothing if
|
||||
// the message pump supports calling |Run()| and |Quit()|.
|
||||
virtual void Attach(Delegate* delegate);
|
||||
virtual void Detach();
|
||||
#endif // OS_IOS
|
||||
|
||||
protected:
|
||||
// Needs access to CreateAutoreleasePool.
|
||||
friend class MessagePumpScopedAutoreleasePool;
|
||||
friend class TestMessagePumpCFRunLoopBase;
|
||||
|
||||
// Tasks will be pumped in the run loop modes described by
|
||||
// |initial_mode_mask|, which maps bits to the index of an internal array of
|
||||
// run loop mode identifiers.
|
||||
explicit MessagePumpCFRunLoopBase(int initial_mode_mask);
|
||||
~MessagePumpCFRunLoopBase() override;
|
||||
|
||||
// Subclasses should implement the work they need to do in MessagePump::Run
|
||||
// in the DoRun method. MessagePumpCFRunLoopBase::Run calls DoRun directly.
|
||||
// This arrangement is used because MessagePumpCFRunLoopBase needs to set
|
||||
// up and tear down things before and after the "meat" of DoRun.
|
||||
virtual void DoRun(Delegate* delegate) = 0;
|
||||
|
||||
// Similar to DoRun, this allows subclasses to perform custom handling when
|
||||
// quitting a run loop. Return true if the quit took effect immediately;
|
||||
// otherwise call OnDidQuit() when the quit is actually applied (e.g., a
|
||||
// nested native runloop exited).
|
||||
virtual bool DoQuit() = 0;
|
||||
|
||||
// Should be called by subclasses to signal when a deferred quit takes place.
|
||||
void OnDidQuit();
|
||||
|
||||
// Accessors for private data members to be used by subclasses.
|
||||
CFRunLoopRef run_loop() const { return run_loop_; }
|
||||
int nesting_level() const { return nesting_level_; }
|
||||
int run_nesting_level() const { return run_nesting_level_; }
|
||||
bool keep_running() const { return keep_running_; }
|
||||
|
||||
// Sets this pump's delegate. Signals the appropriate sources if
|
||||
// |delegateless_work_| is true. |delegate| can be NULL.
|
||||
void SetDelegate(Delegate* delegate);
|
||||
|
||||
// Return an autorelease pool to wrap around any work being performed.
|
||||
// In some cases, CreateAutoreleasePool may return nil intentionally to
|
||||
// preventing an autorelease pool from being created, allowing any
|
||||
// objects autoreleased by work to fall into the current autorelease pool.
|
||||
virtual AutoreleasePoolType* CreateAutoreleasePool();
|
||||
|
||||
// Enable and disable entries in |enabled_modes_| to match |mode_mask|.
|
||||
void SetModeMask(int mode_mask);
|
||||
|
||||
// Get the current mode mask from |enabled_modes_|.
|
||||
int GetModeMask() const;
|
||||
|
||||
// Controls whether the timer invalidation performance optimization is
|
||||
// allowed.
|
||||
void SetTimerInvalidationAllowed(bool allowed);
|
||||
|
||||
private:
|
||||
class ScopedModeEnabler;
|
||||
|
||||
// The maximum number of run loop modes that can be monitored.
|
||||
static constexpr int kNumModes = 4;
|
||||
|
||||
// All sources of delayed work scheduling converge to this, using TimeDelta
|
||||
// avoids querying Now() for key callers.
|
||||
void ScheduleDelayedWorkImpl(TimeDelta delta);
|
||||
|
||||
// Marking timers as invalid at the right time helps significantly reduce
|
||||
// power use (see the comment in RunDelayedWorkTimer()), however there is no
|
||||
// public API for doing so. CFRuntime.h states that CFRuntimeBase, upon which
|
||||
// the above timer invalidation functions are based, can change from release
|
||||
// to release and should not be accessed directly (this struct last changed at
|
||||
// least in 2008 in CF-476).
|
||||
//
|
||||
// This function uses private API to modify a test timer's valid state and
|
||||
// uses public API to confirm that the private API changed the right bit.
|
||||
static bool CanInvalidateCFRunLoopTimers();
|
||||
|
||||
// Sets a Core Foundation object's "invalid" bit to |valid|. Based on code
|
||||
// from CFRunLoop.c.
|
||||
static void ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid);
|
||||
|
||||
// Controls the validity of the delayed work timer. Does nothing if timer
|
||||
// invalidation is disallowed.
|
||||
void SetDelayedWorkTimerValid(bool valid);
|
||||
|
||||
// Timer callback scheduled by ScheduleDelayedWork. This does not do any
|
||||
// work, but it signals |work_source_| so that delayed work can be performed
|
||||
// within the appropriate priority constraints.
|
||||
static void RunDelayedWorkTimer(CFRunLoopTimerRef timer, void* info);
|
||||
|
||||
// Perform highest-priority work. This is associated with |work_source_|
|
||||
// signalled by ScheduleWork or RunDelayedWorkTimer. The static method calls
|
||||
// the instance method; the instance method returns true if it resignalled
|
||||
// |work_source_| to be called again from the loop.
|
||||
static void RunWorkSource(void* info);
|
||||
bool RunWork();
|
||||
|
||||
// Perform idle-priority work. This is normally called by PreWaitObserver,
|
||||
// but is also associated with |idle_work_source_|. When this function
|
||||
// actually does perform idle work, it will resignal that source. The
|
||||
// static method calls the instance method.
|
||||
static void RunIdleWorkSource(void* info);
|
||||
void RunIdleWork();
|
||||
|
||||
// Perform work that may have been deferred because it was not runnable
|
||||
// within a nested run loop. This is associated with
|
||||
// |nesting_deferred_work_source_| and is signalled by
|
||||
// MaybeScheduleNestingDeferredWork when returning from a nested loop,
|
||||
// so that an outer loop will be able to perform the necessary tasks if it
|
||||
// permits nestable tasks.
|
||||
static void RunNestingDeferredWorkSource(void* info);
|
||||
void RunNestingDeferredWork();
|
||||
|
||||
// Schedules possible nesting-deferred work to be processed before the run
|
||||
// loop goes to sleep, exits, or begins processing sources at the top of its
|
||||
// loop. If this function detects that a nested loop had run since the
|
||||
// previous attempt to schedule nesting-deferred work, it will schedule a
|
||||
// call to RunNestingDeferredWorkSource.
|
||||
void MaybeScheduleNestingDeferredWork();
|
||||
|
||||
// Observer callback responsible for performing idle-priority work, before
|
||||
// the run loop goes to sleep. Associated with |pre_wait_observer_|.
|
||||
static void PreWaitObserver(CFRunLoopObserverRef observer,
|
||||
CFRunLoopActivity activity, void* info);
|
||||
|
||||
// Observer callback called before the run loop processes any sources.
|
||||
// Associated with |pre_source_observer_|.
|
||||
static void PreSourceObserver(CFRunLoopObserverRef observer,
|
||||
CFRunLoopActivity activity, void* info);
|
||||
|
||||
// Observer callback called when the run loop starts and stops, at the
|
||||
// beginning and end of calls to CFRunLoopRun. This is used to maintain
|
||||
// |nesting_level_|. Associated with |enter_exit_observer_|.
|
||||
static void EnterExitObserver(CFRunLoopObserverRef observer,
|
||||
CFRunLoopActivity activity, void* info);
|
||||
|
||||
// Called by EnterExitObserver after performing maintenance on
|
||||
// |nesting_level_|. This allows subclasses an opportunity to perform
|
||||
// additional processing on the basis of run loops starting and stopping.
|
||||
virtual void EnterExitRunLoop(CFRunLoopActivity activity);
|
||||
|
||||
// The thread's run loop.
|
||||
CFRunLoopRef run_loop_;
|
||||
|
||||
// The enabled modes. Posted tasks may run in any non-null entry.
|
||||
std::unique_ptr<ScopedModeEnabler> enabled_modes_[kNumModes];
|
||||
|
||||
// The timer, sources, and observers are described above alongside their
|
||||
// callbacks.
|
||||
CFRunLoopTimerRef delayed_work_timer_;
|
||||
CFRunLoopSourceRef work_source_;
|
||||
CFRunLoopSourceRef idle_work_source_;
|
||||
CFRunLoopSourceRef nesting_deferred_work_source_;
|
||||
CFRunLoopObserverRef pre_wait_observer_;
|
||||
CFRunLoopObserverRef pre_source_observer_;
|
||||
CFRunLoopObserverRef enter_exit_observer_;
|
||||
|
||||
// (weak) Delegate passed as an argument to the innermost Run call.
|
||||
Delegate* delegate_;
|
||||
|
||||
base::TimerSlack timer_slack_;
|
||||
|
||||
// The recursion depth of the currently-executing CFRunLoopRun loop on the
|
||||
// run loop's thread. 0 if no run loops are running inside of whatever scope
|
||||
// the object was created in.
|
||||
int nesting_level_;
|
||||
|
||||
// The recursion depth (calculated in the same way as |nesting_level_|) of the
|
||||
// innermost executing CFRunLoopRun loop started by a call to Run.
|
||||
int run_nesting_level_;
|
||||
|
||||
// The deepest (numerically highest) recursion depth encountered since the
|
||||
// most recent attempt to run nesting-deferred work.
|
||||
int deepest_nesting_level_;
|
||||
|
||||
// Whether we should continue running application tasks. Set to false when
|
||||
// Quit() is called for the innermost run loop.
|
||||
bool keep_running_;
|
||||
|
||||
// "Delegateless" work flags are set when work is ready to be performed but
|
||||
// must wait until a delegate is available to process it. This can happen
|
||||
// when a MessagePumpCFRunLoopBase is instantiated and work arrives without
|
||||
// any call to Run on the stack. The Run method will check for delegateless
|
||||
// work on entry and redispatch it as needed once a delegate is available.
|
||||
bool delegateless_work_;
|
||||
bool delegateless_idle_work_;
|
||||
|
||||
// Whether or not timer invalidation can be used in order to reduce the number
|
||||
// of reschedulings.
|
||||
bool allow_timer_invalidation_;
|
||||
|
||||
// If changing timer validitity was attempted while it was disallowed, this
|
||||
// value tracks the desired state of the timer.
|
||||
Optional<bool> pending_timer_validity_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoopBase);
|
||||
};
|
||||
|
||||
class BASE_EXPORT MessagePumpCFRunLoop : public MessagePumpCFRunLoopBase {
|
||||
public:
|
||||
MessagePumpCFRunLoop();
|
||||
~MessagePumpCFRunLoop() override;
|
||||
|
||||
void DoRun(Delegate* delegate) override;
|
||||
bool DoQuit() override;
|
||||
|
||||
private:
|
||||
void EnterExitRunLoop(CFRunLoopActivity activity) override;
|
||||
|
||||
// True if Quit is called to stop the innermost MessagePump
|
||||
// (|innermost_quittable_|) but some other CFRunLoopRun loop
|
||||
// (|nesting_level_|) is running inside the MessagePump's innermost Run call.
|
||||
bool quit_pending_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpCFRunLoop);
|
||||
};
|
||||
|
||||
class BASE_EXPORT MessagePumpNSRunLoop : public MessagePumpCFRunLoopBase {
|
||||
public:
|
||||
MessagePumpNSRunLoop();
|
||||
~MessagePumpNSRunLoop() override;
|
||||
|
||||
void DoRun(Delegate* delegate) override;
|
||||
bool DoQuit() override;
|
||||
|
||||
private:
|
||||
// A source that doesn't do anything but provide something signalable
|
||||
// attached to the run loop. This source will be signalled when Quit
|
||||
// is called, to cause the loop to wake up so that it can stop.
|
||||
CFRunLoopSourceRef quit_source_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpNSRunLoop);
|
||||
};
|
||||
|
||||
#if defined(OS_IOS)
|
||||
// This is a fake message pump. It attaches sources to the main thread's
|
||||
// CFRunLoop, so PostTask() will work, but it is unable to drive the loop
|
||||
// directly, so calling Run() or Quit() are errors.
|
||||
class MessagePumpUIApplication : public MessagePumpCFRunLoopBase {
|
||||
public:
|
||||
MessagePumpUIApplication();
|
||||
~MessagePumpUIApplication() override;
|
||||
void DoRun(Delegate* delegate) override;
|
||||
bool DoQuit() override;
|
||||
|
||||
// MessagePumpCFRunLoopBase.
|
||||
// MessagePumpUIApplication can not spin the main message loop directly.
|
||||
// Instead, call |Attach()| to set up a delegate. It is an error to call
|
||||
// |Run()|.
|
||||
void Attach(Delegate* delegate) override;
|
||||
void Detach() override;
|
||||
|
||||
private:
|
||||
RunLoop* run_loop_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpUIApplication);
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
// While in scope, permits posted tasks to be run in private AppKit run loop
|
||||
// modes that would otherwise make the UI unresponsive. E.g., menu fade out.
|
||||
class BASE_EXPORT ScopedPumpMessagesInPrivateModes {
|
||||
public:
|
||||
ScopedPumpMessagesInPrivateModes();
|
||||
~ScopedPumpMessagesInPrivateModes();
|
||||
|
||||
int GetModeMaskForTest();
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedPumpMessagesInPrivateModes);
|
||||
};
|
||||
|
||||
class MessagePumpNSApplication : public MessagePumpCFRunLoopBase {
|
||||
public:
|
||||
MessagePumpNSApplication();
|
||||
~MessagePumpNSApplication() override;
|
||||
|
||||
void DoRun(Delegate* delegate) override;
|
||||
bool DoQuit() override;
|
||||
|
||||
private:
|
||||
friend class ScopedPumpMessagesInPrivateModes;
|
||||
|
||||
void EnterExitRunLoop(CFRunLoopActivity activity) override;
|
||||
|
||||
// True if DoRun is managing its own run loop as opposed to letting
|
||||
// -[NSApplication run] handle it. The outermost run loop in the application
|
||||
// is managed by -[NSApplication run], inner run loops are handled by a loop
|
||||
// in DoRun.
|
||||
bool running_own_loop_;
|
||||
|
||||
// True if Quit() was called while a modal window was shown and needed to be
|
||||
// deferred.
|
||||
bool quit_pending_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpNSApplication);
|
||||
};
|
||||
|
||||
class MessagePumpCrApplication : public MessagePumpNSApplication {
|
||||
public:
|
||||
MessagePumpCrApplication();
|
||||
~MessagePumpCrApplication() override;
|
||||
|
||||
protected:
|
||||
// Returns nil if NSApp is currently in the middle of calling
|
||||
// -sendEvent. Requires NSApp implementing CrAppProtocol.
|
||||
AutoreleasePoolType* CreateAutoreleasePool() override;
|
||||
|
||||
private:
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpCrApplication);
|
||||
};
|
||||
#endif // !defined(OS_IOS)
|
||||
|
||||
class BASE_EXPORT MessagePumpMac {
|
||||
public:
|
||||
// If not on the main thread, returns a new instance of
|
||||
// MessagePumpNSRunLoop.
|
||||
//
|
||||
// On the main thread, if NSApp exists and conforms to
|
||||
// CrAppProtocol, creates an instances of MessagePumpCrApplication.
|
||||
//
|
||||
// Otherwise creates an instance of MessagePumpNSApplication using a
|
||||
// default NSApplication.
|
||||
static std::unique_ptr<MessagePump> Create();
|
||||
|
||||
#if !defined(OS_IOS)
|
||||
// If a pump is created before the required CrAppProtocol is
|
||||
// created, the wrong MessagePump subclass could be used.
|
||||
// UsingCrApp() returns false if the message pump was created before
|
||||
// NSApp was initialized, or if NSApp does not implement
|
||||
// CrAppProtocol. NSApp must be initialized before calling.
|
||||
static bool UsingCrApp();
|
||||
|
||||
// Wrapper to query -[NSApp isHandlingSendEvent] from C++ code.
|
||||
// Requires NSApp to implement CrAppProtocol.
|
||||
static bool IsHandlingSendEvent();
|
||||
#endif // !defined(OS_IOS)
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(MessagePumpMac);
|
||||
};
|
||||
|
||||
// Tasks posted to the message loop are posted under this mode, as well
|
||||
// as kCFRunLoopCommonModes.
|
||||
extern const CFStringRef BASE_EXPORT kMessageLoopExclusiveRunLoopMode;
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_MAC_H_
|
||||
|
|
@ -0,0 +1,984 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#import "base/message_loop/message_pump_mac.h"
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
|
||||
#include "base/auto_reset.h"
|
||||
#include "base/feature_list.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/mac/call_with_eh_frame.h"
|
||||
#include "base/mac/scoped_cftyperef.h"
|
||||
#include "base/message_loop/timer_slack.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "base/time/time.h"
|
||||
#include "build/build_config.h"
|
||||
|
||||
#if !defined(OS_IOS)
|
||||
#import <AppKit/AppKit.h>
|
||||
#endif // !defined(OS_IOS)
|
||||
|
||||
namespace base {
|
||||
|
||||
const CFStringRef kMessageLoopExclusiveRunLoopMode =
|
||||
CFSTR("kMessageLoopExclusiveRunLoopMode");
|
||||
|
||||
namespace {
|
||||
|
||||
// Mask that determines which modes to use.
|
||||
enum { kCommonModeMask = 0x1, kAllModesMask = 0xf };
|
||||
|
||||
// Modes to use for MessagePumpNSApplication that are considered "safe".
|
||||
// Currently just common and exclusive modes. Ideally, messages would be pumped
|
||||
// in all modes, but that interacts badly with app modal dialogs (e.g. NSAlert).
|
||||
enum { kNSApplicationModalSafeModeMask = 0x3 };
|
||||
|
||||
void NoOp(void* info) {
|
||||
}
|
||||
|
||||
constexpr CFTimeInterval kCFTimeIntervalMax =
|
||||
std::numeric_limits<CFTimeInterval>::max();
|
||||
|
||||
#if !defined(OS_IOS)
|
||||
// Set to true if MessagePumpMac::Create() is called before NSApp is
|
||||
// initialized. Only accessed from the main thread.
|
||||
bool g_not_using_cr_app = false;
|
||||
|
||||
// The MessagePump controlling [NSApp run].
|
||||
MessagePumpNSApplication* g_app_pump;
|
||||
|
||||
Feature kMessagePumpTimerInvalidation{"MessagePumpMacTimerInvalidation",
|
||||
FEATURE_ENABLED_BY_DEFAULT};
|
||||
|
||||
// Various CoreFoundation definitions.
|
||||
typedef struct __CFRuntimeBase {
|
||||
uintptr_t _cfisa;
|
||||
uint8_t _cfinfo[4];
|
||||
uint32_t _rc;
|
||||
} CFRuntimeBase;
|
||||
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
#define __CF_BIG_ENDIAN__ 1
|
||||
#define __CF_LITTLE_ENDIAN__ 0
|
||||
#endif
|
||||
|
||||
#if defined(__LITTLE_ENDIAN__)
|
||||
#define __CF_LITTLE_ENDIAN__ 1
|
||||
#define __CF_BIG_ENDIAN__ 0
|
||||
#endif
|
||||
|
||||
#define CF_INFO_BITS (!!(__CF_BIG_ENDIAN__)*3)
|
||||
|
||||
#define __CFBitfieldMask(N1, N2) \
|
||||
((((UInt32)~0UL) << (31UL - (N1) + (N2))) >> (31UL - N1))
|
||||
#define __CFBitfieldSetValue(V, N1, N2, X) \
|
||||
((V) = ((V) & ~__CFBitfieldMask(N1, N2)) | \
|
||||
(((X) << (N2)) & __CFBitfieldMask(N1, N2)))
|
||||
|
||||
// Marking timers as invalid at the right time by flipping their valid bit helps
|
||||
// significantly reduce power use (see the explanation in
|
||||
// RunDelayedWorkTimer()), however there is no public API for doing so.
|
||||
// CFRuntime.h states that CFRuntimeBase can change from release to release
|
||||
// and should not be accessed directly. The last known change of this struct
|
||||
// occurred in 2008 in CF-476 / 10.5; unfortunately the source for 10.11 and
|
||||
// 10.12 is not available for inspection at this time.
|
||||
// CanInvalidateCFRunLoopTimers() will at least prevent us from invalidating
|
||||
// timers if this function starts flipping the wrong bit on a future OS release.
|
||||
void __ChromeCFRunLoopTimerSetValid(CFRunLoopTimerRef timer, bool valid) {
|
||||
__CFBitfieldSetValue(((CFRuntimeBase*)timer)->_cfinfo[CF_INFO_BITS], 3, 3,
|
||||
valid);
|
||||
}
|
||||
#endif // !defined(OS_IOS)
|
||||
|
||||
} // namespace
|
||||
|
||||
// A scoper for autorelease pools created from message pump run loops.
|
||||
// Avoids dirtying up the ScopedNSAutoreleasePool interface for the rare
|
||||
// case where an autorelease pool needs to be passed in.
|
||||
class MessagePumpScopedAutoreleasePool {
|
||||
public:
|
||||
explicit MessagePumpScopedAutoreleasePool(MessagePumpCFRunLoopBase* pump) :
|
||||
pool_(pump->CreateAutoreleasePool()) {
|
||||
}
|
||||
~MessagePumpScopedAutoreleasePool() {
|
||||
[pool_ drain];
|
||||
}
|
||||
|
||||
private:
|
||||
NSAutoreleasePool* pool_;
|
||||
DISALLOW_COPY_AND_ASSIGN(MessagePumpScopedAutoreleasePool);
|
||||
};
|
||||
|
||||
class MessagePumpCFRunLoopBase::ScopedModeEnabler {
|
||||
public:
|
||||
ScopedModeEnabler(MessagePumpCFRunLoopBase* owner, int mode_index)
|
||||
: owner_(owner), mode_index_(mode_index) {
|
||||
CFRunLoopRef loop = owner_->run_loop_;
|
||||
CFRunLoopAddTimer(loop, owner_->delayed_work_timer_, mode());
|
||||
CFRunLoopAddSource(loop, owner_->work_source_, mode());
|
||||
CFRunLoopAddSource(loop, owner_->idle_work_source_, mode());
|
||||
CFRunLoopAddSource(loop, owner_->nesting_deferred_work_source_, mode());
|
||||
CFRunLoopAddObserver(loop, owner_->pre_wait_observer_, mode());
|
||||
CFRunLoopAddObserver(loop, owner_->pre_source_observer_, mode());
|
||||
CFRunLoopAddObserver(loop, owner_->enter_exit_observer_, mode());
|
||||
}
|
||||
|
||||
~ScopedModeEnabler() {
|
||||
CFRunLoopRef loop = owner_->run_loop_;
|
||||
CFRunLoopRemoveObserver(loop, owner_->enter_exit_observer_, mode());
|
||||
CFRunLoopRemoveObserver(loop, owner_->pre_source_observer_, mode());
|
||||
CFRunLoopRemoveObserver(loop, owner_->pre_wait_observer_, mode());
|
||||
CFRunLoopRemoveSource(loop, owner_->nesting_deferred_work_source_, mode());
|
||||
CFRunLoopRemoveSource(loop, owner_->idle_work_source_, mode());
|
||||
CFRunLoopRemoveSource(loop, owner_->work_source_, mode());
|
||||
CFRunLoopRemoveTimer(loop, owner_->delayed_work_timer_, mode());
|
||||
}
|
||||
|
||||
// This function knows about the AppKit RunLoop modes observed to potentially
|
||||
// run tasks posted to Chrome's main thread task runner. Some are internal to
|
||||
// AppKit but must be observed to keep Chrome's UI responsive. Others that may
|
||||
// be interesting, but are not watched:
|
||||
// - com.apple.hitoolbox.windows.transitionmode
|
||||
// - com.apple.hitoolbox.windows.flushmode
|
||||
const CFStringRef& mode() const {
|
||||
static const CFStringRef modes[] = {
|
||||
// The standard Core Foundation "common modes" constant. Must always be
|
||||
// first in this list to match the value of kCommonModeMask.
|
||||
kCFRunLoopCommonModes,
|
||||
|
||||
// Mode that only sees Chrome work sources.
|
||||
kMessageLoopExclusiveRunLoopMode,
|
||||
|
||||
// Process work when NSMenus are fading out.
|
||||
CFSTR("com.apple.hitoolbox.windows.windowfadingmode"),
|
||||
|
||||
// Process work when AppKit is highlighting an item on the main menubar.
|
||||
CFSTR("NSUnhighlightMenuRunLoopMode"),
|
||||
};
|
||||
static_assert(base::size(modes) == kNumModes, "mode size mismatch");
|
||||
static_assert((1 << kNumModes) - 1 == kAllModesMask,
|
||||
"kAllModesMask not large enough");
|
||||
|
||||
return modes[mode_index_];
|
||||
}
|
||||
|
||||
private:
|
||||
MessagePumpCFRunLoopBase* const owner_; // Weak. Owns this.
|
||||
const int mode_index_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ScopedModeEnabler);
|
||||
};
|
||||
|
||||
// Must be called on the run loop thread.
|
||||
void MessagePumpCFRunLoopBase::Run(Delegate* delegate) {
|
||||
AutoReset<bool> auto_reset_keep_running(&keep_running_, true);
|
||||
// nesting_level_ will be incremented in EnterExitRunLoop, so set
|
||||
// run_nesting_level_ accordingly.
|
||||
int last_run_nesting_level = run_nesting_level_;
|
||||
run_nesting_level_ = nesting_level_ + 1;
|
||||
|
||||
Delegate* last_delegate = delegate_;
|
||||
SetDelegate(delegate);
|
||||
|
||||
ScheduleWork();
|
||||
DoRun(delegate);
|
||||
|
||||
// Restore the previous state of the object.
|
||||
SetDelegate(last_delegate);
|
||||
run_nesting_level_ = last_run_nesting_level;
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::Quit() {
|
||||
if (DoQuit())
|
||||
OnDidQuit();
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::OnDidQuit() {
|
||||
keep_running_ = false;
|
||||
}
|
||||
|
||||
// May be called on any thread.
|
||||
void MessagePumpCFRunLoopBase::ScheduleWork() {
|
||||
CFRunLoopSourceSignal(work_source_);
|
||||
CFRunLoopWakeUp(run_loop_);
|
||||
}
|
||||
|
||||
// Must be called on the run loop thread.
|
||||
void MessagePumpCFRunLoopBase::ScheduleDelayedWork(
|
||||
const TimeTicks& delayed_work_time) {
|
||||
ScheduleDelayedWorkImpl(delayed_work_time - TimeTicks::Now());
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::ScheduleDelayedWorkImpl(TimeDelta delta) {
|
||||
// Flip the timer's validation bit just before setting the new fire time. Do
|
||||
// this now because CFRunLoopTimerSetNextFireDate() likely checks the validity
|
||||
// of a timer before proceeding to set its fire date. Making the timer valid
|
||||
// now won't have any side effects (such as a premature firing of the timer)
|
||||
// because we're only flipping a bit.
|
||||
//
|
||||
// Please see the comment in RunDelayedWorkTimer() for more info on the whys
|
||||
// of invalidation.
|
||||
SetDelayedWorkTimerValid(true);
|
||||
|
||||
// The tolerance needs to be set before the fire date or it may be ignored.
|
||||
if (timer_slack_ == TIMER_SLACK_MAXIMUM) {
|
||||
CFRunLoopTimerSetTolerance(delayed_work_timer_, delta.InSecondsF() * 0.5);
|
||||
} else {
|
||||
CFRunLoopTimerSetTolerance(delayed_work_timer_, 0);
|
||||
}
|
||||
CFRunLoopTimerSetNextFireDate(
|
||||
delayed_work_timer_, CFAbsoluteTimeGetCurrent() + delta.InSecondsF());
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::SetTimerSlack(TimerSlack timer_slack) {
|
||||
timer_slack_ = timer_slack;
|
||||
}
|
||||
|
||||
#if defined(OS_IOS)
|
||||
void MessagePumpCFRunLoopBase::Attach(Delegate* delegate) {}
|
||||
|
||||
void MessagePumpCFRunLoopBase::Detach() {}
|
||||
#endif // OS_IOS
|
||||
|
||||
// Must be called on the run loop thread.
|
||||
MessagePumpCFRunLoopBase::MessagePumpCFRunLoopBase(int initial_mode_mask)
|
||||
: delegate_(NULL),
|
||||
timer_slack_(base::TIMER_SLACK_NONE),
|
||||
nesting_level_(0),
|
||||
run_nesting_level_(0),
|
||||
deepest_nesting_level_(0),
|
||||
keep_running_(true),
|
||||
delegateless_work_(false),
|
||||
delegateless_idle_work_(false),
|
||||
allow_timer_invalidation_(true) {
|
||||
run_loop_ = CFRunLoopGetCurrent();
|
||||
CFRetain(run_loop_);
|
||||
|
||||
// Set a repeating timer with a preposterous firing time and interval. The
|
||||
// timer will effectively never fire as-is. The firing time will be adjusted
|
||||
// as needed when ScheduleDelayedWork is called.
|
||||
CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
|
||||
timer_context.info = this;
|
||||
delayed_work_timer_ = CFRunLoopTimerCreate(NULL, // allocator
|
||||
kCFTimeIntervalMax, // fire time
|
||||
kCFTimeIntervalMax, // interval
|
||||
0, // flags
|
||||
0, // priority
|
||||
RunDelayedWorkTimer,
|
||||
&timer_context);
|
||||
|
||||
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
|
||||
source_context.info = this;
|
||||
source_context.perform = RunWorkSource;
|
||||
work_source_ = CFRunLoopSourceCreate(NULL, // allocator
|
||||
1, // priority
|
||||
&source_context);
|
||||
source_context.perform = RunIdleWorkSource;
|
||||
idle_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
|
||||
2, // priority
|
||||
&source_context);
|
||||
source_context.perform = RunNestingDeferredWorkSource;
|
||||
nesting_deferred_work_source_ = CFRunLoopSourceCreate(NULL, // allocator
|
||||
0, // priority
|
||||
&source_context);
|
||||
|
||||
CFRunLoopObserverContext observer_context = CFRunLoopObserverContext();
|
||||
observer_context.info = this;
|
||||
pre_wait_observer_ = CFRunLoopObserverCreate(NULL, // allocator
|
||||
kCFRunLoopBeforeWaiting,
|
||||
true, // repeat
|
||||
0, // priority
|
||||
PreWaitObserver,
|
||||
&observer_context);
|
||||
pre_source_observer_ = CFRunLoopObserverCreate(NULL, // allocator
|
||||
kCFRunLoopBeforeSources,
|
||||
true, // repeat
|
||||
0, // priority
|
||||
PreSourceObserver,
|
||||
&observer_context);
|
||||
enter_exit_observer_ = CFRunLoopObserverCreate(NULL, // allocator
|
||||
kCFRunLoopEntry |
|
||||
kCFRunLoopExit,
|
||||
true, // repeat
|
||||
0, // priority
|
||||
EnterExitObserver,
|
||||
&observer_context);
|
||||
SetModeMask(initial_mode_mask);
|
||||
}
|
||||
|
||||
// Ideally called on the run loop thread. If other run loops were running
|
||||
// lower on the run loop thread's stack when this object was created, the
|
||||
// same number of run loops must be running when this object is destroyed.
|
||||
MessagePumpCFRunLoopBase::~MessagePumpCFRunLoopBase() {
|
||||
SetModeMask(0);
|
||||
CFRelease(enter_exit_observer_);
|
||||
CFRelease(pre_source_observer_);
|
||||
CFRelease(pre_wait_observer_);
|
||||
CFRelease(nesting_deferred_work_source_);
|
||||
CFRelease(idle_work_source_);
|
||||
CFRelease(work_source_);
|
||||
CFRelease(delayed_work_timer_);
|
||||
CFRelease(run_loop_);
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::SetDelegate(Delegate* delegate) {
|
||||
delegate_ = delegate;
|
||||
|
||||
if (delegate) {
|
||||
// If any work showed up but could not be dispatched for want of a
|
||||
// delegate, set it up for dispatch again now that a delegate is
|
||||
// available.
|
||||
if (delegateless_work_) {
|
||||
CFRunLoopSourceSignal(work_source_);
|
||||
delegateless_work_ = false;
|
||||
}
|
||||
if (delegateless_idle_work_) {
|
||||
CFRunLoopSourceSignal(idle_work_source_);
|
||||
delegateless_idle_work_ = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Base version returns a standard NSAutoreleasePool.
|
||||
AutoreleasePoolType* MessagePumpCFRunLoopBase::CreateAutoreleasePool() {
|
||||
return [[NSAutoreleasePool alloc] init];
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::SetModeMask(int mode_mask) {
|
||||
for (size_t i = 0; i < kNumModes; ++i) {
|
||||
bool enable = mode_mask & (0x1 << i);
|
||||
if (enable == !enabled_modes_[i]) {
|
||||
enabled_modes_[i] =
|
||||
enable ? std::make_unique<ScopedModeEnabler>(this, i) : nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int MessagePumpCFRunLoopBase::GetModeMask() const {
|
||||
int mask = 0;
|
||||
for (size_t i = 0; i < kNumModes; ++i)
|
||||
mask |= enabled_modes_[i] ? (0x1 << i) : 0;
|
||||
return mask;
|
||||
}
|
||||
|
||||
#if !defined(OS_IOS)
|
||||
// This function uses private API to modify a test timer's valid state and
|
||||
// uses public API to confirm that the private API changed the correct bit.
|
||||
// static
|
||||
bool MessagePumpCFRunLoopBase::CanInvalidateCFRunLoopTimers() {
|
||||
if (!FeatureList::IsEnabled(kMessagePumpTimerInvalidation)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CFRunLoopTimerContext timer_context = CFRunLoopTimerContext();
|
||||
timer_context.info = nullptr;
|
||||
ScopedCFTypeRef<CFRunLoopTimerRef> test_timer(
|
||||
CFRunLoopTimerCreate(NULL, // allocator
|
||||
kCFTimeIntervalMax, // fire time
|
||||
kCFTimeIntervalMax, // interval
|
||||
0, // flags
|
||||
0, // priority
|
||||
nullptr, &timer_context));
|
||||
// Should be valid from the start.
|
||||
if (!CFRunLoopTimerIsValid(test_timer)) {
|
||||
return false;
|
||||
}
|
||||
// Confirm that the private API can mark the timer invalid.
|
||||
__ChromeCFRunLoopTimerSetValid(test_timer, false);
|
||||
if (CFRunLoopTimerIsValid(test_timer)) {
|
||||
return false;
|
||||
}
|
||||
// Confirm that the private API can mark the timer valid.
|
||||
__ChromeCFRunLoopTimerSetValid(test_timer, true);
|
||||
return CFRunLoopTimerIsValid(test_timer);
|
||||
}
|
||||
#endif // !defined(OS_IOS)
|
||||
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::ChromeCFRunLoopTimerSetValid(
|
||||
CFRunLoopTimerRef timer,
|
||||
bool valid) {
|
||||
#if !defined(OS_IOS)
|
||||
static bool can_invalidate_timers = CanInvalidateCFRunLoopTimers();
|
||||
if (can_invalidate_timers) {
|
||||
__ChromeCFRunLoopTimerSetValid(timer, valid);
|
||||
}
|
||||
#endif // !defined(OS_IOS)
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::SetDelayedWorkTimerValid(bool valid) {
|
||||
if (allow_timer_invalidation_) {
|
||||
ChromeCFRunLoopTimerSetValid(delayed_work_timer_, valid);
|
||||
} else {
|
||||
pending_timer_validity_ = valid;
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpCFRunLoopBase::SetTimerInvalidationAllowed(bool allowed) {
|
||||
if (!allowed)
|
||||
ChromeCFRunLoopTimerSetValid(delayed_work_timer_, true);
|
||||
allow_timer_invalidation_ = allowed;
|
||||
if (allowed && pending_timer_validity_.has_value()) {
|
||||
SetDelayedWorkTimerValid(*pending_timer_validity_);
|
||||
pending_timer_validity_ = nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::RunDelayedWorkTimer(CFRunLoopTimerRef timer,
|
||||
void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
|
||||
// The message pump's timer needs to fire at changing and unpredictable
|
||||
// intervals. Creating a new timer for each firing time is very expensive, so
|
||||
// the message pump instead uses a repeating timer with a very large repeat
|
||||
// rate. After each firing of the timer, the run loop sets the timer's next
|
||||
// firing time to the distant future, essentially pausing the timer until the
|
||||
// pump sets the next firing time. This is the solution recommended by Apple.
|
||||
//
|
||||
// It turns out, however, that scheduling timers is also quite expensive, and
|
||||
// that every one of the message pump's timer firings incurs two
|
||||
// reschedulings. The first rescheduling occurs in ScheduleDelayedWork(),
|
||||
// which sets the desired next firing time. The second comes after exiting
|
||||
// this method (the timer's callback method), when the run loop sets the
|
||||
// timer's next firing time to far in the future.
|
||||
//
|
||||
// The code in __CFRunLoopDoTimer() inside CFRunLoop.c calls the timer's
|
||||
// callback, confirms that the timer is valid, and then sets its future
|
||||
// firing time based on its repeat frequency. Flipping the valid bit here
|
||||
// causes the __CFRunLoopDoTimer() to skip setting the future firing time.
|
||||
// Note that there's public API to invalidate a timer but it goes beyond
|
||||
// flipping the valid bit, making the timer unusable in the future.
|
||||
//
|
||||
// ScheduleDelayedWork() flips the valid bit back just before setting the
|
||||
// timer's new firing time.
|
||||
self->SetDelayedWorkTimerValid(false);
|
||||
|
||||
// The timer fired, assume we have work and let RunWork() figure out what to
|
||||
// do and what to schedule after.
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->RunWork();
|
||||
});
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::RunWorkSource(void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->RunWork();
|
||||
});
|
||||
}
|
||||
|
||||
// Called by MessagePumpCFRunLoopBase::RunWorkSource and RunDelayedWorkTimer.
|
||||
bool MessagePumpCFRunLoopBase::RunWork() {
|
||||
if (!delegate_) {
|
||||
// This point can be reached with a nullptr |delegate_| if Run is not on the
|
||||
// stack but foreign code is spinning the CFRunLoop. Arrange to come back
|
||||
// here when a delegate is available.
|
||||
delegateless_work_ = true;
|
||||
return false;
|
||||
}
|
||||
if (!keep_running())
|
||||
return false;
|
||||
|
||||
// The NSApplication-based run loop only drains the autorelease pool at each
|
||||
// UI event (NSEvent). The autorelease pool is not drained for each
|
||||
// CFRunLoopSource target that's run. Use a local pool for any autoreleased
|
||||
// objects if the app is not currently handling a UI event to ensure they're
|
||||
// released promptly even in the absence of UI events.
|
||||
MessagePumpScopedAutoreleasePool autorelease_pool(this);
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = delegate_->DoSomeWork();
|
||||
|
||||
if (next_work_info.is_immediate()) {
|
||||
CFRunLoopSourceSignal(work_source_);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!next_work_info.delayed_run_time.is_max())
|
||||
ScheduleDelayedWorkImpl(next_work_info.remaining_delay());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::RunIdleWorkSource(void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->RunIdleWork();
|
||||
});
|
||||
}
|
||||
|
||||
// Called by MessagePumpCFRunLoopBase::RunIdleWorkSource.
|
||||
void MessagePumpCFRunLoopBase::RunIdleWork() {
|
||||
if (!delegate_) {
|
||||
// This point can be reached with a nullptr delegate_ if Run is not on the
|
||||
// stack but foreign code is spinning the CFRunLoop. Arrange to come back
|
||||
// here when a delegate is available.
|
||||
delegateless_idle_work_ = true;
|
||||
return;
|
||||
}
|
||||
if (!keep_running())
|
||||
return;
|
||||
// The NSApplication-based run loop only drains the autorelease pool at each
|
||||
// UI event (NSEvent). The autorelease pool is not drained for each
|
||||
// CFRunLoopSource target that's run. Use a local pool for any autoreleased
|
||||
// objects if the app is not currently handling a UI event to ensure they're
|
||||
// released promptly even in the absence of UI events.
|
||||
MessagePumpScopedAutoreleasePool autorelease_pool(this);
|
||||
// Call DoIdleWork once, and if something was done, arrange to come back here
|
||||
// again as long as the loop is still running.
|
||||
bool did_work = delegate_->DoIdleWork();
|
||||
if (did_work)
|
||||
CFRunLoopSourceSignal(idle_work_source_);
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource(void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->RunNestingDeferredWork();
|
||||
});
|
||||
}
|
||||
|
||||
// Called by MessagePumpCFRunLoopBase::RunNestingDeferredWorkSource.
|
||||
void MessagePumpCFRunLoopBase::RunNestingDeferredWork() {
|
||||
if (!delegate_) {
|
||||
// This point can be reached with a nullptr |delegate_| if Run is not on the
|
||||
// stack but foreign code is spinning the CFRunLoop. There's no sense in
|
||||
// attempting to do any work or signalling the work sources because
|
||||
// without a delegate, work is not possible.
|
||||
return;
|
||||
}
|
||||
|
||||
if (RunWork()) {
|
||||
// Work was done. Arrange for the loop to try non-nestable idle work on
|
||||
// a subsequent pass.
|
||||
CFRunLoopSourceSignal(idle_work_source_);
|
||||
} else {
|
||||
RunIdleWork();
|
||||
}
|
||||
}
|
||||
|
||||
// Called before the run loop goes to sleep or exits, or processes sources.
|
||||
void MessagePumpCFRunLoopBase::MaybeScheduleNestingDeferredWork() {
|
||||
// deepest_nesting_level_ is set as run loops are entered. If the deepest
|
||||
// level encountered is deeper than the current level, a nested loop
|
||||
// (relative to the current level) ran since the last time nesting-deferred
|
||||
// work was scheduled. When that situation is encountered, schedule
|
||||
// nesting-deferred work in case any work was deferred because nested work
|
||||
// was disallowed.
|
||||
if (deepest_nesting_level_ > nesting_level_) {
|
||||
deepest_nesting_level_ = nesting_level_;
|
||||
CFRunLoopSourceSignal(nesting_deferred_work_source_);
|
||||
}
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::PreWaitObserver(CFRunLoopObserverRef observer,
|
||||
CFRunLoopActivity activity,
|
||||
void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
base::mac::CallWithEHFrame(^{
|
||||
// Attempt to do some idle work before going to sleep.
|
||||
self->RunIdleWork();
|
||||
|
||||
// The run loop is about to go to sleep. If any of the work done since it
|
||||
// started or woke up resulted in a nested run loop running,
|
||||
// nesting-deferred work may have accumulated. Schedule it for processing
|
||||
// if appropriate.
|
||||
self->MaybeScheduleNestingDeferredWork();
|
||||
});
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::PreSourceObserver(CFRunLoopObserverRef observer,
|
||||
CFRunLoopActivity activity,
|
||||
void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
|
||||
// The run loop has reached the top of the loop and is about to begin
|
||||
// processing sources. If the last iteration of the loop at this nesting
|
||||
// level did not sleep or exit, nesting-deferred work may have accumulated
|
||||
// if a nested loop ran. Schedule nesting-deferred work for processing if
|
||||
// appropriate.
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->MaybeScheduleNestingDeferredWork();
|
||||
});
|
||||
}
|
||||
|
||||
// Called from the run loop.
|
||||
// static
|
||||
void MessagePumpCFRunLoopBase::EnterExitObserver(CFRunLoopObserverRef observer,
|
||||
CFRunLoopActivity activity,
|
||||
void* info) {
|
||||
MessagePumpCFRunLoopBase* self = static_cast<MessagePumpCFRunLoopBase*>(info);
|
||||
|
||||
switch (activity) {
|
||||
case kCFRunLoopEntry:
|
||||
++self->nesting_level_;
|
||||
if (self->nesting_level_ > self->deepest_nesting_level_) {
|
||||
self->deepest_nesting_level_ = self->nesting_level_;
|
||||
}
|
||||
break;
|
||||
|
||||
case kCFRunLoopExit:
|
||||
// Not all run loops go to sleep. If a run loop is stopped before it
|
||||
// goes to sleep due to a CFRunLoopStop call, or if the timeout passed
|
||||
// to CFRunLoopRunInMode expires, the run loop may proceed directly from
|
||||
// handling sources to exiting without any sleep. This most commonly
|
||||
// occurs when CFRunLoopRunInMode is passed a timeout of 0, causing it
|
||||
// to make a single pass through the loop and exit without sleep. Some
|
||||
// native loops use CFRunLoop in this way. Because PreWaitObserver will
|
||||
// not be called in these case, MaybeScheduleNestingDeferredWork needs
|
||||
// to be called here, as the run loop exits.
|
||||
//
|
||||
// MaybeScheduleNestingDeferredWork consults self->nesting_level_
|
||||
// to determine whether to schedule nesting-deferred work. It expects
|
||||
// the nesting level to be set to the depth of the loop that is going
|
||||
// to sleep or exiting. It must be called before decrementing the
|
||||
// value so that the value still corresponds to the level of the exiting
|
||||
// loop.
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->MaybeScheduleNestingDeferredWork();
|
||||
});
|
||||
--self->nesting_level_;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
base::mac::CallWithEHFrame(^{
|
||||
self->EnterExitRunLoop(activity);
|
||||
});
|
||||
}
|
||||
|
||||
// Called by MessagePumpCFRunLoopBase::EnterExitRunLoop. The default
|
||||
// implementation is a no-op.
|
||||
void MessagePumpCFRunLoopBase::EnterExitRunLoop(CFRunLoopActivity activity) {
|
||||
}
|
||||
|
||||
MessagePumpCFRunLoop::MessagePumpCFRunLoop()
|
||||
: MessagePumpCFRunLoopBase(kCommonModeMask), quit_pending_(false) {}
|
||||
|
||||
MessagePumpCFRunLoop::~MessagePumpCFRunLoop() {}
|
||||
|
||||
// Called by MessagePumpCFRunLoopBase::DoRun. If other CFRunLoopRun loops were
|
||||
// running lower on the run loop thread's stack when this object was created,
|
||||
// the same number of CFRunLoopRun loops must be running for the outermost call
|
||||
// to Run. Run/DoRun are reentrant after that point.
|
||||
void MessagePumpCFRunLoop::DoRun(Delegate* delegate) {
|
||||
// This is completely identical to calling CFRunLoopRun(), except autorelease
|
||||
// pool management is introduced.
|
||||
int result;
|
||||
do {
|
||||
MessagePumpScopedAutoreleasePool autorelease_pool(this);
|
||||
result = CFRunLoopRunInMode(kCFRunLoopDefaultMode,
|
||||
kCFTimeIntervalMax,
|
||||
false);
|
||||
} while (result != kCFRunLoopRunStopped && result != kCFRunLoopRunFinished);
|
||||
}
|
||||
|
||||
// Must be called on the run loop thread.
|
||||
bool MessagePumpCFRunLoop::DoQuit() {
|
||||
// Stop the innermost run loop managed by this MessagePumpCFRunLoop object.
|
||||
if (nesting_level() == run_nesting_level()) {
|
||||
// This object is running the innermost loop, just stop it.
|
||||
CFRunLoopStop(run_loop());
|
||||
return true;
|
||||
} else {
|
||||
// There's another loop running inside the loop managed by this object.
|
||||
// In other words, someone else called CFRunLoopRunInMode on the same
|
||||
// thread, deeper on the stack than the deepest Run call. Don't preempt
|
||||
// other run loops, just mark this object to quit the innermost Run as
|
||||
// soon as the other inner loops not managed by Run are done.
|
||||
quit_pending_ = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Called by MessagePumpCFRunLoopBase::EnterExitObserver.
|
||||
void MessagePumpCFRunLoop::EnterExitRunLoop(CFRunLoopActivity activity) {
|
||||
if (activity == kCFRunLoopExit &&
|
||||
nesting_level() == run_nesting_level() &&
|
||||
quit_pending_) {
|
||||
// Quit was called while loops other than those managed by this object
|
||||
// were running further inside a run loop managed by this object. Now
|
||||
// that all unmanaged inner run loops are gone, stop the loop running
|
||||
// just inside Run.
|
||||
CFRunLoopStop(run_loop());
|
||||
quit_pending_ = false;
|
||||
OnDidQuit();
|
||||
}
|
||||
}
|
||||
|
||||
MessagePumpNSRunLoop::MessagePumpNSRunLoop()
|
||||
: MessagePumpCFRunLoopBase(kCommonModeMask) {
|
||||
CFRunLoopSourceContext source_context = CFRunLoopSourceContext();
|
||||
source_context.perform = NoOp;
|
||||
quit_source_ = CFRunLoopSourceCreate(NULL, // allocator
|
||||
0, // priority
|
||||
&source_context);
|
||||
CFRunLoopAddSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
|
||||
}
|
||||
|
||||
MessagePumpNSRunLoop::~MessagePumpNSRunLoop() {
|
||||
CFRunLoopRemoveSource(run_loop(), quit_source_, kCFRunLoopCommonModes);
|
||||
CFRelease(quit_source_);
|
||||
}
|
||||
|
||||
void MessagePumpNSRunLoop::DoRun(Delegate* delegate) {
|
||||
while (keep_running()) {
|
||||
// NSRunLoop manages autorelease pools itself.
|
||||
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
|
||||
beforeDate:[NSDate distantFuture]];
|
||||
}
|
||||
}
|
||||
|
||||
bool MessagePumpNSRunLoop::DoQuit() {
|
||||
CFRunLoopSourceSignal(quit_source_);
|
||||
CFRunLoopWakeUp(run_loop());
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(OS_IOS)
|
||||
MessagePumpUIApplication::MessagePumpUIApplication()
|
||||
: MessagePumpCFRunLoopBase(kCommonModeMask), run_loop_(NULL) {}
|
||||
|
||||
MessagePumpUIApplication::~MessagePumpUIApplication() {}
|
||||
|
||||
void MessagePumpUIApplication::DoRun(Delegate* delegate) {
|
||||
NOTREACHED();
|
||||
}
|
||||
|
||||
bool MessagePumpUIApplication::DoQuit() {
|
||||
NOTREACHED();
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessagePumpUIApplication::Attach(Delegate* delegate) {
|
||||
DCHECK(!run_loop_);
|
||||
run_loop_ = new RunLoop();
|
||||
CHECK(run_loop_->BeforeRun());
|
||||
SetDelegate(delegate);
|
||||
}
|
||||
|
||||
void MessagePumpUIApplication::Detach() {
|
||||
DCHECK(run_loop_);
|
||||
run_loop_->AfterRun();
|
||||
SetDelegate(nullptr);
|
||||
run_loop_ = nullptr;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
ScopedPumpMessagesInPrivateModes::ScopedPumpMessagesInPrivateModes() {
|
||||
DCHECK(g_app_pump);
|
||||
DCHECK_EQ(kNSApplicationModalSafeModeMask, g_app_pump->GetModeMask());
|
||||
// Pumping events in private runloop modes is known to interact badly with
|
||||
// app modal windows like NSAlert.
|
||||
if ([NSApp modalWindow])
|
||||
return;
|
||||
g_app_pump->SetModeMask(kAllModesMask);
|
||||
// Disable timer invalidation to avoid hangs. See crbug.com/912273.
|
||||
g_app_pump->SetTimerInvalidationAllowed(false);
|
||||
}
|
||||
|
||||
ScopedPumpMessagesInPrivateModes::~ScopedPumpMessagesInPrivateModes() {
|
||||
DCHECK(g_app_pump);
|
||||
g_app_pump->SetModeMask(kNSApplicationModalSafeModeMask);
|
||||
g_app_pump->SetTimerInvalidationAllowed(true);
|
||||
}
|
||||
|
||||
int ScopedPumpMessagesInPrivateModes::GetModeMaskForTest() {
|
||||
return g_app_pump ? g_app_pump->GetModeMask() : -1;
|
||||
}
|
||||
|
||||
MessagePumpNSApplication::MessagePumpNSApplication()
|
||||
: MessagePumpCFRunLoopBase(kNSApplicationModalSafeModeMask),
|
||||
running_own_loop_(false),
|
||||
quit_pending_(false) {
|
||||
DCHECK_EQ(nullptr, g_app_pump);
|
||||
g_app_pump = this;
|
||||
}
|
||||
|
||||
MessagePumpNSApplication::~MessagePumpNSApplication() {
|
||||
DCHECK_EQ(this, g_app_pump);
|
||||
g_app_pump = nullptr;
|
||||
}
|
||||
|
||||
void MessagePumpNSApplication::DoRun(Delegate* delegate) {
|
||||
bool last_running_own_loop_ = running_own_loop_;
|
||||
|
||||
// NSApp must be initialized by calling:
|
||||
// [{some class which implements CrAppProtocol} sharedApplication]
|
||||
// Most likely candidates are CrApplication or BrowserCrApplication.
|
||||
// These can be initialized from C++ code by calling
|
||||
// RegisterCrApp() or RegisterBrowserCrApp().
|
||||
CHECK(NSApp);
|
||||
|
||||
if (![NSApp isRunning]) {
|
||||
running_own_loop_ = false;
|
||||
// NSApplication manages autorelease pools itself when run this way.
|
||||
[NSApp run];
|
||||
} else {
|
||||
running_own_loop_ = true;
|
||||
NSDate* distant_future = [NSDate distantFuture];
|
||||
while (keep_running()) {
|
||||
MessagePumpScopedAutoreleasePool autorelease_pool(this);
|
||||
NSEvent* event = [NSApp nextEventMatchingMask:NSAnyEventMask
|
||||
untilDate:distant_future
|
||||
inMode:NSDefaultRunLoopMode
|
||||
dequeue:YES];
|
||||
if (event) {
|
||||
[NSApp sendEvent:event];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
running_own_loop_ = last_running_own_loop_;
|
||||
}
|
||||
|
||||
bool MessagePumpNSApplication::DoQuit() {
|
||||
// If the app is displaying a modal window in a native run loop, we can only
|
||||
// quit our run loop after the window is closed. Otherwise the [NSApplication
|
||||
// stop] below will apply to the modal window run loop instead. To work around
|
||||
// this, the quit is applied when we re-enter our own run loop after the
|
||||
// window is gone (see MessagePumpNSApplication::EnterExitRunLoop).
|
||||
if (nesting_level() > run_nesting_level() &&
|
||||
[[NSApplication sharedApplication] modalWindow] != nil) {
|
||||
quit_pending_ = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!running_own_loop_) {
|
||||
[[NSApplication sharedApplication] stop:nil];
|
||||
}
|
||||
|
||||
// Send a fake event to wake the loop up.
|
||||
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
|
||||
location:NSZeroPoint
|
||||
modifierFlags:0
|
||||
timestamp:0
|
||||
windowNumber:0
|
||||
context:NULL
|
||||
subtype:0
|
||||
data1:0
|
||||
data2:0]
|
||||
atStart:NO];
|
||||
return true;
|
||||
}
|
||||
|
||||
void MessagePumpNSApplication::EnterExitRunLoop(CFRunLoopActivity activity) {
|
||||
// If we previously tried quitting while a modal window was active, check if
|
||||
// the window is gone now and we're no longer nested in a system run loop.
|
||||
if (activity == kCFRunLoopEntry && quit_pending_ &&
|
||||
nesting_level() <= run_nesting_level() &&
|
||||
[[NSApplication sharedApplication] modalWindow] == nil) {
|
||||
quit_pending_ = false;
|
||||
if (DoQuit())
|
||||
OnDidQuit();
|
||||
}
|
||||
}
|
||||
|
||||
MessagePumpCrApplication::MessagePumpCrApplication() {
|
||||
}
|
||||
|
||||
MessagePumpCrApplication::~MessagePumpCrApplication() {
|
||||
}
|
||||
|
||||
// Prevents an autorelease pool from being created if the app is in the midst of
|
||||
// handling a UI event because various parts of AppKit depend on objects that
|
||||
// are created while handling a UI event to be autoreleased in the event loop.
|
||||
// An example of this is NSWindowController. When a window with a window
|
||||
// controller is closed it goes through a stack like this:
|
||||
// (Several stack frames elided for clarity)
|
||||
//
|
||||
// #0 [NSWindowController autorelease]
|
||||
// #1 DoAClose
|
||||
// #2 MessagePumpCFRunLoopBase::DoWork()
|
||||
// #3 [NSRunLoop run]
|
||||
// #4 [NSButton performClick:]
|
||||
// #5 [NSWindow sendEvent:]
|
||||
// #6 [NSApp sendEvent:]
|
||||
// #7 [NSApp run]
|
||||
//
|
||||
// -performClick: spins a nested run loop. If the pool created in DoWork was a
|
||||
// standard NSAutoreleasePool, it would release the objects that were
|
||||
// autoreleased into it once DoWork released it. This would cause the window
|
||||
// controller, which autoreleased itself in frame #0, to release itself, and
|
||||
// possibly free itself. Unfortunately this window controller controls the
|
||||
// window in frame #5. When the stack is unwound to frame #5, the window would
|
||||
// no longer exists and crashes may occur. Apple gets around this by never
|
||||
// releasing the pool it creates in frame #4, and letting frame #7 clean it up
|
||||
// when it cleans up the pool that wraps frame #7. When an autorelease pool is
|
||||
// released it releases all other pools that were created after it on the
|
||||
// autorelease pool stack.
|
||||
//
|
||||
// CrApplication is responsible for setting handlingSendEvent to true just
|
||||
// before it sends the event through the event handling mechanism, and
|
||||
// returning it to its previous value once the event has been sent.
|
||||
AutoreleasePoolType* MessagePumpCrApplication::CreateAutoreleasePool() {
|
||||
if (MessagePumpMac::IsHandlingSendEvent())
|
||||
return nil;
|
||||
return MessagePumpNSApplication::CreateAutoreleasePool();
|
||||
}
|
||||
|
||||
// static
|
||||
bool MessagePumpMac::UsingCrApp() {
|
||||
DCHECK([NSThread isMainThread]);
|
||||
|
||||
// If NSApp is still not initialized, then the subclass used cannot
|
||||
// be determined.
|
||||
DCHECK(NSApp);
|
||||
|
||||
// The pump was created using MessagePumpNSApplication.
|
||||
if (g_not_using_cr_app)
|
||||
return false;
|
||||
|
||||
return [NSApp conformsToProtocol:@protocol(CrAppProtocol)];
|
||||
}
|
||||
|
||||
// static
|
||||
bool MessagePumpMac::IsHandlingSendEvent() {
|
||||
DCHECK([NSApp conformsToProtocol:@protocol(CrAppProtocol)]);
|
||||
NSObject<CrAppProtocol>* app = static_cast<NSObject<CrAppProtocol>*>(NSApp);
|
||||
return [app isHandlingSendEvent];
|
||||
}
|
||||
#endif // !defined(OS_IOS)
|
||||
|
||||
// static
|
||||
std::unique_ptr<MessagePump> MessagePumpMac::Create() {
|
||||
if ([NSThread isMainThread]) {
|
||||
#if defined(OS_IOS)
|
||||
return std::make_unique<MessagePumpUIApplication>();
|
||||
#else
|
||||
if ([NSApp conformsToProtocol:@protocol(CrAppProtocol)])
|
||||
return std::make_unique<MessagePumpCrApplication>();
|
||||
|
||||
// The main-thread MessagePump implementations REQUIRE an NSApp.
|
||||
// Executables which have specific requirements for their
|
||||
// NSApplication subclass should initialize appropriately before
|
||||
// creating an event loop.
|
||||
[NSApplication sharedApplication];
|
||||
g_not_using_cr_app = true;
|
||||
return std::make_unique<MessagePumpNSApplication>();
|
||||
#endif
|
||||
}
|
||||
|
||||
return std::make_unique<MessagePumpNSRunLoop>();
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_TYPE_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_TYPE_H_
|
||||
|
||||
#include "build/build_config.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// A MessagePump has a particular type, which indicates the set of
|
||||
// asynchronous events it may process in addition to tasks and timers.
|
||||
|
||||
enum class MessagePumpType {
|
||||
// This type of pump only supports tasks and timers.
|
||||
DEFAULT,
|
||||
|
||||
// This type of pump also supports native UI events (e.g., Windows
|
||||
// messages).
|
||||
UI,
|
||||
|
||||
// User provided implementation of MessagePump interface
|
||||
CUSTOM,
|
||||
|
||||
// This type of pump also supports asynchronous IO.
|
||||
IO,
|
||||
|
||||
#if defined(OS_ANDROID)
|
||||
// This type of pump is backed by a Java message handler which is
|
||||
// responsible for running the tasks added to the ML. This is only for use
|
||||
// on Android. TYPE_JAVA behaves in essence like TYPE_UI, except during
|
||||
// construction where it does not use the main thread specific pump factory.
|
||||
JAVA,
|
||||
#endif // defined(OS_ANDROID)
|
||||
|
||||
#if defined(OS_MACOSX)
|
||||
// This type of pump is backed by a NSRunLoop. This is only for use on
|
||||
// OSX and IOS.
|
||||
NS_RUNLOOP,
|
||||
#endif // defined(OS_MACOSX)
|
||||
|
||||
#if defined(OS_WIN)
|
||||
// This type of pump supports WM_QUIT messages in addition to other native
|
||||
// UI events. This is only for use on windows.
|
||||
UI_WITH_WM_QUIT_SUPPORT,
|
||||
#endif // defined(OS_WIN)
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_TYPE_H_
|
||||
|
|
@ -0,0 +1,736 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/message_pump_win.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/debug/alias.h"
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/numerics/ranges.h"
|
||||
#include "base/numerics/safe_conversions.h"
|
||||
#include "base/trace_event/trace_event.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
namespace {
|
||||
|
||||
enum MessageLoopProblems {
|
||||
MESSAGE_POST_ERROR,
|
||||
COMPLETION_POST_ERROR,
|
||||
SET_TIMER_ERROR,
|
||||
RECEIVED_WM_QUIT_ERROR,
|
||||
MESSAGE_LOOP_PROBLEM_MAX,
|
||||
};
|
||||
|
||||
// Returns the number of milliseconds before |next_task_time|, clamped between
|
||||
// zero and the biggest DWORD value (or INFINITE if |next_task_time.is_max()|).
|
||||
// Optionally, a recent value of Now() may be passed in to avoid resampling it.
|
||||
DWORD GetSleepTimeoutMs(TimeTicks next_task_time,
|
||||
TimeTicks recent_now = TimeTicks()) {
|
||||
// Shouldn't need to sleep or install a timer when there's pending immediate
|
||||
// work.
|
||||
DCHECK(!next_task_time.is_null());
|
||||
|
||||
if (next_task_time.is_max())
|
||||
return INFINITE;
|
||||
|
||||
auto now = recent_now.is_null() ? TimeTicks::Now() : recent_now;
|
||||
auto timeout_ms = (next_task_time - now).InMillisecondsRoundedUp();
|
||||
|
||||
// A saturated_cast with an unsigned destination automatically clamps negative
|
||||
// values at zero.
|
||||
static_assert(!std::is_signed<DWORD>::value, "DWORD is unexpectedly signed");
|
||||
return saturated_cast<DWORD>(timeout_ms);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Message sent to get an additional time slice for pumping (processing) another
|
||||
// task (a series of such messages creates a continuous task pump).
|
||||
static const int kMsgHaveWork = WM_USER + 1;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpWin public:
|
||||
|
||||
MessagePumpWin::MessagePumpWin() = default;
|
||||
MessagePumpWin::~MessagePumpWin() = default;
|
||||
|
||||
void MessagePumpWin::Run(Delegate* delegate) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
RunState s;
|
||||
s.delegate = delegate;
|
||||
s.should_quit = false;
|
||||
s.run_depth = state_ ? state_->run_depth + 1 : 1;
|
||||
|
||||
RunState* previous_state = state_;
|
||||
state_ = &s;
|
||||
|
||||
DoRunLoop();
|
||||
|
||||
state_ = previous_state;
|
||||
}
|
||||
|
||||
void MessagePumpWin::Quit() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
DCHECK(state_);
|
||||
state_->should_quit = true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpForUI public:
|
||||
|
||||
MessagePumpForUI::MessagePumpForUI() {
|
||||
bool succeeded = message_window_.Create(
|
||||
BindRepeating(&MessagePumpForUI::MessageCallback, Unretained(this)));
|
||||
DCHECK(succeeded);
|
||||
}
|
||||
|
||||
MessagePumpForUI::~MessagePumpForUI() = default;
|
||||
|
||||
void MessagePumpForUI::ScheduleWork() {
|
||||
// This is the only MessagePumpForUI method which can be called outside of
|
||||
// |bound_thread_|.
|
||||
|
||||
bool not_scheduled = false;
|
||||
if (!work_scheduled_.compare_exchange_strong(not_scheduled, true))
|
||||
return; // Someone else continued the pumping.
|
||||
|
||||
// Make sure the MessagePump does some work for us.
|
||||
BOOL ret = PostMessage(message_window_.hwnd(), kMsgHaveWork, 0, 0);
|
||||
if (ret)
|
||||
return; // There was room in the Window Message queue.
|
||||
|
||||
// We have failed to insert a have-work message, so there is a chance that we
|
||||
// will starve tasks/timers while sitting in a nested run loop. Nested
|
||||
// loops only look at Windows Message queues, and don't look at *our* task
|
||||
// queues, etc., so we might not get a time slice in such. :-(
|
||||
// We could abort here, but the fear is that this failure mode is plausibly
|
||||
// common (queue is full, of about 2000 messages), so we'll do a near-graceful
|
||||
// recovery. Nested loops are pretty transient (we think), so this will
|
||||
// probably be recoverable.
|
||||
|
||||
// Clarify that we didn't really insert.
|
||||
work_scheduled_ = false;
|
||||
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", MESSAGE_POST_ERROR,
|
||||
MESSAGE_LOOP_PROBLEM_MAX);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// Since this is always called from |bound_thread_|, there is almost always
|
||||
// nothing to do as the loop is already running. When the loop becomes idle,
|
||||
// it will typically WaitForWork() in DoRunLoop() with the timeout provided by
|
||||
// DoSomeWork(). The only alternative to this is entering a native nested loop
|
||||
// (e.g. modal dialog) under a ScopedNestableTaskAllower, in which case
|
||||
// HandleWorkMessage() will be invoked when the system picks up kMsgHaveWork
|
||||
// and it will ScheduleNativeTimer() if it's out of immediate work. However,
|
||||
// in that alternate scenario : it's possible for a Windows native task (e.g.
|
||||
// https://docs.microsoft.com/en-us/windows/desktop/winmsg/using-hooks) to
|
||||
// wake the native nested loop and PostDelayedTask() to the current thread
|
||||
// from it. This is the only case where we must install/adjust the native
|
||||
// timer from ScheduleDelayedWork() because if we don't, the native loop will
|
||||
// go back to sleep, unaware of the new |delayed_work_time|.
|
||||
// TODO(gab): This could potentially be replaced by a ForegroundIdleProc hook
|
||||
// if Windows ends up being the only platform requiring ScheduleDelayedWork().
|
||||
if (in_native_loop_ && !work_scheduled_) {
|
||||
// TODO(gab): Consider passing a NextWorkInfo object to ScheduleDelayedWork
|
||||
// to take advantage of |recent_now| here too.
|
||||
ScheduleNativeTimer({delayed_work_time, TimeTicks::Now()});
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::EnableWmQuit() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
enable_wm_quit_ = true;
|
||||
}
|
||||
|
||||
void MessagePumpForUI::AddObserver(Observer* observer) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
observers_.AddObserver(observer);
|
||||
}
|
||||
|
||||
void MessagePumpForUI::RemoveObserver(Observer* observer) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
observers_.RemoveObserver(observer);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpForUI private:
|
||||
|
||||
bool MessagePumpForUI::MessageCallback(
|
||||
UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
switch (message) {
|
||||
case kMsgHaveWork:
|
||||
HandleWorkMessage();
|
||||
break;
|
||||
case WM_TIMER:
|
||||
if (wparam == reinterpret_cast<UINT_PTR>(this))
|
||||
HandleTimerMessage();
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MessagePumpForUI::DoRunLoop() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// IF this was just a simple PeekMessage() loop (servicing all possible work
|
||||
// queues), then Windows would try to achieve the following order according
|
||||
// to MSDN documentation about PeekMessage with no filter):
|
||||
// * Sent messages
|
||||
// * Posted messages
|
||||
// * Sent messages (again)
|
||||
// * WM_PAINT messages
|
||||
// * WM_TIMER messages
|
||||
//
|
||||
// Summary: none of the above classes is starved, and sent messages has twice
|
||||
// the chance of being processed (i.e., reduced service time).
|
||||
|
||||
for (;;) {
|
||||
// If we do any work, we may create more messages etc., and more work may
|
||||
// possibly be waiting in another task group. When we (for example)
|
||||
// ProcessNextWindowsMessage(), there is a good chance there are still more
|
||||
// messages waiting. On the other hand, when any of these methods return
|
||||
// having done no work, then it is pretty unlikely that calling them again
|
||||
// quickly will find any work to do. Finally, if they all say they had no
|
||||
// work, then it is a good time to consider sleeping (waiting) for more
|
||||
// work.
|
||||
|
||||
in_native_loop_ = false;
|
||||
state_->delegate->BeforeDoInternalWork();
|
||||
DCHECK(!in_native_loop_);
|
||||
|
||||
bool more_work_is_plausible = ProcessNextWindowsMessage();
|
||||
in_native_loop_ = false;
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = state_->delegate->DoSomeWork();
|
||||
in_native_loop_ = false;
|
||||
more_work_is_plausible |= next_work_info.is_immediate();
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
if (installed_native_timer_) {
|
||||
// As described in ScheduleNativeTimer(), the native timer is only
|
||||
// installed and needed while in a nested native loop. If it is installed,
|
||||
// it means the above work entered such a loop. Having now resumed, the
|
||||
// native timer is no longer needed.
|
||||
KillNativeTimer();
|
||||
}
|
||||
|
||||
if (more_work_is_plausible)
|
||||
continue;
|
||||
|
||||
more_work_is_plausible = state_->delegate->DoIdleWork();
|
||||
// DoIdleWork() shouldn't end up in native nested loops and thus shouldn't
|
||||
// have any chance of reinstalling a native timer.
|
||||
DCHECK(!in_native_loop_);
|
||||
DCHECK(!installed_native_timer_);
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
if (more_work_is_plausible)
|
||||
continue;
|
||||
|
||||
// WaitForWork() does some work itself, so notify the delegate of it.
|
||||
state_->delegate->BeforeWait();
|
||||
WaitForWork(next_work_info);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::WaitForWork(Delegate::NextWorkInfo next_work_info) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// Wait until a message is available, up to the time needed by the timer
|
||||
// manager to fire the next set of timers.
|
||||
DWORD wait_flags = MWMO_INPUTAVAILABLE;
|
||||
for (DWORD delay = GetSleepTimeoutMs(next_work_info.delayed_run_time,
|
||||
next_work_info.recent_now);
|
||||
delay != 0; delay = GetSleepTimeoutMs(next_work_info.delayed_run_time)) {
|
||||
// Tell the optimizer to retain these values to simplify analyzing hangs.
|
||||
base::debug::Alias(&delay);
|
||||
base::debug::Alias(&wait_flags);
|
||||
DWORD result = MsgWaitForMultipleObjectsEx(0, nullptr, delay, QS_ALLINPUT,
|
||||
wait_flags);
|
||||
|
||||
if (WAIT_OBJECT_0 == result) {
|
||||
// A WM_* message is available.
|
||||
// If a parent child relationship exists between windows across threads
|
||||
// then their thread inputs are implicitly attached.
|
||||
// This causes the MsgWaitForMultipleObjectsEx API to return indicating
|
||||
// that messages are ready for processing (Specifically, mouse messages
|
||||
// intended for the child window may appear if the child window has
|
||||
// capture).
|
||||
// The subsequent PeekMessages call may fail to return any messages thus
|
||||
// causing us to enter a tight loop at times.
|
||||
// The code below is a workaround to give the child window
|
||||
// some time to process its input messages by looping back to
|
||||
// MsgWaitForMultipleObjectsEx above when there are no messages for the
|
||||
// current thread.
|
||||
MSG msg = {0};
|
||||
bool has_pending_sent_message =
|
||||
(HIWORD(::GetQueueStatus(QS_SENDMESSAGE)) & QS_SENDMESSAGE) != 0;
|
||||
if (has_pending_sent_message ||
|
||||
::PeekMessage(&msg, nullptr, 0, 0, PM_NOREMOVE)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We know there are no more messages for this thread because PeekMessage
|
||||
// has returned false. Reset |wait_flags| so that we wait for a *new*
|
||||
// message.
|
||||
wait_flags = 0;
|
||||
}
|
||||
|
||||
DCHECK_NE(WAIT_FAILED, result) << GetLastError();
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::HandleWorkMessage() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// The kMsgHaveWork message was consumed by a native loop, we must assume
|
||||
// we're in one until DoRunLoop() gets control back.
|
||||
in_native_loop_ = true;
|
||||
|
||||
// If we are being called outside of the context of Run, then don't try to do
|
||||
// any work. This could correspond to a MessageBox call or something of that
|
||||
// sort.
|
||||
if (!state_) {
|
||||
// Since we handled a kMsgHaveWork message, we must still update this flag.
|
||||
work_scheduled_ = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Let whatever would have run had we not been putting messages in the queue
|
||||
// run now. This is an attempt to make our dummy message not starve other
|
||||
// messages that may be in the Windows message queue.
|
||||
ProcessPumpReplacementMessage();
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = state_->delegate->DoSomeWork();
|
||||
if (next_work_info.is_immediate()) {
|
||||
ScheduleWork();
|
||||
} else {
|
||||
ScheduleNativeTimer(next_work_info);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::HandleTimerMessage() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// ::KillTimer doesn't remove pending WM_TIMER messages from the queue,
|
||||
// explicitly ignore the last WM_TIMER message in that case to avoid handling
|
||||
// work from here when DoRunLoop() is active (which could result in scheduling
|
||||
// work from two places at once). Note: we're still fine in the event that a
|
||||
// second native nested loop is entered before such a dead WM_TIMER message is
|
||||
// discarded because ::SetTimer merely resets the timer if invoked twice with
|
||||
// the same id.
|
||||
if (!installed_native_timer_)
|
||||
return;
|
||||
|
||||
// We only need to fire once per specific delay, another timer may be
|
||||
// scheduled below but we're done with this one.
|
||||
KillNativeTimer();
|
||||
|
||||
// If we are being called outside of the context of Run, then don't do
|
||||
// anything. This could correspond to a MessageBox call or something of
|
||||
// that sort.
|
||||
if (!state_)
|
||||
return;
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = state_->delegate->DoSomeWork();
|
||||
if (next_work_info.is_immediate()) {
|
||||
ScheduleWork();
|
||||
} else {
|
||||
ScheduleNativeTimer(next_work_info);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::ScheduleNativeTimer(
|
||||
Delegate::NextWorkInfo next_work_info) {
|
||||
DCHECK(!next_work_info.is_immediate());
|
||||
DCHECK(in_native_loop_);
|
||||
|
||||
// Do not redundantly set the same native timer again if it was already set.
|
||||
// This can happen when a nested native loop goes idle with pending delayed
|
||||
// tasks, then gets woken up by an immediate task, and goes back to idle with
|
||||
// the same pending delay. No need to kill the native timer if there is
|
||||
// already one but the |delayed_run_time| has changed as ::SetTimer reuses the
|
||||
// same id and will replace and reset the existing timer.
|
||||
if (installed_native_timer_ &&
|
||||
*installed_native_timer_ == next_work_info.delayed_run_time) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (next_work_info.delayed_run_time.is_max())
|
||||
return;
|
||||
|
||||
// We do not use native Windows timers in general as they have a poor, 10ms,
|
||||
// granularity. Instead we rely on MsgWaitForMultipleObjectsEx's
|
||||
// high-resolution timeout to sleep without timers in WaitForWork(). However,
|
||||
// when entering a nested native ::GetMessage() loop (e.g. native modal
|
||||
// windows) under a ScopedNestableTaskAllower, we have to rely on a native
|
||||
// timer when HandleWorkMessage() runs out of immediate work. Since
|
||||
// ScopedNestableTaskAllower invokes ScheduleWork() : we are guaranteed that
|
||||
// HandleWorkMessage() will be called after entering a nested native loop that
|
||||
// should process application tasks. But once HandleWorkMessage() is out of
|
||||
// immediate work, ::SetTimer() is used to guarantee we are invoked again
|
||||
// should the next delayed task expire before the nested native loop ends. The
|
||||
// native timer being unnecessary once we return to our DoRunLoop(), we
|
||||
// ::KillTimer when it resumes (nested native loops should be rare so we're
|
||||
// not worried about ::SetTimer<=>::KillTimer churn).
|
||||
// TODO(gab): The long-standing legacy dependency on the behavior of
|
||||
// ScopedNestableTaskAllower is unfortunate, would be nice to make this a
|
||||
// MessagePump concept (instead of requiring impls to invoke ScheduleWork()
|
||||
// one-way and no-op DoWork() the other way).
|
||||
|
||||
UINT delay_msec = strict_cast<UINT>(GetSleepTimeoutMs(
|
||||
next_work_info.delayed_run_time, next_work_info.recent_now));
|
||||
if (delay_msec == 0) {
|
||||
ScheduleWork();
|
||||
} else {
|
||||
// TODO(gab): ::SetTimer()'s documentation claims it does this for us.
|
||||
// Consider removing this safety net.
|
||||
delay_msec = ClampToRange(delay_msec, UINT(USER_TIMER_MINIMUM),
|
||||
UINT(USER_TIMER_MAXIMUM));
|
||||
|
||||
// Tell the optimizer to retain the delay to simplify analyzing hangs.
|
||||
base::debug::Alias(&delay_msec);
|
||||
UINT_PTR ret =
|
||||
::SetTimer(message_window_.hwnd(), reinterpret_cast<UINT_PTR>(this),
|
||||
delay_msec, nullptr);
|
||||
installed_native_timer_ = next_work_info.delayed_run_time;
|
||||
|
||||
if (ret)
|
||||
return;
|
||||
// If we can't set timers, we are in big trouble... but cross our fingers
|
||||
// for now.
|
||||
// TODO(jar): If we don't see this error, use a CHECK() here instead.
|
||||
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", SET_TIMER_ERROR,
|
||||
MESSAGE_LOOP_PROBLEM_MAX);
|
||||
}
|
||||
}
|
||||
|
||||
void MessagePumpForUI::KillNativeTimer() {
|
||||
DCHECK(installed_native_timer_);
|
||||
const bool success =
|
||||
::KillTimer(message_window_.hwnd(), reinterpret_cast<UINT_PTR>(this));
|
||||
DPCHECK(success);
|
||||
installed_native_timer_.reset();
|
||||
}
|
||||
|
||||
bool MessagePumpForUI::ProcessNextWindowsMessage() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// If there are sent messages in the queue then PeekMessage internally
|
||||
// dispatches the message and returns false. We return true in this
|
||||
// case to ensure that the message loop peeks again instead of calling
|
||||
// MsgWaitForMultipleObjectsEx.
|
||||
bool sent_messages_in_queue = false;
|
||||
DWORD queue_status = ::GetQueueStatus(QS_SENDMESSAGE);
|
||||
if (HIWORD(queue_status) & QS_SENDMESSAGE)
|
||||
sent_messages_in_queue = true;
|
||||
|
||||
MSG msg;
|
||||
if (::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE)
|
||||
return ProcessMessageHelper(msg);
|
||||
|
||||
return sent_messages_in_queue;
|
||||
}
|
||||
|
||||
bool MessagePumpForUI::ProcessMessageHelper(const MSG& msg) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
TRACE_EVENT1("base,toplevel", "MessagePumpForUI::ProcessMessageHelper",
|
||||
"message", msg.message);
|
||||
if (WM_QUIT == msg.message) {
|
||||
// WM_QUIT is the standard way to exit a ::GetMessage() loop. Our
|
||||
// MessageLoop has its own quit mechanism, so WM_QUIT should only terminate
|
||||
// it if |enable_wm_quit_| is explicitly set (and is generally unexpected
|
||||
// otherwise).
|
||||
if (enable_wm_quit_) {
|
||||
state_->should_quit = true;
|
||||
return false;
|
||||
}
|
||||
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem",
|
||||
RECEIVED_WM_QUIT_ERROR, MESSAGE_LOOP_PROBLEM_MAX);
|
||||
return true;
|
||||
}
|
||||
|
||||
// While running our main message pump, we discard kMsgHaveWork messages.
|
||||
if (msg.message == kMsgHaveWork && msg.hwnd == message_window_.hwnd())
|
||||
return ProcessPumpReplacementMessage();
|
||||
|
||||
for (Observer& observer : observers_)
|
||||
observer.WillDispatchMSG(msg);
|
||||
::TranslateMessage(&msg);
|
||||
::DispatchMessage(&msg);
|
||||
for (Observer& observer : observers_)
|
||||
observer.DidDispatchMSG(msg);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpForUI::ProcessPumpReplacementMessage() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// When we encounter a kMsgHaveWork message, this method is called to peek and
|
||||
// process a replacement message. The goal is to make the kMsgHaveWork as non-
|
||||
// intrusive as possible, even though a continuous stream of such messages are
|
||||
// posted. This method carefully peeks a message while there is no chance for
|
||||
// a kMsgHaveWork to be pending, then resets the |have_work_| flag (allowing a
|
||||
// replacement kMsgHaveWork to possibly be posted), and finally dispatches
|
||||
// that peeked replacement. Note that the re-post of kMsgHaveWork may be
|
||||
// asynchronous to this thread!!
|
||||
|
||||
MSG msg;
|
||||
const bool have_message =
|
||||
::PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE) != FALSE;
|
||||
|
||||
// Expect no message or a message different than kMsgHaveWork.
|
||||
DCHECK(!have_message || kMsgHaveWork != msg.message ||
|
||||
msg.hwnd != message_window_.hwnd());
|
||||
|
||||
// Since we discarded a kMsgHaveWork message, we must update the flag.
|
||||
DCHECK(work_scheduled_);
|
||||
work_scheduled_ = false;
|
||||
|
||||
// We don't need a special time slice if we didn't |have_message| to process.
|
||||
if (!have_message)
|
||||
return false;
|
||||
|
||||
if (WM_QUIT == msg.message) {
|
||||
// If we're in a nested ::GetMessage() loop then we must let that loop see
|
||||
// the WM_QUIT in order for it to exit. If we're in DoRunLoop then the re-
|
||||
// posted WM_QUIT will be either ignored, or handled, by
|
||||
// ProcessMessageHelper() called directly from ProcessNextWindowsMessage().
|
||||
::PostQuitMessage(static_cast<int>(msg.wParam));
|
||||
// Note: we *must not* ScheduleWork() here as WM_QUIT is a low-priority
|
||||
// message on Windows (it is only returned by ::PeekMessage() when idle) :
|
||||
// https://blogs.msdn.microsoft.com/oldnewthing/20051104-33/?p=33453. As
|
||||
// such posting a kMsgHaveWork message via ScheduleWork() would cause an
|
||||
// infinite loop (kMsgHaveWork message handled first means we end up here
|
||||
// again and repost WM_QUIT+ScheduleWork() again, etc.). Not leaving a
|
||||
// kMsgHaveWork message behind however is also problematic as unwinding
|
||||
// multiple layers of nested ::GetMessage() loops can result in starving
|
||||
// application tasks. TODO(https://crbug.com/890016) : Fix this.
|
||||
|
||||
// The return value is mostly irrelevant but return true like we would after
|
||||
// processing a QuitClosure() task.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Guarantee we'll get another time slice in the case where we go into native
|
||||
// windows code. This ScheduleWork() may hurt performance a tiny bit when
|
||||
// tasks appear very infrequently, but when the event queue is busy, the
|
||||
// kMsgHaveWork events get (percentage wise) rarer and rarer.
|
||||
ScheduleWork();
|
||||
return ProcessMessageHelper(msg);
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpForIO public:
|
||||
|
||||
MessagePumpForIO::IOContext::IOContext() {
|
||||
memset(&overlapped, 0, sizeof(overlapped));
|
||||
}
|
||||
|
||||
MessagePumpForIO::MessagePumpForIO() {
|
||||
port_.Set(::CreateIoCompletionPort(INVALID_HANDLE_VALUE, nullptr,
|
||||
reinterpret_cast<ULONG_PTR>(nullptr), 1));
|
||||
DCHECK(port_.IsValid());
|
||||
}
|
||||
|
||||
MessagePumpForIO::~MessagePumpForIO() = default;
|
||||
|
||||
void MessagePumpForIO::ScheduleWork() {
|
||||
// This is the only MessagePumpForIO method which can be called outside of
|
||||
// |bound_thread_|.
|
||||
|
||||
bool not_scheduled = false;
|
||||
if (!work_scheduled_.compare_exchange_strong(not_scheduled, true))
|
||||
return; // Someone else continued the pumping.
|
||||
|
||||
// Make sure the MessagePump does some work for us.
|
||||
BOOL ret = ::PostQueuedCompletionStatus(port_.Get(), 0,
|
||||
reinterpret_cast<ULONG_PTR>(this),
|
||||
reinterpret_cast<OVERLAPPED*>(this));
|
||||
if (ret)
|
||||
return; // Post worked perfectly.
|
||||
|
||||
// See comment in MessagePumpForUI::ScheduleWork() for this error recovery.
|
||||
|
||||
work_scheduled_ = false; // Clarify that we didn't succeed.
|
||||
UMA_HISTOGRAM_ENUMERATION("Chrome.MessageLoopProblem", COMPLETION_POST_ERROR,
|
||||
MESSAGE_LOOP_PROBLEM_MAX);
|
||||
}
|
||||
|
||||
void MessagePumpForIO::ScheduleDelayedWork(const TimeTicks& delayed_work_time) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// Since this is always called from |bound_thread_|, there is nothing to do as
|
||||
// the loop is already running. It will WaitForWork() in
|
||||
// DoRunLoop() with the correct timeout when it's out of immediate tasks.
|
||||
}
|
||||
|
||||
HRESULT MessagePumpForIO::RegisterIOHandler(HANDLE file_handle,
|
||||
IOHandler* handler) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
HANDLE port = ::CreateIoCompletionPort(
|
||||
file_handle, port_.Get(), reinterpret_cast<ULONG_PTR>(handler), 1);
|
||||
return (port != nullptr) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
bool MessagePumpForIO::RegisterJobObject(HANDLE job_handle,
|
||||
IOHandler* handler) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
JOBOBJECT_ASSOCIATE_COMPLETION_PORT info;
|
||||
info.CompletionKey = handler;
|
||||
info.CompletionPort = port_.Get();
|
||||
return ::SetInformationJobObject(job_handle,
|
||||
JobObjectAssociateCompletionPortInformation,
|
||||
&info, sizeof(info)) != FALSE;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpForIO private:
|
||||
|
||||
void MessagePumpForIO::DoRunLoop() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
for (;;) {
|
||||
// If we do any work, we may create more messages etc., and more work may
|
||||
// possibly be waiting in another task group. When we (for example)
|
||||
// WaitForIOCompletion(), there is a good chance there are still more
|
||||
// messages waiting. On the other hand, when any of these methods return
|
||||
// having done no work, then it is pretty unlikely that calling them
|
||||
// again quickly will find any work to do. Finally, if they all say they
|
||||
// had no work, then it is a good time to consider sleeping (waiting) for
|
||||
// more work.
|
||||
|
||||
Delegate::NextWorkInfo next_work_info = state_->delegate->DoSomeWork();
|
||||
bool more_work_is_plausible = next_work_info.is_immediate();
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
state_->delegate->BeforeWait();
|
||||
more_work_is_plausible |= WaitForIOCompletion(0, nullptr);
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
if (more_work_is_plausible)
|
||||
continue;
|
||||
|
||||
more_work_is_plausible = state_->delegate->DoIdleWork();
|
||||
if (state_->should_quit)
|
||||
break;
|
||||
|
||||
if (more_work_is_plausible)
|
||||
continue;
|
||||
|
||||
state_->delegate->BeforeWait();
|
||||
WaitForWork(next_work_info);
|
||||
}
|
||||
}
|
||||
|
||||
// Wait until IO completes, up to the time needed by the timer manager to fire
|
||||
// the next set of timers.
|
||||
void MessagePumpForIO::WaitForWork(Delegate::NextWorkInfo next_work_info) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
// We do not support nested IO message loops. This is to avoid messy
|
||||
// recursion problems.
|
||||
DCHECK_EQ(1, state_->run_depth) << "Cannot nest an IO message loop!";
|
||||
|
||||
DWORD timeout = GetSleepTimeoutMs(next_work_info.delayed_run_time,
|
||||
next_work_info.recent_now);
|
||||
|
||||
// Tell the optimizer to retain these values to simplify analyzing hangs.
|
||||
base::debug::Alias(&timeout);
|
||||
WaitForIOCompletion(timeout, nullptr);
|
||||
}
|
||||
|
||||
bool MessagePumpForIO::WaitForIOCompletion(DWORD timeout, IOHandler* filter) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
IOItem item;
|
||||
if (completed_io_.empty() || !MatchCompletedIOItem(filter, &item)) {
|
||||
// We have to ask the system for another IO completion.
|
||||
if (!GetIOItem(timeout, &item))
|
||||
return false;
|
||||
|
||||
if (ProcessInternalIOItem(item))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (filter && item.handler != filter) {
|
||||
// Save this item for later
|
||||
completed_io_.push_back(item);
|
||||
} else {
|
||||
item.handler->OnIOCompleted(item.context, item.bytes_transfered,
|
||||
item.error);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Asks the OS for another IO completion result.
|
||||
bool MessagePumpForIO::GetIOItem(DWORD timeout, IOItem* item) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
memset(item, 0, sizeof(*item));
|
||||
ULONG_PTR key = reinterpret_cast<ULONG_PTR>(nullptr);
|
||||
OVERLAPPED* overlapped = nullptr;
|
||||
if (!::GetQueuedCompletionStatus(port_.Get(), &item->bytes_transfered, &key,
|
||||
&overlapped, timeout)) {
|
||||
if (!overlapped)
|
||||
return false; // Nothing in the queue.
|
||||
item->error = GetLastError();
|
||||
item->bytes_transfered = 0;
|
||||
}
|
||||
|
||||
item->handler = reinterpret_cast<IOHandler*>(key);
|
||||
item->context = reinterpret_cast<IOContext*>(overlapped);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MessagePumpForIO::ProcessInternalIOItem(const IOItem& item) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
if (reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.context) &&
|
||||
reinterpret_cast<void*>(this) == reinterpret_cast<void*>(item.handler)) {
|
||||
// This is our internal completion.
|
||||
DCHECK(!item.bytes_transfered);
|
||||
work_scheduled_ = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Returns a completion item that was previously received.
|
||||
bool MessagePumpForIO::MatchCompletedIOItem(IOHandler* filter, IOItem* item) {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_);
|
||||
|
||||
DCHECK(!completed_io_.empty());
|
||||
for (std::list<IOItem>::iterator it = completed_io_.begin();
|
||||
it != completed_io_.end(); ++it) {
|
||||
if (!filter || it->handler == filter) {
|
||||
*item = *it;
|
||||
completed_io_.erase(it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,295 @@
|
|||
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_
|
||||
#define BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_
|
||||
|
||||
#include <windows.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/message_loop/message_pump.h"
|
||||
#include "base/observer_list.h"
|
||||
#include "base/optional.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "base/time/time.h"
|
||||
#include "base/win/message_window.h"
|
||||
#include "base/win/scoped_handle.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// MessagePumpWin serves as the base for specialized versions of the MessagePump
|
||||
// for Windows. It provides basic functionality like handling of observers and
|
||||
// controlling the lifetime of the message pump.
|
||||
class BASE_EXPORT MessagePumpWin : public MessagePump {
|
||||
public:
|
||||
MessagePumpWin();
|
||||
~MessagePumpWin() override;
|
||||
|
||||
// MessagePump methods:
|
||||
void Run(Delegate* delegate) override;
|
||||
void Quit() override;
|
||||
|
||||
protected:
|
||||
struct RunState {
|
||||
Delegate* delegate;
|
||||
|
||||
// Used to flag that the current Run() invocation should return ASAP.
|
||||
bool should_quit;
|
||||
|
||||
// Used to count how many Run() invocations are on the stack.
|
||||
int run_depth;
|
||||
};
|
||||
|
||||
virtual void DoRunLoop() = 0;
|
||||
|
||||
// True iff:
|
||||
// * MessagePumpForUI: there's a kMsgDoWork message pending in the Windows
|
||||
// Message queue. i.e. when:
|
||||
// a. The pump is about to wakeup from idle.
|
||||
// b. The pump is about to enter a nested native loop and a
|
||||
// ScopedNestableTaskAllower was instantiated to allow application
|
||||
// tasks to execute in that nested loop (ScopedNestableTaskAllower
|
||||
// invokes ScheduleWork()).
|
||||
// c. While in a native (nested) loop : HandleWorkMessage() =>
|
||||
// ProcessPumpReplacementMessage() invokes ScheduleWork() before
|
||||
// processing a native message to guarantee this pump will get another
|
||||
// time slice if it goes into native Windows code and enters a native
|
||||
// nested loop. This is different from (b.) because we're not yet
|
||||
// processing an application task at the current run level and
|
||||
// therefore are expected to keep pumping application tasks without
|
||||
// necessitating a ScopedNestableTaskAllower.
|
||||
//
|
||||
// * MessagePumpforIO: there's a dummy IO completion item with |this| as an
|
||||
// lpCompletionKey in the queue which is about to wakeup
|
||||
// WaitForIOCompletion(). MessagePumpForIO doesn't support nesting so
|
||||
// this is simpler than MessagePumpForUI.
|
||||
std::atomic_bool work_scheduled_{false};
|
||||
|
||||
// State for the current invocation of Run.
|
||||
RunState* state_ = nullptr;
|
||||
|
||||
THREAD_CHECKER(bound_thread_);
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpForUI extends MessagePumpWin with methods that are particular to a
|
||||
// MessageLoop instantiated with TYPE_UI.
|
||||
//
|
||||
// MessagePumpForUI implements a "traditional" Windows message pump. It contains
|
||||
// a nearly infinite loop that peeks out messages, and then dispatches them.
|
||||
// Intermixed with those peeks are callouts to DoSomeWork. When there are no
|
||||
// events to be serviced, this pump goes into a wait state. In most cases, this
|
||||
// message pump handles all processing.
|
||||
//
|
||||
// However, when a task, or windows event, invokes on the stack a native dialog
|
||||
// box or such, that window typically provides a bare bones (native?) message
|
||||
// pump. That bare-bones message pump generally supports little more than a
|
||||
// peek of the Windows message queue, followed by a dispatch of the peeked
|
||||
// message. MessageLoop extends that bare-bones message pump to also service
|
||||
// Tasks, at the cost of some complexity.
|
||||
//
|
||||
// The basic structure of the extension (referred to as a sub-pump) is that a
|
||||
// special message, kMsgHaveWork, is repeatedly injected into the Windows
|
||||
// Message queue. Each time the kMsgHaveWork message is peeked, checks are made
|
||||
// for an extended set of events, including the availability of Tasks to run.
|
||||
//
|
||||
// After running a task, the special message kMsgHaveWork is again posted to the
|
||||
// Windows Message queue, ensuring a future time slice for processing a future
|
||||
// event. To prevent flooding the Windows Message queue, care is taken to be
|
||||
// sure that at most one kMsgHaveWork message is EVER pending in the Window's
|
||||
// Message queue.
|
||||
//
|
||||
// There are a few additional complexities in this system where, when there are
|
||||
// no Tasks to run, this otherwise infinite stream of messages which drives the
|
||||
// sub-pump is halted. The pump is automatically re-started when Tasks are
|
||||
// queued.
|
||||
//
|
||||
// A second complexity is that the presence of this stream of posted tasks may
|
||||
// prevent a bare-bones message pump from ever peeking a WM_PAINT or WM_TIMER.
|
||||
// Such paint and timer events always give priority to a posted message, such as
|
||||
// kMsgHaveWork messages. As a result, care is taken to do some peeking in
|
||||
// between the posting of each kMsgHaveWork message (i.e., after kMsgHaveWork is
|
||||
// peeked, and before a replacement kMsgHaveWork is posted).
|
||||
//
|
||||
// NOTE: Although it may seem odd that messages are used to start and stop this
|
||||
// flow (as opposed to signaling objects, etc.), it should be understood that
|
||||
// the native message pump will *only* respond to messages. As a result, it is
|
||||
// an excellent choice. It is also helpful that the starter messages that are
|
||||
// placed in the queue when new task arrive also awakens DoRunLoop.
|
||||
//
|
||||
class BASE_EXPORT MessagePumpForUI : public MessagePumpWin {
|
||||
public:
|
||||
MessagePumpForUI();
|
||||
~MessagePumpForUI() override;
|
||||
|
||||
// MessagePump methods:
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
// Make the MessagePumpForUI respond to WM_QUIT messages.
|
||||
void EnableWmQuit();
|
||||
|
||||
// An observer interface to give the scheduler an opportunity to log
|
||||
// information about MSGs before and after they are dispatched.
|
||||
class BASE_EXPORT Observer {
|
||||
public:
|
||||
virtual void WillDispatchMSG(const MSG& msg) = 0;
|
||||
virtual void DidDispatchMSG(const MSG& msg) = 0;
|
||||
};
|
||||
|
||||
void AddObserver(Observer* observer);
|
||||
void RemoveObserver(Observer* obseerver);
|
||||
|
||||
private:
|
||||
bool MessageCallback(UINT message,
|
||||
WPARAM wparam,
|
||||
LPARAM lparam,
|
||||
LRESULT* result);
|
||||
void DoRunLoop() override;
|
||||
void WaitForWork(Delegate::NextWorkInfo next_work_info);
|
||||
void HandleWorkMessage();
|
||||
void HandleTimerMessage();
|
||||
void ScheduleNativeTimer(Delegate::NextWorkInfo next_work_info);
|
||||
void KillNativeTimer();
|
||||
bool ProcessNextWindowsMessage();
|
||||
bool ProcessMessageHelper(const MSG& msg);
|
||||
bool ProcessPumpReplacementMessage();
|
||||
|
||||
base::win::MessageWindow message_window_;
|
||||
|
||||
// Whether MessagePumpForUI responds to WM_QUIT messages or not.
|
||||
// TODO(thestig): Remove when the Cloud Print Service goes away.
|
||||
bool enable_wm_quit_ = false;
|
||||
|
||||
// Non-nullopt if there's currently a native timer installed. If so, it
|
||||
// indicates when the timer is set to fire and can be used to avoid setting
|
||||
// redundant timers.
|
||||
Optional<TimeTicks> installed_native_timer_;
|
||||
|
||||
// This will become true when a native loop takes our kMsgHaveWork out of the
|
||||
// system queue. It will be reset to false whenever DoRunLoop regains control.
|
||||
// Used to decide whether ScheduleDelayedWork() should start a native timer.
|
||||
bool in_native_loop_ = false;
|
||||
|
||||
ObserverList<Observer>::Unchecked observers_;
|
||||
};
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// MessagePumpForIO extends MessagePumpWin with methods that are particular to a
|
||||
// MessageLoop instantiated with TYPE_IO. This version of MessagePump does not
|
||||
// deal with Windows mesagges, and instead has a Run loop based on Completion
|
||||
// Ports so it is better suited for IO operations.
|
||||
//
|
||||
class BASE_EXPORT MessagePumpForIO : public MessagePumpWin {
|
||||
public:
|
||||
struct BASE_EXPORT IOContext {
|
||||
IOContext();
|
||||
OVERLAPPED overlapped;
|
||||
};
|
||||
|
||||
// Clients interested in receiving OS notifications when asynchronous IO
|
||||
// operations complete should implement this interface and register themselves
|
||||
// with the message pump.
|
||||
//
|
||||
// Typical use #1:
|
||||
// class MyFile : public IOHandler {
|
||||
// MyFile() {
|
||||
// ...
|
||||
// message_pump->RegisterIOHandler(file_, this);
|
||||
// }
|
||||
// // Plus some code to make sure that this destructor is not called
|
||||
// // while there are pending IO operations.
|
||||
// ~MyFile() {
|
||||
// }
|
||||
// virtual void OnIOCompleted(IOContext* context, DWORD bytes_transfered,
|
||||
// DWORD error) {
|
||||
// ...
|
||||
// delete context;
|
||||
// }
|
||||
// void DoSomeIo() {
|
||||
// ...
|
||||
// IOContext* context = new IOContext;
|
||||
// ReadFile(file_, buffer, num_bytes, &read, &context);
|
||||
// }
|
||||
// HANDLE file_;
|
||||
// };
|
||||
//
|
||||
// Typical use #2:
|
||||
// Same as the previous example, except that in order to deal with the
|
||||
// requirement stated for the destructor, the class calls WaitForIOCompletion
|
||||
// from the destructor to block until all IO finishes.
|
||||
// ~MyFile() {
|
||||
// while(pending_)
|
||||
// message_pump->WaitForIOCompletion(INFINITE, this);
|
||||
// }
|
||||
//
|
||||
class IOHandler {
|
||||
public:
|
||||
virtual ~IOHandler() {}
|
||||
// This will be called once the pending IO operation associated with
|
||||
// |context| completes. |error| is the Win32 error code of the IO operation
|
||||
// (ERROR_SUCCESS if there was no error). |bytes_transfered| will be zero
|
||||
// on error.
|
||||
virtual void OnIOCompleted(IOContext* context,
|
||||
DWORD bytes_transfered,
|
||||
DWORD error) = 0;
|
||||
};
|
||||
|
||||
MessagePumpForIO();
|
||||
~MessagePumpForIO() override;
|
||||
|
||||
// MessagePump methods:
|
||||
void ScheduleWork() override;
|
||||
void ScheduleDelayedWork(const TimeTicks& delayed_work_time) override;
|
||||
|
||||
// Register the handler to be used when asynchronous IO for the given file
|
||||
// completes. The registration persists as long as |file_handle| is valid, so
|
||||
// |handler| must be valid as long as there is pending IO for the given file.
|
||||
HRESULT RegisterIOHandler(HANDLE file_handle, IOHandler* handler);
|
||||
|
||||
// Register the handler to be used to process job events. The registration
|
||||
// persists as long as the job object is live, so |handler| must be valid
|
||||
// until the job object is destroyed. Returns true if the registration
|
||||
// succeeded, and false otherwise.
|
||||
bool RegisterJobObject(HANDLE job_handle, IOHandler* handler);
|
||||
|
||||
// Waits for the next IO completion that should be processed by |filter|, for
|
||||
// up to |timeout| milliseconds. Return true if any IO operation completed,
|
||||
// regardless of the involved handler, and false if the timeout expired. If
|
||||
// the completion port received any message and the involved IO handler
|
||||
// matches |filter|, the callback is called before returning from this code;
|
||||
// if the handler is not the one that we are looking for, the callback will
|
||||
// be postponed for another time, so reentrancy problems can be avoided.
|
||||
// External use of this method should be reserved for the rare case when the
|
||||
// caller is willing to allow pausing regular task dispatching on this thread.
|
||||
bool WaitForIOCompletion(DWORD timeout, IOHandler* filter);
|
||||
|
||||
private:
|
||||
struct IOItem {
|
||||
IOHandler* handler;
|
||||
IOContext* context;
|
||||
DWORD bytes_transfered;
|
||||
DWORD error;
|
||||
};
|
||||
|
||||
void DoRunLoop() override;
|
||||
void WaitForWork(Delegate::NextWorkInfo next_work_info);
|
||||
bool MatchCompletedIOItem(IOHandler* filter, IOItem* item);
|
||||
bool GetIOItem(DWORD timeout, IOItem* item);
|
||||
bool ProcessInternalIOItem(const IOItem& item);
|
||||
|
||||
// The completion port associated with this thread.
|
||||
win::ScopedHandle port_;
|
||||
// This list will be empty almost always. It stores IO completions that have
|
||||
// not been delivered yet because somebody was doing cleanup.
|
||||
std::list<IOItem> completed_io_;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_MESSAGE_PUMP_WIN_H_
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright 2014 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_TIMER_SLACK_H_
|
||||
#define BASE_MESSAGE_LOOP_TIMER_SLACK_H_
|
||||
|
||||
namespace base {
|
||||
|
||||
// Amount of timer slack to use for delayed timers. Increasing timer slack
|
||||
// allows the OS to coalesce timers more effectively.
|
||||
enum TimerSlack {
|
||||
// Lowest value for timer slack allowed by OS.
|
||||
TIMER_SLACK_NONE,
|
||||
|
||||
// Maximal value for timer slack allowed by OS.
|
||||
TIMER_SLACK_MAXIMUM
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_TIMER_SLACK_H_
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/watchable_io_message_pump_posix.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
WatchableIOMessagePumpPosix::FdWatchControllerInterface::
|
||||
FdWatchControllerInterface(const Location& from_here)
|
||||
: created_from_location_(from_here) {}
|
||||
|
||||
WatchableIOMessagePumpPosix::FdWatchControllerInterface::
|
||||
~FdWatchControllerInterface() = default;
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2018 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_WATCHABLE_IO_MESSAGE_PUMP_POSIX_H_
|
||||
#define BASE_MESSAGE_LOOP_WATCHABLE_IO_MESSAGE_PUMP_POSIX_H_
|
||||
|
||||
#include "base/location.h"
|
||||
#include "base/macros.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
class WatchableIOMessagePumpPosix {
|
||||
public:
|
||||
// Used with WatchFileDescriptor to asynchronously monitor the I/O readiness
|
||||
// of a file descriptor.
|
||||
class FdWatcher {
|
||||
public:
|
||||
virtual void OnFileCanReadWithoutBlocking(int fd) = 0;
|
||||
virtual void OnFileCanWriteWithoutBlocking(int fd) = 0;
|
||||
|
||||
protected:
|
||||
virtual ~FdWatcher() = default;
|
||||
};
|
||||
|
||||
class FdWatchControllerInterface {
|
||||
public:
|
||||
explicit FdWatchControllerInterface(const Location& from_here);
|
||||
// Subclasses must call StopWatchingFileDescriptor() in their destructor
|
||||
// (this parent class cannot generically do it for them as it must usually
|
||||
// be invoked before they destroy their state which happens before the
|
||||
// parent destructor is invoked).
|
||||
virtual ~FdWatchControllerInterface();
|
||||
|
||||
// NOTE: This method isn't called StopWatching() to avoid confusion with the
|
||||
// win32 ObjectWatcher class. While this doesn't really need to be virtual
|
||||
// as there's only one impl per platform and users don't use pointers to the
|
||||
// base class. Having this interface forces implementers to share similar
|
||||
// implementations (a problem in the past).
|
||||
|
||||
// Stop watching the FD, always safe to call. No-op if there's nothing to
|
||||
// do.
|
||||
virtual bool StopWatchingFileDescriptor() = 0;
|
||||
|
||||
const Location& created_from_location() const {
|
||||
return created_from_location_;
|
||||
}
|
||||
|
||||
private:
|
||||
const Location created_from_location_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FdWatchControllerInterface);
|
||||
};
|
||||
|
||||
enum Mode {
|
||||
WATCH_READ = 1 << 0,
|
||||
WATCH_WRITE = 1 << 1,
|
||||
WATCH_READ_WRITE = WATCH_READ | WATCH_WRITE
|
||||
};
|
||||
|
||||
// Every subclass of WatchableIOMessagePumpPosix must provide a
|
||||
// WatchFileDescriptor() which has the following signature where
|
||||
// |FdWatchController| must be the complete type based on
|
||||
// FdWatchControllerInterface.
|
||||
|
||||
// Registers |delegate| with the current thread's message loop so that its
|
||||
// methods are invoked when file descriptor |fd| becomes ready for reading or
|
||||
// writing (or both) without blocking. |mode| selects ready for reading, for
|
||||
// writing, or both. See "enum Mode" above. |controller| manages the
|
||||
// lifetime of registrations. ("Registrations" are also ambiguously called
|
||||
// "events" in many places, for instance in libevent.) It is an error to use
|
||||
// the same |controller| for different file descriptors; however, the same
|
||||
// controller can be reused to add registrations with a different |mode|. If
|
||||
// |controller| is already attached to one or more registrations, the new
|
||||
// registration is added onto those. If an error occurs while calling this
|
||||
// method, any registration previously attached to |controller| is removed.
|
||||
// Returns true on success. Must be called on the same thread the MessagePump
|
||||
// is running on.
|
||||
// bool WatchFileDescriptor(int fd,
|
||||
// bool persistent,
|
||||
// int mode,
|
||||
// FdWatchController* controller,
|
||||
// FdWatcher* delegate) = 0;
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_WATCHABLE_IO_MESSAGE_PUMP_POSIX_H_
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "base/message_loop/work_id_provider.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/no_destructor.h"
|
||||
#include "base/threading/thread_local.h"
|
||||
|
||||
namespace base {
|
||||
|
||||
// static
|
||||
WorkIdProvider* WorkIdProvider::GetForCurrentThread() {
|
||||
static NoDestructor<ThreadLocalOwnedPointer<WorkIdProvider>> instance;
|
||||
if (!instance->Get())
|
||||
instance->Set(WrapUnique(new WorkIdProvider));
|
||||
return instance->Get();
|
||||
}
|
||||
|
||||
// This function must support being invoked while other threads are suspended so
|
||||
// must not take any locks, including indirectly through use of heap allocation,
|
||||
// LOG, CHECK, or DCHECK.
|
||||
unsigned int WorkIdProvider::GetWorkId() {
|
||||
return work_id_.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
WorkIdProvider::~WorkIdProvider() = default;
|
||||
|
||||
void WorkIdProvider::SetCurrentWorkIdForTesting(unsigned int id) {
|
||||
work_id_.store(id, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
void WorkIdProvider::IncrementWorkIdForTesting() {
|
||||
IncrementWorkId();
|
||||
}
|
||||
|
||||
WorkIdProvider::WorkIdProvider() : work_id_(0) {}
|
||||
|
||||
void WorkIdProvider::IncrementWorkId() {
|
||||
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
|
||||
|
||||
unsigned int next_id = work_id_.load(std::memory_order_relaxed) + 1;
|
||||
// Reserve 0 to mean no work items have been executed.
|
||||
if (next_id == 0)
|
||||
++next_id;
|
||||
// Release order ensures this state is visible to other threads prior to the
|
||||
// following task/event execution.
|
||||
work_id_.store(next_id, std::memory_order_release);
|
||||
}
|
||||
|
||||
} // namespace base
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BASE_MESSAGE_LOOP_WORK_ID_PROVIDER_H_
|
||||
#define BASE_MESSAGE_LOOP_WORK_ID_PROVIDER_H_
|
||||
|
||||
#include <atomic>
|
||||
|
||||
#include "base/base_export.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
|
||||
namespace base {
|
||||
namespace sequence_manager {
|
||||
namespace internal {
|
||||
class ThreadControllerWithMessagePumpImpl;
|
||||
}
|
||||
} // namespace sequence_manager
|
||||
|
||||
// WorkIdProvider associates with the current thread (via TLS) an id state
|
||||
// reflecting the current work item being executed by the message loop. The item
|
||||
// is accessed lock-free from other threads to provide a snapshot of the
|
||||
// currently-executing work item.
|
||||
//
|
||||
// The expected user is the ThreadProfiler which samples the id along with the
|
||||
// thread's stack to identify cases where the same task spans multiple
|
||||
// samples. The state is stored in TLS rather than on the MessageLoop or the
|
||||
// ThreadProfiler because the lifetime relationship between the two classes
|
||||
// varies depending on which thread is being profiled, plus the fact that
|
||||
// MessageLoop doesn't have a well-defined creation point/owner on some threads.
|
||||
class BASE_EXPORT WorkIdProvider {
|
||||
public:
|
||||
// Returns the WorkIdProvider for the current thread. Allocates a
|
||||
// WorkIdProvider in TLS if not already present.
|
||||
static WorkIdProvider* GetForCurrentThread();
|
||||
|
||||
// Returns the work id for the thread to which this WorkIdProvider
|
||||
// belongs. The work id is 0 before calls to IncrementWorkId() and always
|
||||
// non-zero after that point. The implementation supports being invoked while
|
||||
// other threads are suspended, and thus is guaranteed to take no locks,
|
||||
// directly or indirectly. May be called from any thread, as long as the
|
||||
// owning thread hasn't been destroyed.
|
||||
unsigned int GetWorkId();
|
||||
|
||||
// Public to support unique_ptr<WorkIdProvider>.
|
||||
~WorkIdProvider();
|
||||
|
||||
void SetCurrentWorkIdForTesting(unsigned int id);
|
||||
void IncrementWorkIdForTesting();
|
||||
|
||||
WorkIdProvider(const WorkIdProvider&) = delete;
|
||||
WorkIdProvider& operator=(const WorkIdProvider&) = delete;
|
||||
|
||||
private:
|
||||
// Friended to allow use of IncrementWorkId().
|
||||
friend class sequence_manager::internal::ThreadControllerWithMessagePumpImpl;
|
||||
|
||||
WorkIdProvider();
|
||||
|
||||
void IncrementWorkId();
|
||||
|
||||
std::atomic_uint work_id_;
|
||||
|
||||
THREAD_CHECKER(thread_checker_);
|
||||
};
|
||||
|
||||
} // namespace base
|
||||
|
||||
#endif // BASE_MESSAGE_LOOP_WORK_ID_PROVIDER_H_
|
||||
Loading…
Add table
Add a link
Reference in a new issue