/* * Copyright 2017-present Facebook, Inc. * * 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.facebook.buck.cli; import com.facebook.buck.httpserver.WebServer; import com.facebook.buck.log.Logger; import com.facebook.buck.rules.Cell; import com.facebook.buck.util.HumanReadableException; import java.io.IOException; import java.nio.file.Path; import java.util.Optional; import javax.annotation.Nullable; import javax.annotation.concurrent.ThreadSafe; /** * Guards access to the daemon instance behind cell configuration checks. Creates or re-creates the * daemon instance as necessary if the cell configuration changes. */ @ThreadSafe class DaemonLifecycleManager { private static final Logger LOG = Logger.get(DaemonLifecycleManager.class); @Nullable private volatile Daemon daemon; boolean hasDaemon() { return daemon != null; } /** Get or create Daemon. */ synchronized Daemon getDaemon(Cell rootCell) throws IOException { Path rootPath = rootCell.getFilesystem().getRootPath(); if (daemon == null) { LOG.debug("Starting up daemon for project root [%s]", rootPath); daemon = new Daemon(rootCell, Optional.empty()); } else { // Buck daemons cache build files within a single project root, changing to a different // project root is not supported and will likely result in incorrect builds. The buck and // buckd scripts attempt to enforce this, so a change in project root is an error that // should be reported rather than silently worked around by invalidating the cache and // creating a new daemon object. Path parserRoot = rootCell.getFilesystem().getRootPath(); if (!rootPath.equals(parserRoot)) { throw new HumanReadableException( String.format("Unsupported root path change from %s to %s", rootPath, parserRoot)); } // If Buck config or the AndroidDirectoryResolver has changed, invalidate the cache and // create a new daemon. if (!daemon.getRootCell().equals(rootCell)) { LOG.warn( "Shutting down and restarting daemon on config or directory resolver change (%s != %s)", daemon.getRootCell(), rootCell); Optional<WebServer> webServer; if (shouldReuseWebServer(rootCell)) { webServer = daemon.getWebServer(); LOG.info("Reusing web server"); } else { webServer = Optional.empty(); daemon.close(); } daemon = new Daemon(rootCell, webServer); } } return daemon; } /** Manually kill the daemon instance, used for testing. */ synchronized void resetDaemon() { if (daemon != null) { try { LOG.info("Closing daemon on reset request."); daemon.close(); } catch (IOException e) { // NOPMD // Swallow exceptions while closing daemon. } } daemon = null; } private boolean shouldReuseWebServer(Cell newCell) { if (newCell == null || daemon == null) { return false; } Optional<Integer> portFromOldConfig = Daemon.getValidWebServerPort(daemon.getRootCell().getBuckConfig()); Optional<Integer> portFromUpdatedConfig = Daemon.getValidWebServerPort(newCell.getBuckConfig()); return portFromOldConfig.equals(portFromUpdatedConfig); } }