| /* |
| * Copyright (c) 2015, 2023, 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/g1ServiceThread.hpp" |
| #include "logging/log.hpp" |
| #include "runtime/mutexLocker.hpp" |
| #include "runtime/timer.hpp" |
| #include "runtime/os.hpp" |
| |
| G1SentinelTask::G1SentinelTask() : G1ServiceTask("Sentinel Task") { |
| set_time(max_jlong); |
| set_next(this); |
| } |
| |
| void G1SentinelTask::execute() { |
| guarantee(false, "Sentinel service task should never be executed."); |
| } |
| |
| G1ServiceThread::G1ServiceThread() : |
| ConcurrentGCThread(), |
| _monitor(Mutex::nosafepoint, "G1ServiceThread_lock"), |
| _task_queue() { |
| set_name("G1 Service"); |
| create_and_start(); |
| } |
| |
| void G1ServiceThread::register_task(G1ServiceTask* task, jlong delay_ms) { |
| guarantee(!task->is_registered(), "Task already registered"); |
| guarantee(task->next() == nullptr, "Task already in queue"); |
| |
| // Make sure the service thread is still up and running, there is a race |
| // during shutdown where the service thread has been stopped, but other |
| // GC threads might still be running and trying to add tasks. |
| if (has_terminated()) { |
| log_debug(gc, task)("G1 Service Thread (%s) (terminated)", task->name()); |
| return; |
| } |
| |
| log_debug(gc, task)("G1 Service Thread (%s) (register)", task->name()); |
| |
| // Associate the task with the service thread. |
| task->set_service_thread(this); |
| |
| // Schedule the task to run after the given delay. The service will be |
| // notified to check if this task is first in the queue. |
| schedule_task(task, delay_ms); |
| } |
| |
| void G1ServiceThread::schedule(G1ServiceTask* task, jlong delay_ms, bool notify) { |
| guarantee(task->is_registered(), "Must be registered before scheduled"); |
| guarantee(task->next() == nullptr, "Task already in queue"); |
| |
| // Schedule task by setting the task time and adding it to queue. |
| jlong delay = TimeHelper::millis_to_counter(delay_ms); |
| task->set_time(os::elapsed_counter() + delay); |
| |
| MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); |
| _task_queue.add_ordered(task); |
| if (notify) { |
| ml.notify(); |
| } |
| |
| log_trace(gc, task)("G1 Service Thread (%s) (schedule) @%1.3fs", |
| task->name(), TimeHelper::counter_to_seconds(task->time())); |
| } |
| |
| void G1ServiceThread::schedule_task(G1ServiceTask* task, jlong delay_ms) { |
| schedule(task, delay_ms, true /* notify */); |
| } |
| |
| G1ServiceTask* G1ServiceThread::wait_for_task() { |
| MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); |
| while (!should_terminate()) { |
| if (_task_queue.is_empty()) { |
| log_trace(gc, task)("G1 Service Thread (wait for new tasks)"); |
| ml.wait(); |
| } else { |
| G1ServiceTask* task = _task_queue.front(); |
| jlong scheduled = task->time(); |
| jlong now = os::elapsed_counter(); |
| if (scheduled <= now) { |
| _task_queue.remove_front(); |
| return task; |
| } else { |
| // Round up to try not to wake up early, and to avoid round down to |
| // zero (which has special meaning of wait forever) by conversion. |
| double delay = ceil(TimeHelper::counter_to_millis(scheduled - now)); |
| log_trace(gc, task)("G1 Service Thread (wait %1.3fs)", (delay / 1000.0)); |
| int64_t delay_ms = static_cast<int64_t>(delay); |
| assert(delay_ms > 0, "invariant"); |
| ml.wait(delay_ms); |
| } |
| } |
| } |
| return nullptr; // Return null when terminating. |
| } |
| |
| void G1ServiceThread::run_task(G1ServiceTask* task) { |
| jlong start = os::elapsed_counter(); |
| double vstart = os::elapsedVTime(); |
| |
| assert(task->time() <= start, |
| "task run early: " JLONG_FORMAT " > " JLONG_FORMAT, |
| task->time(), start); |
| log_debug(gc, task, start)("G1 Service Thread (%s) (run %1.3fms after schedule)", |
| task->name(), |
| TimeHelper::counter_to_millis(start - task->time())); |
| |
| task->execute(); |
| |
| log_debug(gc, task)("G1 Service Thread (%s) (run: %1.3fms) (cpu: %1.3fms)", |
| task->name(), |
| TimeHelper::counter_to_millis(os::elapsed_counter() - start), |
| (os::elapsedVTime() - vstart) * MILLIUNITS); |
| } |
| |
| void G1ServiceThread::run_service() { |
| while (true) { |
| G1ServiceTask* task = wait_for_task(); |
| if (task == nullptr) break; |
| run_task(task); |
| } |
| assert(should_terminate(), "invariant"); |
| log_debug(gc, task)("G1 Service Thread (stopping)"); |
| } |
| |
| void G1ServiceThread::stop_service() { |
| MonitorLocker ml(&_monitor, Mutex::_no_safepoint_check_flag); |
| ml.notify(); |
| } |
| |
| G1ServiceTask::G1ServiceTask(const char* name) : |
| _time(), |
| _name(name), |
| _next(nullptr), |
| _service_thread(nullptr) { } |
| |
| void G1ServiceTask::set_service_thread(G1ServiceThread* thread) { |
| _service_thread = thread; |
| } |
| |
| bool G1ServiceTask::is_registered() { |
| return _service_thread != nullptr; |
| } |
| |
| void G1ServiceTask::schedule(jlong delay_ms) { |
| assert(Thread::current() == _service_thread, |
| "Can only be used when already running on the service thread"); |
| // No need to notify, since we *are* the service thread. |
| _service_thread->schedule(this, delay_ms, false /* notify */); |
| } |
| |
| const char* G1ServiceTask::name() { |
| return _name; |
| } |
| |
| void G1ServiceTask::set_time(jlong time) { |
| assert(_next == nullptr, "Not allowed to update time while in queue"); |
| _time = time; |
| } |
| |
| jlong G1ServiceTask::time() { |
| return _time; |
| } |
| |
| void G1ServiceTask::set_next(G1ServiceTask* next) { |
| _next = next; |
| } |
| |
| G1ServiceTask* G1ServiceTask::next() { |
| return _next; |
| } |
| |
| G1ServiceTaskQueue::G1ServiceTaskQueue() : _sentinel() { } |
| |
| void G1ServiceTaskQueue::remove_front() { |
| verify_task_queue(); |
| |
| G1ServiceTask* task = _sentinel.next(); |
| _sentinel.set_next(task->next()); |
| task->set_next(nullptr); |
| } |
| |
| G1ServiceTask* G1ServiceTaskQueue::front() { |
| verify_task_queue(); |
| return _sentinel.next(); |
| } |
| |
| bool G1ServiceTaskQueue::is_empty() { |
| return &_sentinel == _sentinel.next(); |
| } |
| |
| void G1ServiceTaskQueue::add_ordered(G1ServiceTask* task) { |
| assert(task != nullptr, "not a valid task"); |
| assert(task->next() == nullptr, "invariant"); |
| assert(task->time() != max_jlong, "invalid time for task"); |
| |
| G1ServiceTask* current = &_sentinel; |
| while (task->time() >= current->next()->time()) { |
| assert(task != current, "Task should only be added once."); |
| current = current->next(); |
| } |
| |
| // Update the links. |
| task->set_next(current->next()); |
| current->set_next(task); |
| |
| verify_task_queue(); |
| } |
| |
| #ifdef ASSERT |
| void G1ServiceTaskQueue::verify_task_queue() { |
| G1ServiceTask* cur = _sentinel.next(); |
| |
| assert(cur != &_sentinel, "Should never try to verify empty queue"); |
| while (cur != &_sentinel) { |
| G1ServiceTask* next = cur->next(); |
| assert(cur->time() <= next->time(), |
| "Tasks out of order, prev: %s (%1.3fs), next: %s (%1.3fs)", |
| cur->name(), TimeHelper::counter_to_seconds(cur->time()), next->name(), TimeHelper::counter_to_seconds(next->time())); |
| |
| assert(cur != next, "Invariant"); |
| cur = next; |
| } |
| } |
| #endif |