// 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 "util/msg_loop.h"

#include "base/logging.h"

namespace {

#if !defined(OS_ZOS)
thread_local MsgLoop* g_current;
#else
// TODO(gabylb) - zos: thread_local not yet supported, use zoslib's impl'n:
__tlssim<MsgLoop*> __g_current_impl(nullptr);
#define g_current (*__g_current_impl.access())
#endif
}  // namespace

MsgLoop::MsgLoop() {
  DCHECK(g_current == nullptr);
  g_current = this;
}

MsgLoop::~MsgLoop() {
  DCHECK(g_current == this);
  g_current = nullptr;
}

void MsgLoop::Run() {
  should_quit_ = false;

  while (!should_quit_) {
    std::function<void()> task;
    {
      std::unique_lock<std::mutex> queue_lock(queue_mutex_);
      notifier_.wait(queue_lock, [this]() {
        return (!task_queue_.empty()) || should_quit_;
      });

      if (should_quit_)
        return;

      task = std::move(task_queue_.front());
      task_queue_.pop();
    }

    task();
  }
}

void MsgLoop::PostQuit() {
  PostTask([this]() { should_quit_ = true; });
}

void MsgLoop::PostTask(std::function<void()> work) {
  {
    std::unique_lock<std::mutex> queue_lock(queue_mutex_);
    task_queue_.emplace(std::move(work));
  }

  notifier_.notify_one();
}

void MsgLoop::RunUntilIdleForTesting() {
  for (bool done = false; !done;) {
    std::function<void()> task;
    {
      std::unique_lock<std::mutex> queue_lock(queue_mutex_);
      task = std::move(task_queue_.front());
      task_queue_.pop();

      if (task_queue_.empty())
        done = true;
    }

    task();
  }
}

MsgLoop* MsgLoop::Current() {
  return g_current;
}
