/*
 * Copyright (c) 2001, 2022, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 *
 */

#include "precompiled.hpp"
#include "gc/g1/g1BarrierSet.hpp"
#include "gc/g1/g1ConcurrentRefine.hpp"
#include "gc/g1/g1ConcurrentRefineStats.hpp"
#include "gc/g1/g1ConcurrentRefineThread.hpp"
#include "gc/g1/g1DirtyCardQueue.hpp"
#include "gc/shared/suspendibleThreadSet.hpp"
#include "logging/log.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "runtime/thread.hpp"
#include "utilities/debug.hpp"
#include "utilities/formatBuffer.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/ticks.hpp"

G1ConcurrentRefineThread::G1ConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) :
  ConcurrentGCThread(),
  _vtime_start(0.0),
  _vtime_accum(0.0),
  _notifier(Mutex::nosafepoint, FormatBuffer<>("G1 Refine#%d", worker_id), true),
  _requested_active(false),
  _refinement_stats(),
  _worker_id(worker_id),
  _cr(cr)
{
  // set name
  set_name("G1 Refine#%d", worker_id);
}

void G1ConcurrentRefineThread::run_service() {
  _vtime_start = os::elapsedVTime();

  while (wait_for_completed_buffers()) {
    SuspendibleThreadSetJoiner sts_join;
    G1ConcurrentRefineStats active_stats_start = _refinement_stats;
    report_active("Activated");
    while (!should_terminate()) {
      if (sts_join.should_yield()) {
        report_inactive("Paused", _refinement_stats - active_stats_start);
        sts_join.yield();
        // Reset after yield rather than accumulating across yields, else a
        // very long running thread could overflow.
        active_stats_start = _refinement_stats;
        report_active("Resumed");
      } else if (maybe_deactivate()) {
        break;
      } else {
        do_refinement_step();
      }
    }
    report_inactive("Deactivated", _refinement_stats - active_stats_start);
    if (os::supports_vtime()) {
      _vtime_accum = (os::elapsedVTime() - _vtime_start);
    } else {
      _vtime_accum = 0.0;
    }
  }

  log_debug(gc, refine)("Stopping %d", _worker_id);
}

void G1ConcurrentRefineThread::report_active(const char* reason) const {
  log_trace(gc, refine)("%s worker %u, current: %zu",
                        reason,
                        _worker_id,
                        G1BarrierSet::dirty_card_queue_set().num_cards());
}

void G1ConcurrentRefineThread::report_inactive(const char* reason,
                                               const G1ConcurrentRefineStats& stats) const {
  log_trace(gc, refine)
           ("%s worker %u, cards: %zu, refined %zu, rate %1.2fc/ms",
            reason,
            _worker_id,
            G1BarrierSet::dirty_card_queue_set().num_cards(),
            stats.refined_cards(),
            stats.refinement_rate_ms());
}

void G1ConcurrentRefineThread::activate() {
  assert(this != Thread::current(), "precondition");
  MonitorLocker ml(&_notifier, Mutex::_no_safepoint_check_flag);
  if (!_requested_active || should_terminate()) {
    _requested_active = true;
    ml.notify();
  }
}

bool G1ConcurrentRefineThread::maybe_deactivate() {
  assert(this == Thread::current(), "precondition");
  if (cr()->is_thread_wanted(_worker_id)) {
    return false;
  } else {
    MutexLocker ml(&_notifier, Mutex::_no_safepoint_check_flag);
    bool requested = _requested_active;
    _requested_active = false;
    return !requested;  // Deactivate only if not recently requested active.
  }
}

bool G1ConcurrentRefineThread::try_refinement_step(size_t stop_at) {
  assert(this == Thread::current(), "precondition");
  return _cr->try_refinement_step(_worker_id, stop_at, &_refinement_stats);
}

void G1ConcurrentRefineThread::stop_service() {
  activate();
}

// The (single) primary thread drives the controller for the refinement threads.
class G1PrimaryConcurrentRefineThread final : public G1ConcurrentRefineThread {
  bool wait_for_completed_buffers() override;
  bool maybe_deactivate() override;
  void do_refinement_step() override;

public:
  G1PrimaryConcurrentRefineThread(G1ConcurrentRefine* cr) :
    G1ConcurrentRefineThread(cr, 0)
  {}
};

// When inactive, the primary thread periodically wakes up and requests
// adjustment of the number of active refinement threads.
bool G1PrimaryConcurrentRefineThread::wait_for_completed_buffers() {
  assert(this == Thread::current(), "precondition");
  MonitorLocker ml(notifier(), Mutex::_no_safepoint_check_flag);
  if (!requested_active() && !should_terminate()) {
    // Rather than trying to be smart about spurious wakeups, we just treat
    // them as timeouts.
    ml.wait(cr()->adjust_threads_wait_ms());
  }
  // Record adjustment needed whenever reactivating.
  cr()->record_thread_adjustment_needed();
  return !should_terminate();
}

bool G1PrimaryConcurrentRefineThread::maybe_deactivate() {
  // Don't deactivate while needing to adjust the number of active threads.
  return !cr()->is_thread_adjustment_needed() &&
         G1ConcurrentRefineThread::maybe_deactivate();
}

void G1PrimaryConcurrentRefineThread::do_refinement_step() {
  // Try adjustment first.  If it succeeds then don't do any refinement this
  // round.  This thread may have just woken up but no threads are currently
  // needed, which is common.  In this case we want to just go back to
  // waiting, with a minimum of fuss; in particular, don't do any "premature"
  // refinement.  However, adjustment may be pending but temporarily
  // blocked. In that case we *do* try refinement, rather than possibly
  // uselessly spinning while waiting for adjustment to succeed.
  if (!cr()->adjust_threads_periodically()) {
    // No adjustment, so try refinement, with the target as a cuttoff.
    if (!try_refinement_step(cr()->pending_cards_target())) {
      // Refinement was cut off, so proceed with fewer threads.
      cr()->reduce_threads_wanted();
    }
  }
}

class G1SecondaryConcurrentRefineThread final : public G1ConcurrentRefineThread {
  bool wait_for_completed_buffers() override;
  void do_refinement_step() override;

public:
  G1SecondaryConcurrentRefineThread(G1ConcurrentRefine* cr, uint worker_id) :
    G1ConcurrentRefineThread(cr, worker_id)
  {
    assert(worker_id > 0, "precondition");
  }
};

bool G1SecondaryConcurrentRefineThread::wait_for_completed_buffers() {
  assert(this == Thread::current(), "precondition");
  MonitorLocker ml(notifier(), Mutex::_no_safepoint_check_flag);
  while (!requested_active() && !should_terminate()) {
    ml.wait();
  }
  return !should_terminate();
}

void G1SecondaryConcurrentRefineThread::do_refinement_step() {
  assert(this == Thread::current(), "precondition");
  // Secondary threads ignore the target and just drive the number of pending
  // dirty cards down.  The primary thread is responsible for noticing the
  // target has been reached and reducing the number of wanted threads.  This
  // makes the control of wanted threads all under the primary, while avoiding
  // useless spinning by secondary threads until the primary thread notices.
  // (Useless spinning is still possible if there are no pending cards, but
  // that should rarely happen.)
  try_refinement_step(0);
}

G1ConcurrentRefineThread*
G1ConcurrentRefineThread::create(G1ConcurrentRefine* cr, uint worker_id) {
  G1ConcurrentRefineThread* crt;
  if (worker_id == 0) {
    crt = new (std::nothrow) G1PrimaryConcurrentRefineThread(cr);
  } else {
    crt = new (std::nothrow) G1SecondaryConcurrentRefineThread(cr, worker_id);
  }
  if (crt != nullptr) {
    crt->create_and_start();
  }
  return crt;
}
