Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow starting daemon with lower priority #7403

Merged
merged 1 commit into from Oct 18, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 5 additions & 0 deletions subprojects/docs/src/docs/release/notes.md
Expand Up @@ -70,6 +70,11 @@ Here are the new features introduced in this Gradle release.
IMPORTANT: if this is a patch release, ensure that a prominent link is included in the foreword to all releases of the same minor stream.
Add-->

### Starting Gradle as a lower priority process

You can now use the `--priority low` command line argument or `org.gradle.priority=low` property to start Gradle as a low priority process.
This ensures that other applications like your IDE or browser stay responsive, even while a very demanding build is running.

### Build init plugin improvements

This release includes a number of improvements to The [Build Init plugin](userguide/build_init_plugin.html).
Expand Down
2 changes: 2 additions & 0 deletions subprojects/docs/src/docs/userguide/build_environment.adoc
Expand Up @@ -66,6 +66,8 @@ When configured, Gradle will fork up to `org.gradle.workers.max` JVMs to execute
When set to `all`, `summary` or `none`, Gradle will use different warning type display. See <<command_line_interface.adoc#sec:command_line_logging,Command-line logging options>> for details.
`org.gradle.workers.max=(max # of worker processes)`::
When configured, Gradle will use a maximum of the given number of workers. Default is number of CPU processors. See also <<command_line_interface.adoc#sec:command_line_performance, performance command-line options>>.
`org.gradle.priority=(low,normal)`::
Specifies the scheduling priority for the Gradle daemon and all processes launched by it. Default is `normal`. See also <<command_line_interface.adoc#sec:command_line_performance, performance command-line options>>.

The following example demonstrates usage of various properties.

Expand Down
Expand Up @@ -340,6 +340,9 @@ Sets maximum number of workers that Gradle may use. _Default is number of proces
`--parallel`, `--no-parallel`::
Build projects in parallel. For limitations of this option, see <<multi_project_builds.adoc#sec:parallel_execution, Parallel Project Execution>>. _Default is off_.

`--priority`::
Specifies the scheduling priority for the Gradle daemon and all processes launched by it. Values are `normal` or `low`. _Default is normal_.
marcphilipp marked this conversation as resolved.
Show resolved Hide resolved

`--profile`::
Generates a high-level performance report in the `$buildDir/reports/profile` directory. `--scan` is preferred.

Expand Down
Expand Up @@ -18,6 +18,7 @@

import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import org.gradle.launcher.daemon.configuration.DaemonParameters;
import org.gradle.launcher.daemon.context.DaemonContext;
import org.gradle.launcher.daemon.context.DefaultDaemonContext;

Expand Down Expand Up @@ -57,7 +58,7 @@ public static DaemonContext parseFromString(String source) {
}

private static DaemonContext parseFrom(String source) {
Pattern pattern = Pattern.compile("^.*DefaultDaemonContext\\[(uid=[^\\n,]+)?,?javaHome=([^\\n]+),daemonRegistryDir=([^\\n]+),pid=([^\\n]+),idleTimeout=(.+?),daemonOpts=([^\\n]+)].*",
Pattern pattern = Pattern.compile("^.*DefaultDaemonContext\\[(uid=[^\\n,]+)?,?javaHome=([^\\n]+),daemonRegistryDir=([^\\n]+),pid=([^\\n]+),idleTimeout=(.+?)(,priority=[^\\n]+)?,daemonOpts=([^\\n]+)].*",
Pattern.MULTILINE + Pattern.DOTALL);
Matcher matcher = pattern.matcher(source);

Expand All @@ -68,8 +69,9 @@ private static DaemonContext parseFrom(String source) {
String pidStr = matcher.group(4);
Long pid = pidStr.equals("null") ? null : Long.parseLong(pidStr);
Integer idleTimeout = Integer.decode(matcher.group(5));
List<String> jvmOpts = Lists.newArrayList(Splitter.on(',').split(matcher.group(6)));
return new DefaultDaemonContext(uid, new File(javaHome), new File(daemonRegistryDir), pid, idleTimeout, jvmOpts);
DaemonParameters.Priority priority = matcher.group(6) == null ? DaemonParameters.Priority.NORMAL : DaemonParameters.Priority.valueOf(matcher.group(6).substring(",priority=".length()));
List<String> jvmOpts = Lists.newArrayList(Splitter.on(',').split(matcher.group(7)));
marcphilipp marked this conversation as resolved.
Show resolved Hide resolved
return new DefaultDaemonContext(uid, new File(javaHome), new File(daemonRegistryDir), pid, idleTimeout, jvmOpts, priority);
} else {
return null;
}
Expand Down
@@ -0,0 +1,65 @@
/*
* Copyright 2018 the original author or authors.
*
* 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 org.gradle.launcher.daemon

import org.gradle.integtests.fixtures.daemon.DaemonIntegrationSpec
import org.gradle.internal.os.OperatingSystem

class DaemonPriorityIntegrationTest extends DaemonIntegrationSpec {

/*
* Our command line length is already close to the limit of what Windows can handle.
* The few extra arguments that the priority change requires seem to push it over the edge.
* Using a distribution drastically shrinks the command line length, as the classpath is only
* one jar in that case.
*/
def setup() {
if (OperatingSystem.current().isWindows()) {
requireGradleDistribution()
}
}

def "forks new daemon when priority is set to a different value via command line"() {
when:
run("help")

then:
daemons.daemons.size() == 1

when:
executer.withArguments("--priority", "low")
run("help")

then:
daemons.daemons.size() == 2
}

def "forks new daemon when priority is set to a different value via properties"() {
when:
run("help")

then:
daemons.daemons.size() == 1

when:
file("gradle.properties") << "org.gradle.priority=low"
run("help")

then:
daemons.daemons.size() == 2
}
}
Expand Up @@ -31,6 +31,7 @@
import org.gradle.internal.service.scopes.GradleUserHomeScopeServiceRegistry;
import org.gradle.launcher.bootstrap.EntryPoint;
import org.gradle.launcher.bootstrap.ExecutionListener;
import org.gradle.launcher.daemon.configuration.DaemonParameters;
import org.gradle.launcher.daemon.configuration.DaemonServerConfiguration;
import org.gradle.launcher.daemon.configuration.DefaultDaemonServerConfiguration;
import org.gradle.launcher.daemon.context.DaemonContext;
Expand Down Expand Up @@ -79,6 +80,7 @@ protected void doAction(String[] args, ExecutionListener listener) {
int periodicCheckIntervalMs;
boolean singleUse;
String daemonUid;
DaemonParameters.Priority priority;
List<File> additionalClassPath;

KryoBackedDecoder decoder = new KryoBackedDecoder(new EncodedStream.EncodedInput(System.in));
Expand All @@ -89,6 +91,7 @@ protected void doAction(String[] args, ExecutionListener listener) {
periodicCheckIntervalMs = decoder.readSmallInt();
singleUse = decoder.readBoolean();
daemonUid = decoder.readString();
priority = DaemonParameters.Priority.values()[decoder.readSmallInt()];
int argCount = decoder.readSmallInt();
startupOpts = new ArrayList<String>(argCount);
for (int i = 0; i < argCount; i++) {
Expand All @@ -104,7 +107,7 @@ protected void doAction(String[] args, ExecutionListener listener) {
}

NativeServices.initialize(gradleHomeDir);
DaemonServerConfiguration parameters = new DefaultDaemonServerConfiguration(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, singleUse, startupOpts);
DaemonServerConfiguration parameters = new DefaultDaemonServerConfiguration(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, singleUse, priority, startupOpts);
LoggingServiceRegistry loggingRegistry = LoggingServiceRegistry.newCommandLineProcessLogging();
LoggingManagerInternal loggingManager = loggingRegistry.newInstance(LoggingManagerInternal.class);

Expand Down
Expand Up @@ -27,6 +27,7 @@
import org.gradle.internal.installation.CurrentGradleInstallation;
import org.gradle.internal.installation.GradleInstallation;
import org.gradle.internal.io.StreamByteBuffer;
import org.gradle.internal.os.OperatingSystem;
import org.gradle.internal.serialize.FlushableEncoder;
import org.gradle.internal.serialize.kryo.KryoBackedEncoder;
import org.gradle.internal.time.Time;
Expand All @@ -48,6 +49,7 @@
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
Expand Down Expand Up @@ -93,6 +95,7 @@ public DaemonStartupInfo startDaemon(boolean singleUse) {
versionValidator.validate(daemonParameters);

List<String> daemonArgs = new ArrayList<String>();
daemonArgs.addAll(getPriorityArgs(daemonParameters.getPriority()));
daemonArgs.add(daemonParameters.getEffectiveJvm().getJavaExecutable().getAbsolutePath());

List<String> daemonOpts = daemonParameters.getEffectiveJvmArgs();
Expand All @@ -119,6 +122,7 @@ public DaemonStartupInfo startDaemon(boolean singleUse) {
encoder.writeSmallInt(daemonParameters.getPeriodicCheckInterval());
encoder.writeBoolean(singleUse);
encoder.writeString(daemonUid);
encoder.writeSmallInt(daemonParameters.getPriority().ordinal());
encoder.writeSmallInt(daemonOpts.size());
for (String daemonOpt : daemonOpts) {
encoder.writeString(daemonOpt);
Expand All @@ -136,6 +140,20 @@ public DaemonStartupInfo startDaemon(boolean singleUse) {
return startProcess(daemonArgs, daemonDir.getVersionedDir(), stdInput);
}

private List<String> getPriorityArgs(DaemonParameters.Priority priority) {
if (priority == DaemonParameters.Priority.NORMAL) {
return Collections.emptyList();
}
OperatingSystem os = OperatingSystem.current();
if (os.isUnix()) {
return Arrays.asList("nice", "-n", "10");
} else if (os.isWindows()) {
return Arrays.asList("cmd", "/C", "start", "\"Gradle build daemon\"", "/B", "/belownormal", "/WAIT");
} else {
return Collections.emptyList();
}
}

private DaemonStartupInfo startProcess(List<String> args, File workingDir, InputStream stdInput) {
LOGGER.debug("Starting daemon process: workingDir = {}, daemonArgs: {}", workingDir, args);
Timer clock = Time.startTimer();
Expand Down
Expand Up @@ -32,6 +32,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;

public class DaemonBuildOptions {

Expand All @@ -49,6 +50,7 @@ public class DaemonBuildOptions {
options.add(new ForegroundOption());
options.add(new StopOption());
options.add(new StatusOption());
options.add(new PriorityOption());
DaemonBuildOptions.options = Collections.unmodifiableList(options);
}

Expand Down Expand Up @@ -201,4 +203,21 @@ public void applyTo(DaemonParameters settings, Origin origin) {
settings.setStatus(true);
}
}

public static class PriorityOption extends StringBuildOption<DaemonParameters> {
public static final String GRADLE_PROPERTY = "org.gradle.priority";

public PriorityOption() {
super(GRADLE_PROPERTY, CommandLineOptionConfiguration.create("priority", "Specifies the scheduling priority for the Gradle daemon and all processes launched by it. Values are 'normal' (default) or 'low'").incubating());
}

@Override
public void applyTo(String value, DaemonParameters settings, Origin origin) {
try {
settings.setPriority(DaemonParameters.Priority.valueOf(value.toUpperCase(Locale.ROOT)));
} catch (IllegalArgumentException e) {
origin.handleInvalidValue(value);
}
}
}
}
Expand Up @@ -53,6 +53,7 @@ public class DaemonParameters {
private boolean foreground;
private boolean stop;
private boolean status;
private Priority priority = Priority.NORMAL;
private JavaInfo jvm = Jvm.current();

public DaemonParameters(BuildLayoutParameters layout) {
Expand Down Expand Up @@ -207,4 +208,17 @@ public void setStatus(boolean status) {
public Map<String, String> getEnvironmentVariables() {
return envVariables;
}

public Priority getPriority() {
return priority;
}

public void setPriority(Priority priority) {
this.priority = priority;
}

public enum Priority {
LOW,
NORMAL,
}
}
Expand Up @@ -31,5 +31,7 @@ public interface DaemonServerConfiguration {

List<String> getJvmOptions();

DaemonParameters.Priority getPriority();

boolean isSingleUse();
}
Expand Up @@ -26,14 +26,16 @@ public class DefaultDaemonServerConfiguration implements DaemonServerConfigurati
private final int idleTimeoutMs;
private final int periodicCheckIntervalMs;
private final boolean singleUse;
private final DaemonParameters.Priority priority;
private final List<String> jvmOptions;

public DefaultDaemonServerConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs, boolean singleUse, List<String> jvmOptions) {
public DefaultDaemonServerConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs, boolean singleUse, DaemonParameters.Priority priority, List<String> jvmOptions) {
this.daemonUid = daemonUid;
this.daemonBaseDir = daemonBaseDir;
this.idleTimeoutMs = idleTimeoutMs;
this.periodicCheckIntervalMs = periodicCheckIntervalMs;
this.singleUse = singleUse;
this.priority = priority;
this.jvmOptions = jvmOptions;
}

Expand All @@ -57,6 +59,10 @@ public String getUid() {
return daemonUid;
}

public DaemonParameters.Priority getPriority() {
return priority;
}

@Override
public List<String> getJvmOptions() {
return jvmOptions;
Expand Down
Expand Up @@ -23,6 +23,6 @@
public class ForegroundDaemonConfiguration extends DefaultDaemonServerConfiguration {
public ForegroundDaemonConfiguration(String daemonUid, File daemonBaseDir, int idleTimeoutMs, int periodicCheckIntervalMs) {
// Foreground daemon cannot be 'told' what's his startup options as the client sits in the same process so we will infer the jvm opts from the inputArguments()
super(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, false, new CurrentProcess().getJvmOptions().getAllImmutableJvmArgs());
super(daemonUid, daemonBaseDir, idleTimeoutMs, periodicCheckIntervalMs, false, DaemonParameters.Priority.NORMAL, new CurrentProcess().getJvmOptions().getAllImmutableJvmArgs());
}
}
Expand Up @@ -36,26 +36,32 @@ public String whyUnsatisfied(DaemonContext context) {
return "Java home is different.\n" + description(context);
} else if (!daemonOptsMatch(context)) {
return "At least one daemon option is different.\n" + description(context);
} else if (!priorityMatches(context)) {
return "Process priority is different.\n" + description(context);
}
return null;
}

private String description(DaemonContext context) {
return "Wanted: " + this + "\n"
+ "Actual: " + context + "\n";
+ "Actual: " + context + "\n";
}

private boolean daemonOptsMatch(DaemonContext potentialContext) {
return potentialContext.getDaemonOpts().containsAll(desiredContext.getDaemonOpts())
&& potentialContext.getDaemonOpts().size() == desiredContext.getDaemonOpts().size();
&& potentialContext.getDaemonOpts().size() == desiredContext.getDaemonOpts().size();
}

private boolean javaHomeMatches(DaemonContext potentialContext) {
return canonicalize(potentialContext.getJavaHome()).equals(canonicalize(desiredContext.getJavaHome()));
}

private boolean priorityMatches(DaemonContext context) {
return desiredContext.getPriority() == context.getPriority();
}

@Override
public String toString() {
return desiredContext.toString();
}
}
}
Expand Up @@ -15,6 +15,8 @@
*/
package org.gradle.launcher.daemon.context;

import org.gradle.launcher.daemon.configuration.DaemonParameters;

import java.io.File;
import java.io.Serializable;
import java.util.List;
Expand Down Expand Up @@ -65,4 +67,6 @@ public interface DaemonContext extends Serializable {
* @return the JVM options that the daemon was started with
*/
List<String> getDaemonOpts();
}

DaemonParameters.Priority getPriority();
}