提交 70db23bf 编写于 作者: K kohsuke

Merged revisions 19919,19998,20022 via svnmerge from

https://www.dev.java.net/svn/hudson/branches/matrix-parent

........
  r19919 | kohsuke | 2009-07-18 18:45:59 -0700 (Sat, 18 Jul 2009) | 1 line
  
  committing the work in progress.
........
  r19998 | kohsuke | 2009-07-20 18:58:15 -0700 (Mon, 20 Jul 2009) | 1 line
  
  [HUDSON-936] I believe this should do.
........
  r20022 | kohsuke | 2009-07-21 09:48:03 -0700 (Tue, 21 Jul 2009) | 1 line
  
  adding a switch to enable this selectively, so that we can give this feature a longer soak time.
........


git-svn-id: https://hudson.dev.java.net/svn/hudson/trunk/hudson/main@20025 71c3de6d-444a-0410-be80-ed276b4c234a
上级 c7de2266
...@@ -44,6 +44,7 @@ import hudson.model.SCMedItem; ...@@ -44,6 +44,7 @@ import hudson.model.SCMedItem;
import hudson.model.Saveable; import hudson.model.Saveable;
import hudson.model.TopLevelItem; import hudson.model.TopLevelItem;
import hudson.model.TopLevelItemDescriptor; import hudson.model.TopLevelItemDescriptor;
import hudson.model.Queue.FlyweightTask;
import hudson.model.Descriptor.FormException; import hudson.model.Descriptor.FormException;
import hudson.tasks.BuildStep; import hudson.tasks.BuildStep;
import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepDescriptor;
...@@ -86,7 +87,7 @@ import org.kohsuke.stapler.HttpResponse; ...@@ -86,7 +87,7 @@ import org.kohsuke.stapler.HttpResponse;
* *
* @author Kohsuke Kawaguchi * @author Kohsuke Kawaguchi
*/ */
public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> implements TopLevelItem, SCMedItem, ItemGroup<MatrixConfiguration>, Saveable { public class MatrixProject extends AbstractProject<MatrixProject,MatrixBuild> implements TopLevelItem, SCMedItem, ItemGroup<MatrixConfiguration>, Saveable, FlyweightTask {
/** /**
* Other configuration axes. * Other configuration axes.
* *
......
...@@ -98,6 +98,8 @@ import java.net.Inet4Address; ...@@ -98,6 +98,8 @@ import java.net.Inet4Address;
public /*transient*/ abstract class Computer extends Actionable implements AccessControlled, ExecutorListener { public /*transient*/ abstract class Computer extends Actionable implements AccessControlled, ExecutorListener {
private final CopyOnWriteArrayList<Executor> executors = new CopyOnWriteArrayList<Executor>(); private final CopyOnWriteArrayList<Executor> executors = new CopyOnWriteArrayList<Executor>();
// TODO:
private final CopyOnWriteArrayList<OneOffExecutor> oneOffExecutors = new CopyOnWriteArrayList<OneOffExecutor>();
private int numExecutors; private int numExecutors;
...@@ -451,7 +453,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces ...@@ -451,7 +453,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
} else { } else {
// if the number is increased, add new ones // if the number is increased, add new ones
while(executors.size()<numExecutors) while(executors.size()<numExecutors)
executors.add(new Executor(this)); executors.add(new Executor(this,executors.size()));
} }
} }
...@@ -486,6 +488,14 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces ...@@ -486,6 +488,14 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return new ArrayList<Executor>(executors); return new ArrayList<Executor>(executors);
} }
/**
* Gets the read-only snapshot view of all {@link OneOffExecutor}s.
*/
@Exported
public List<OneOffExecutor> getOneOffExecutors() {
return new ArrayList<OneOffExecutor>(oneOffExecutors);
}
/** /**
* Returns true if all the executors of this computer is idle. * Returns true if all the executors of this computer is idle.
*/ */
...@@ -656,6 +666,17 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces ...@@ -656,6 +666,17 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
return null; return null;
} }
/**
* Starts executing a fly-weight task.
*/
/*package*/ final void startFlyWeightTask(Queue.BuildableItem p) {
oneOffExecutors.add(new OneOffExecutor(this, p));
}
/*package*/ final void remove(OneOffExecutor e) {
oneOffExecutors.remove(e);
}
private static class ListPossibleNames implements Callable<List<String>,IOException> { private static class ListPossibleNames implements Callable<List<String>,IOException> {
public List<String> call() throws IOException { public List<String> call() throws IOException {
List<String> names = new ArrayList<String>(); List<String> names = new ArrayList<String>();
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
package hudson.model; package hudson.model;
import hudson.Util; import hudson.Util;
import hudson.model.Queue.*;
import hudson.util.TimeUnit2; import hudson.util.TimeUnit2;
import hudson.security.ACL; import hudson.security.ACL;
import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerRequest;
...@@ -45,7 +46,7 @@ import java.util.logging.Level; ...@@ -45,7 +46,7 @@ import java.util.logging.Level;
*/ */
@ExportedBean @ExportedBean
public class Executor extends Thread implements ModelObject { public class Executor extends Thread implements ModelObject {
private final Computer owner; protected final Computer owner;
private final Queue queue; private final Queue queue;
private long startTime; private long startTime;
...@@ -65,11 +66,11 @@ public class Executor extends Thread implements ModelObject { ...@@ -65,11 +66,11 @@ public class Executor extends Thread implements ModelObject {
private Throwable causeOfDeath; private Throwable causeOfDeath;
public Executor(Computer owner) { public Executor(Computer owner, int n) {
super("Executor #"+owner.getExecutors().size()+" for "+owner.getDisplayName()); super("Executor #"+n+" for "+owner.getDisplayName());
this.owner = owner; this.owner = owner;
this.queue = Hudson.getInstance().getQueue(); this.queue = Hudson.getInstance().getQueue();
this.number = owner.getExecutors().size(); this.number = n;
start(); start();
} }
...@@ -79,10 +80,7 @@ public class Executor extends Thread implements ModelObject { ...@@ -79,10 +80,7 @@ public class Executor extends Thread implements ModelObject {
try { try {
finishTime = System.currentTimeMillis(); finishTime = System.currentTimeMillis();
while(true) { while(shouldRun()) {
if(Hudson.getInstance() == null || Hudson.getInstance().isTerminating())
return;
synchronized(owner) { synchronized(owner) {
if(owner.getNumExecutors()<owner.getExecutors().size()) { if(owner.getNumExecutors()<owner.getExecutors().size()) {
// we've got too many executors. // we've got too many executors.
...@@ -98,7 +96,7 @@ public class Executor extends Thread implements ModelObject { ...@@ -98,7 +96,7 @@ public class Executor extends Thread implements ModelObject {
Queue.Item queueItem; Queue.Item queueItem;
try { try {
queueItem = queue.pop(); queueItem = grabJob();
} catch (InterruptedException e) { } catch (InterruptedException e) {
continue; continue;
} }
...@@ -144,6 +142,17 @@ public class Executor extends Thread implements ModelObject { ...@@ -144,6 +142,17 @@ public class Executor extends Thread implements ModelObject {
} }
} }
/**
* Returns true if we should keep going.
*/
protected boolean shouldRun() {
return Hudson.getInstance() != null && !Hudson.getInstance().isTerminating();
}
protected Queue.Item grabJob() throws InterruptedException {
return queue.pop();
}
/** /**
* Returns the current {@link Queue.Task} this executor is running. * Returns the current {@link Queue.Task} this executor is running.
* *
......
...@@ -3369,6 +3369,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl ...@@ -3369,6 +3369,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
public static boolean KILL_AFTER_LOAD = Boolean.getBoolean(Hudson.class.getName()+".killAfterLoad"); public static boolean KILL_AFTER_LOAD = Boolean.getBoolean(Hudson.class.getName()+".killAfterLoad");
public static boolean LOG_STARTUP_PERFORMANCE = Boolean.getBoolean(Hudson.class.getName()+".logStartupPerformance"); public static boolean LOG_STARTUP_PERFORMANCE = Boolean.getBoolean(Hudson.class.getName()+".logStartupPerformance");
private static final boolean CONSISTENT_HASH = true; // Boolean.getBoolean(Hudson.class.getName()+".consistentHash"); private static final boolean CONSISTENT_HASH = true; // Boolean.getBoolean(Hudson.class.getName()+".consistentHash");
public static boolean FLYWEIGHT_SUPPORT = Boolean.getBoolean(Hudson.class.getName()+".flyweightSupport");
private static final Logger LOGGER = Logger.getLogger(Hudson.class.getName()); private static final Logger LOGGER = Logger.getLogger(Hudson.class.getName());
......
/*
* The MIT License
*
* Copyright (c) 2004-2009, Sun Microsystems, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.model;
import hudson.model.Queue.FlyweightTask;
/**
* {@link Executor} that's temporarily added to carry out tasks that doesn't consume
* regular executors, like a matrix project parent build.
*
* @author Kohsuke Kawaguchi
* @see FlyweightTask
*/
public class OneOffExecutor extends Executor {
private Queue.Item item;
public OneOffExecutor(Computer owner, Queue.Item item) {
super(owner,-1);
this.item = item;
}
@Override
protected boolean shouldRun() {
// TODO: consulting super.shouldRun() here means we'll lose the item if it gets scheduled
// when super.shouldRun() returns false.
return super.shouldRun() && item!=null;
}
@Override
protected Queue.Item grabJob() throws InterruptedException {
Queue.Item r = item;
item = null;
return r;
}
@Override
public void run() {
try {
super.run();
} finally {
owner.remove(this);
}
}
}
...@@ -35,6 +35,8 @@ import hudson.triggers.Trigger; ...@@ -35,6 +35,8 @@ import hudson.triggers.Trigger;
import hudson.util.OneShotEvent; import hudson.util.OneShotEvent;
import hudson.util.TimeUnit2; import hudson.util.TimeUnit2;
import hudson.util.XStream2; import hudson.util.XStream2;
import hudson.util.ConsistentHash;
import hudson.util.ConsistentHash.Hash;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.File; import java.io.File;
...@@ -852,7 +854,7 @@ public class Queue extends ResourceController implements Saveable { ...@@ -852,7 +854,7 @@ public class Queue extends ResourceController implements Saveable {
// ready to be executed // ready to be executed
LOGGER.fine(p.task.getFullDisplayName() + " no longer blocked"); LOGGER.fine(p.task.getFullDisplayName() + " no longer blocked");
itr.remove(); itr.remove();
buildables.put(p.task,new BuildableItem(p)); makeBuildable(new BuildableItem(p));
} }
} }
...@@ -862,16 +864,15 @@ public class Queue extends ResourceController implements Saveable { ...@@ -862,16 +864,15 @@ public class Queue extends ResourceController implements Saveable {
if (!top.timestamp.before(new GregorianCalendar())) if (!top.timestamp.before(new GregorianCalendar()))
return; // finished moving all ready items from queue return; // finished moving all ready items from queue
waitingList.remove(top);
Task p = top.task; Task p = top.task;
if (!isBuildBlocked(p)) { if (!isBuildBlocked(p)) {
// ready to be executed immediately // ready to be executed immediately
waitingList.remove(top);
LOGGER.fine(p.getFullDisplayName() + " ready to build"); LOGGER.fine(p.getFullDisplayName() + " ready to build");
buildables.put(p,new BuildableItem(top)); makeBuildable(new BuildableItem(top));
} else { } else {
// this can't be built now because another build is in progress // this can't be built now because another build is in progress
// set this project aside. // set this project aside.
waitingList.remove(top);
LOGGER.fine(p.getFullDisplayName() + " is blocked"); LOGGER.fine(p.getFullDisplayName() + " is blocked");
blockedProjects.put(p,new BlockedItem(top)); blockedProjects.put(p,new BlockedItem(top));
} }
...@@ -881,6 +882,31 @@ public class Queue extends ResourceController implements Saveable { ...@@ -881,6 +882,31 @@ public class Queue extends ResourceController implements Saveable {
sortingHandler.sortBuildableItems(buildables); sortingHandler.sortBuildableItems(buildables);
} }
private void makeBuildable(BuildableItem p) {
if(Hudson.FLYWEIGHT_SUPPORT && p.task instanceof FlyweightTask) {
ConsistentHash<Node> hash = new ConsistentHash<Node>(new Hash<Node>() {
public String hash(Node node) {
return node.getNodeName();
}
});
Hudson h = Hudson.getInstance();
hash.add(h, h.getNumExecutors()*100);
for (Node n : h.getNodes())
hash.add(n,n.getNumExecutors()*100);
for (Node n : hash.list(p.task.getFullDisplayName())) {
Computer c = n.toComputer();
if (c==null) continue;
c.startFlyWeightTask(p);
return;
}
// if the execution get here, it means we couldn't schedule it anywhere.
// so do the scheduling like other normal jobs.
}
buildables.put(p.task,p);
}
public Api getApi() { public Api getApi() {
return new Api(this); return new Api(this);
} }
...@@ -891,6 +917,13 @@ public class Queue extends ResourceController implements Saveable { ...@@ -891,6 +917,13 @@ public class Queue extends ResourceController implements Saveable {
*/ */
public interface TransientTask extends Task {} public interface TransientTask extends Task {}
/**
* Marks {@link Task}s that do not consume {@link Executor}.
* @see OneOffExecutor
* @since 1.318
*/
public interface FlyweightTask extends Task {}
/** /**
* Task whose execution is controlled by the queue. * Task whose execution is controlled by the queue.
* *
......
...@@ -36,45 +36,15 @@ THE SOFTWARE. ...@@ -36,45 +36,15 @@ THE SOFTWARE.
<j:if test="${c.offline}"> (${%offline})</j:if> <j:if test="${c.offline}"> (${%offline})</j:if>
<j:if test="${!c.acceptingTasks}"> (${%suspended})</j:if> <j:if test="${!c.acceptingTasks}"> (${%suspended})</j:if>
</d:tag> </d:tag>
</d:taglib> <d:tag name="executor">
<j:set var="computers" value="${attrs.computers?:app.computers}" />
<l:pane title="&lt;a href='${rootURL}/computer/'>${%Build Executor Status}&lt;/a>" width="3" id="executors">
<j:forEach var="c" items="${computers}" varStatus="cloop">
<j:choose>
<j:when test="${c.node==app or computers.size()==1}">
<tr>
<th class="pane" align="right">#</th>
<th class="pane" width="80%">
<j:choose>
<j:when test="${empty(app.slaves) or computers.size()==1}">
${%Status}
</j:when>
<j:otherwise>
<local:computerCaption title="${%Master}" />
</j:otherwise>
</j:choose>
</th>
<th class="pane" width="24"><st:nbsp/></th>
</tr>
</j:when>
<j:otherwise>
<tr>
<th class="pane" colspan="3">
<local:computerCaption title="${c.displayName}" />
</th>
</tr>
</j:otherwise>
</j:choose>
<j:forEach var="e" items="${c.executors}" varStatus="eloop">
<tr> <tr>
<td class="pane" align="right" style="vertical-align: top"> <td class="pane" align="right" style="vertical-align: top">
${eloop.index+1} ${name}
</td> </td>
<j:choose> <j:choose>
<j:when test="${!e.alive}"> <j:when test="${!e.alive}">
<td class="pane"> <td class="pane">
<a href="${rootURL}/computers/${cloop.index}/executors/${eloop.index}/causeOfDeath"> <a href="${rootURL}/computers/${cloop.index}/${url}/causeOfDeath">
<span class="error">${%Dead} (!)</span> <span class="error">${%Dead} (!)</span>
</a> </a>
</td> </td>
...@@ -109,12 +79,48 @@ THE SOFTWARE. ...@@ -109,12 +79,48 @@ THE SOFTWARE.
</td> </td>
<td class="pane" align="center" valign="middle"> <td class="pane" align="center" valign="middle">
<j:if test="${e.hasStopPermission()}"> <j:if test="${e.hasStopPermission()}">
<a href="${rootURL}/computers/${cloop.index}/executors/${eloop.index}/stop"><img src="${imagesURL}/16x16/stop.gif" alt="${%terminate this build}" /></a> <a href="${rootURL}/computers/${cloop.index}/${url}/stop"><img src="${imagesURL}/16x16/stop.gif" alt="${%terminate this build}" /></a>
</j:if> </j:if>
</td> </td>
</j:otherwise> </j:otherwise>
</j:choose> </j:choose>
</tr> </tr>
</d:tag>
</d:taglib>
<j:set var="computers" value="${attrs.computers?:app.computers}" />
<l:pane title="&lt;a href='${rootURL}/computer/'>${%Build Executor Status}&lt;/a>" width="3" id="executors">
<j:forEach var="c" items="${computers}" varStatus="cloop">
<j:choose>
<j:when test="${c.node==app or computers.size()==1}">
<tr>
<th class="pane" align="right">#</th>
<th class="pane" width="80%">
<j:choose>
<j:when test="${empty(app.slaves) or computers.size()==1}">
${%Status}
</j:when>
<j:otherwise>
<local:computerCaption title="${%Master}" />
</j:otherwise>
</j:choose>
</th>
<th class="pane" width="24"><st:nbsp/></th>
</tr>
</j:when>
<j:otherwise>
<tr>
<th class="pane" colspan="3">
<local:computerCaption title="${c.displayName}" />
</th>
</tr>
</j:otherwise>
</j:choose>
<j:forEach var="e" items="${c.executors}" varStatus="eloop">
<local:executor name="${eloop.index+1}" url="executors/${eloop.index}" />
</j:forEach>
<j:forEach var="e" items="${c.oneOffExecutors}" varStatus="eloop">
<local:executor name="" url="oneOffExecutors/${eloop.index}" />
</j:forEach> </j:forEach>
</j:forEach> </j:forEach>
</l:pane> </l:pane>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册