Skip to content

Commit

Permalink
Allow starting daemon with lower priority
Browse files Browse the repository at this point in the history
  • Loading branch information
oehme committed Oct 16, 2018
1 parent 13e9a39 commit 662e81d
Show file tree
Hide file tree
Showing 23 changed files with 195 additions and 26 deletions.
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
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_.

`--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)));
return new DefaultDaemonContext(uid, new File(javaHome), new File(daemonRegistryDir), pid, idleTimeout, jvmOpts, priority);
} else {
return null;
}
Expand Down
@@ -0,0 +1,53 @@
/*
* 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

class DaemonPriorityIntegrationTest extends DaemonIntegrationSpec {

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", "/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();
}
Expand Up @@ -42,6 +42,7 @@ public class DaemonContextBuilder implements Factory<DaemonContext> {
private Integer idleTimeout;
private Locale locale = Locale.getDefault();
private List<String> daemonOpts = Lists.newArrayList();
private DaemonParameters.Priority priority;

public DaemonContextBuilder(ProcessEnvironment processEnvironment) {
javaHome = canonicalize(Jvm.current().getJavaHome());
Expand Down Expand Up @@ -104,9 +105,14 @@ public void setDaemonOpts(List<String> daemonOpts) {
this.daemonOpts = daemonOpts;
}

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

public void useDaemonParameters(DaemonParameters daemonParameters) {
setJavaHome(daemonParameters.getEffectiveJvm().getJavaHome());
setDaemonOpts(daemonParameters.getEffectiveJvmArgs());
setPriority(daemonParameters.getPriority());
}

/**
Expand All @@ -116,6 +122,6 @@ public DaemonContext create() {
if (daemonRegistryDir == null) {
throw new IllegalStateException("Registry dir must be specified.");
}
return new DefaultDaemonContext(uid, javaHome, daemonRegistryDir, pid, idleTimeout, daemonOpts);
return new DefaultDaemonContext(uid, javaHome, daemonRegistryDir, pid, idleTimeout, daemonOpts, priority);
}
}
}

0 comments on commit 662e81d

Please sign in to comment.