// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package com.google.devtools.build.lib.server; import com.google.common.base.Preconditions; import com.google.devtools.build.lib.profiler.AutoProfiler; import com.google.devtools.build.lib.util.StringUtilities; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; import java.lang.management.MemoryUsage; import java.util.concurrent.Future; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Logger; /** * Run cleanup-related tasks during idle periods in the server. * idle() and busy() must be called in that order, and only once. */ class IdleServerTasks { private final ScheduledThreadPoolExecutor executor; private static final Logger logger = Logger.getLogger(IdleServerTasks.class.getName()); /** Must be called from the main thread. */ public IdleServerTasks() { this.executor = new ScheduledThreadPoolExecutor(1); } /** * Called when the server becomes idle. Should not block, but may invoke * new threads. */ public void idle() { Preconditions.checkState(!executor.isShutdown()); @SuppressWarnings("unused") Future possiblyIgnoredError = executor.schedule( () -> { MemoryMXBean memBean = ManagementFactory.getMemoryMXBean(); MemoryUsage before = memBean.getHeapMemoryUsage(); try (AutoProfiler p = AutoProfiler.logged("Idle GC", logger)) { System.gc(); } MemoryUsage after = memBean.getHeapMemoryUsage(); logger.info( String.format( "[Idle GC] used: %s -> %s, committed: %s -> %s", StringUtilities.prettyPrintBytes(before.getUsed()), StringUtilities.prettyPrintBytes(after.getUsed()), StringUtilities.prettyPrintBytes(before.getCommitted()), StringUtilities.prettyPrintBytes(after.getCommitted()))); }, 10, TimeUnit.SECONDS); } /** * Called by the main thread when the server gets to work. * Should return quickly. */ public void busy() { Preconditions.checkState(!executor.isShutdown()); // Make sure tasks are finished after shutdown(), so they do not intefere // with subsequent server invocations. executor.shutdown(); executor.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); boolean interrupted = false; while (true) { try { executor.awaitTermination(Long.MAX_VALUE, TimeUnit.HOURS); break; } catch (InterruptedException e) { // It's unsafe to leak threads - just reset the interrupt bit later. interrupted = true; } } if (interrupted) { Thread.currentThread().interrupt(); } } }