Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View 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

View 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_

View file

@ -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

View file

@ -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_

View file

@ -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

View 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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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_

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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_

View file

@ -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

View file

@ -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_

View file

@ -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

View file

@ -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_