// Copyright (c) 2012, 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.

#include "vm/native_message_handler.h"

#include <memory>
#include <utility>

#include "vm/dart_api_message.h"
#include "vm/isolate.h"
#include "vm/message.h"
#include "vm/message_snapshot.h"
#include "vm/snapshot.h"

namespace dart {

Monitor* NativeMessageHandler::monitor_ = nullptr;
intptr_t NativeMessageHandler::pending_deletions_ = 0;

NativeMessageHandler::NativeMessageHandler(const char* name,
                                           Dart_NativeMessageHandler func,
                                           intptr_t max_concurrency)
    : name_(Utils::StrDup(name)), func_(func), pool_(max_concurrency) {}

NativeMessageHandler::~NativeMessageHandler() {}

namespace {
class HandleMessage : public ThreadPool::Task {
 public:
  HandleMessage(Dart_NativeMessageHandler handler,
                std::unique_ptr<Message> message)
      : handler_(handler), message_(std::move(message)) {
    ASSERT(handler != nullptr);
  }

  virtual void Run() {
    ApiNativeScope scope;
    Dart_CObject* object = ReadApiMessage(scope.zone(), message_.get());
    handler_(message_->dest_port(), object);
  }

 private:
  Dart_NativeMessageHandler handler_;
  std::unique_ptr<Message> message_;

  DISALLOW_COPY_AND_ASSIGN(HandleMessage);
};
}  // namespace

void NativeMessageHandler::PostMessage(std::unique_ptr<Message> message,
                                       bool before_events /* = false */) {
  if (message->IsOOB()) {
    UNREACHABLE();
  }

  pool_.Run<HandleMessage>(func_, std::move(message));
}

void NativeMessageHandler::RequestDeletion(NativeMessageHandler* handler) {
  {
    MonitorLocker ml(monitor_);
    pending_deletions_++;
  }

  ThreadPool::RequestShutdown(&handler->pool_, [handler]() {
    delete handler;

    // Once the handler and its pool is gone make sure to wake up
    // |NativeMessageHandler::Cleanup| which might be waiting.
    {
      MonitorLocker ml(monitor_);
      pending_deletions_--;
      if (pending_deletions_ == 0) {
        ml.Notify();
      }
    }
  });
}

void NativeMessageHandler::Shutdown() {
  pool_.Shutdown();
}

void NativeMessageHandler::Init() {
  monitor_ = new Monitor();
}

void NativeMessageHandler::Cleanup() {
  {
    MonitorLocker ml(monitor_);
    // By the time we get here we don't really expect new deletions to be
    // requested. We proceed with VM shutdown once we have no pending deletions.
    // In other words words don't try to guard against a race between
    // |Dart_CloseNativePort| and |Dart_Cleanup| - that's considered an API
    // misuse.
    while (pending_deletions_ > 0) {
      ml.Wait();
    }
  }
  delete monitor_;
  monitor_ = nullptr;
}

}  // namespace dart
