// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

#ifndef RUNTIME_VM_MESSAGE_HANDLER_H_
#define RUNTIME_VM_MESSAGE_HANDLER_H_

#include <memory>

#include "vm/isolate.h"
#include "vm/lockers.h"
#include "vm/message.h"
#include "vm/os_thread.h"
#include "vm/port.h"
#include "vm/port_set.h"
#include "vm/thread_pool.h"

namespace dart {

// A MessageHandler is an entity capable of accepting messages.
class MessageHandler : public PortHandler {
 protected:
  MessageHandler();

 public:
  enum MessageStatus {
    kOK,        // We successfully handled a message.
    kError,     // We encountered an error handling a message.
    kShutdown,  // The VM is shutting down.
  };
  static const char* MessageStatusString(MessageStatus status);

  virtual ~MessageHandler();

  typedef uword CallbackData;
  typedef void (*EndCallback)(CallbackData data);

  // Runs this message handler on the thread pool.
  //
  // A message handler will run until it terminates either normally or
  // abnormally.  Normal termination occurs when the message handler
  // no longer has any live ports.  Abnormal termination occurs when
  // HandleMessage() indicates that an error has occurred during
  // message processing.

  // Returns false if the handler failed to launch on the thread pool(due to
  // thread pool shutting down for example), otherwise it returns true.
  bool Run(ThreadPool* pool, EndCallback end_callback, CallbackData data);

  // Runs this message handler on current thread.
  //
  // A message handler will run until it terminates either normally or
  // abnormally.  Normal termination occurs when the message handler
  // no longer has any live ports.  Abnormal termination occurs when
  // HandleMessage() indicates that an error has occurred during
  // message processing.
  void RunSync();

  // Handles the next message for this message handler.  Should only
  // be used when not running the handler on the thread pool (via Run
  // or RunBlocking).
  //
  // Returns true on success.
  MessageStatus HandleNextMessage();

  // Handles any OOB messages for this message handler.  Can be used
  // even if the message handler is running on the thread pool.
  //
  // Returns true on success.
  MessageStatus HandleOOBMessages();

  // Returns true if there are pending OOB messages for this message
  // handler.
  bool HasOOBMessages();

#if defined(TESTING)
  std::unique_ptr<Message> StealOOBMessage();
#endif

  // Returns true if there are pending normal messages for this message
  // handler.
  bool HasMessages();

  struct MessageCount {
    intptr_t num_messages;
    intptr_t num_oob_messages;
  };

  MessageCount GetMessageCounts();

  // Whether to keep this message handler alive or whether it should shutdown.
  virtual bool KeepAliveLocked() { return true; }

  bool paused() const { return paused_ > 0; }

  void increment_paused() { paused_++; }
  void decrement_paused() {
    ASSERT(paused_ > 0);
    paused_--;
  }

#if !defined(PRODUCT)
  bool should_pause_on_start() const { return should_pause_on_start_; }

  void set_should_pause_on_start(bool should_pause_on_start) {
    should_pause_on_start_ = should_pause_on_start;
  }

  bool is_paused_on_start() const { return is_paused_on_start_; }

  bool should_pause_on_exit() const { return should_pause_on_exit_; }

  void set_should_pause_on_exit(bool should_pause_on_exit) {
    should_pause_on_exit_ = should_pause_on_exit;
  }

  bool is_paused_on_exit() const { return is_paused_on_exit_; }

  // Timestamp of the paused on start or paused on exit.
  int64_t paused_timestamp() const { return paused_timestamp_; }

  bool ShouldPauseOnStart(MessageStatus status) const;
  bool ShouldPauseOnExit(MessageStatus status) const;
  void PausedOnStart(bool paused);
  void PausedOnExit(bool paused);
#endif

 protected:
  // Custom message notification.  Optionally provided by subclass.
  virtual void MessageNotify(Message::Priority priority);

  // Handles a single message.  Provided by subclass.
  //
  // Returns true on success.
  virtual MessageStatus HandleMessage(std::unique_ptr<Message> message) = 0;

  virtual void NotifyPauseOnStart() {}
  virtual void NotifyPauseOnExit() {}

  // TODO(iposva): Set a local field before entering MessageHandler methods.
  Thread* thread() const { return Thread::Current(); }

  // Posts a message on this handler's message queue.
  // If before_events is true, then the message is enqueued before any pending
  // events, but after any pending isolate library events.
  void PostMessage(std::unique_ptr<Message> message,
                   bool before_events = false) override;

  virtual void set_is_scheduled() {}

 private:
  template <typename GCVisitorType>
  friend void MournFinalizerEntry(GCVisitorType*, FinalizerEntryPtr);
  friend class PortMap;
  friend class MessageHandlerTestPeer;
  friend class MessageHandlerTask;

  // ------------ START PortMap API ------------
  // These functions should only be called from the PortMap.
  // Implementaion of PortHandler API.

  const char* name() const override;

  void OnPortClosed(Dart_Port port) override;

  void Shutdown() override {
    // Nothing to do.
  }

  // Return Isolate to which this message handler corresponds to.
  Isolate* isolate() const override { return nullptr; }

  PortSet<PortSetEntry>* ports(PortMap::Locker& locker) override {
    return &ports_;
  }

  // Notifies this handler that all ports are being closed.
  void OnAllPortsClosed();
  // ------------ END PortMap API ------------

  // Called by MessageHandlerTask to process our task queue.
  void TaskCallback();

  // Checks if we have a slot for idle task execution, if we have a slot
  // for idle task execution it is scheduled immediately or we wait for
  // idle expiration and then attempt to schedule the idle task.
  // Returns true if their is scope for idle task execution so that we
  // can loop back to handle more messages or false if idle tasks are not
  // scheduled.
  bool CheckIfIdleLocked(MonitorLocker* ml);

  // Triggers a run of the idle task.
  void RunIdleTaskLocked(MonitorLocker* ml);

  // NOTE: These two functions release and reacquire the monitor, you may
  // need to call HandleMessages to ensure all pending messages are handled.
  void PausedOnStartLocked(MonitorLocker* ml, bool paused);
  void PausedOnExitLocked(MonitorLocker* ml, bool paused);

  // Dequeue the next message.  Prefer messages from the oob_queue_ to
  // messages from the queue_.
  std::unique_ptr<Message> DequeueMessage(Message::Priority min_priority);

  void ClearOOBQueue();

  // Handles any pending messages.
  MessageStatus HandleMessages(MonitorLocker* ml,
                               bool allow_normal_messages,
                               bool allow_multiple_normal_messages);

  Monitor monitor_;  // Protects all fields in MessageHandler.
  MessageQueue* queue_;
  MessageQueue* oob_queue_;

  // Only accessed by [PortMap], protected by [PortMap]s lock. See ports()
  // getter.
  PortSet<PortSetEntry> ports_;

  intptr_t paused_;  // The number of pause messages received.
#if !defined(PRODUCT)
  bool should_pause_on_start_;
  bool should_pause_on_exit_;
  bool is_paused_on_start_;
  bool is_paused_on_exit_;
  // When isolate gets paused, remember the status of the message being
  // processed so that we can resume correctly(into potentially not-OK status).
  MessageStatus remembered_paused_on_exit_status_;
  int64_t paused_timestamp_;
#endif
  bool task_running_;
  ThreadPool* pool_;
  EndCallback end_callback_;
  CallbackData callback_data_;

  DISALLOW_COPY_AND_ASSIGN(MessageHandler);
};

}  // namespace dart

#endif  // RUNTIME_VM_MESSAGE_HANDLER_H_
