// 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 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()); // Do a GC cycle while the server is idle. @SuppressWarnings("unused") Future possiblyIgnoredError = executor.schedule( () -> { try (AutoProfiler p = AutoProfiler.logged("Idle GC", logger)) { System.gc(); } }, 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(); } } }