// 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 "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 "build/build_config.h"

namespace base {

class MessageLoop;

// 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 around.
  MessageLoopCurrent(const MessageLoopCurrent& other) = default;
  MessageLoopCurrent& operator=(const MessageLoopCurrent& other) = default;

  // 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();

  // 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_; }

  // TODO(gab): Migrate the types of variables that store MessageLoop::current()
  // and remove this implicit cast back to MessageLoop*.
  operator MessageLoop*() 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::task_runner().
  // DEPRECATED(https://crbug.com/616447): Use ThreadTaskRunnerHandle::Get()
  // instead of MessageLoopCurrent::Get()->task_runner().
  const scoped_refptr<SingleThreadTaskRunner>& task_runner() const;

  // 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);

  // A TaskObserver is an object that receives task notifications from the
  // MessageLoop.
  //
  // NOTE: A TaskObserver implementation should be extremely fast!
  class BASE_EXPORT TaskObserver {
   public:
    // This method is called before processing a task.
    virtual void WillProcessTask(const PendingTask& pending_task) = 0;

    // This method is called after processing a task.
    virtual void DidProcessTask(const PendingTask& pending_task) = 0;

   protected:
    virtual ~TaskObserver() = default;
  };

  // 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);

  // 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:
    MessageLoop* const loop_;
    const bool old_state_;
  };

  // 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();

  // Binds |current| to the current thread. It will from then on be the
  // MessageLoop driven by MessageLoopCurrent on this thread. This is only meant
  // to be invoked by the MessageLoop itself.
  static void BindToCurrentThreadInternal(MessageLoop* current);

  // Unbinds |current| from the current thread. Must be invoked on the same
  // thread that invoked |BindToCurrentThreadInternal(current)|. This is only
  // meant to be invoked by the MessageLoop itself.
  static void UnbindFromCurrentThreadInternal(MessageLoop* current);

  // Returns true if |message_loop| is bound to MessageLoopCurrent on the
  // current thread. This is only meant to be invoked by the MessageLoop itself.
  static bool IsBoundToCurrentThreadInternal(MessageLoop* message_loop);

 protected:
  explicit MessageLoopCurrent(MessageLoop* current) : current_(current) {}

  MessageLoop* const current_;
};

#if !defined(OS_NACL) && !defined(OS_ANDROID)

// 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)
  // Please see MessagePumpLibevent for definition.
  static_assert(std::is_same<MessagePumpForUI, MessagePumpLibevent>::value,
                "MessageLoopCurrentForUI::WatchFileDescriptor is not supported "
                "when MessagePumpForUI is not a MessagePumpLibevent.");
  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

 private:
  MessageLoopCurrentForUI(MessageLoop* current, MessagePumpForUI* pump)
      : MessageLoopCurrent(current), pump_(pump) {
    DCHECK(pump_);
  }

  MessagePumpForUI* const pump_;
};

#endif  // !defined(OS_NACL) && !defined(OS_ANDROID)

// 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_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:
  MessageLoopCurrentForIO(MessageLoop* current, MessagePumpForIO* pump)
      : MessageLoopCurrent(current), pump_(pump) {
    DCHECK(pump_);
  }

  MessagePumpForIO* const pump_;
};

}  // namespace base

#endif  // BASE_MESSAGE_LOOP_MESSAGE_LOOP_CURRENT_H_
