提交 3583f344 编写于 作者: J Jesse Glick

Merge branch 'scheduled-executor-timer' of github.com:recampbell/jenkins into...

Merge branch 'scheduled-executor-timer' of github.com:recampbell/jenkins into recampbell-scheduled-executor-timer
......@@ -24,6 +24,7 @@
package hudson.util
import hudson.model.Computer
import jenkins.util.Timer
import jenkins.model.Jenkins
import hudson.model.Label
import hudson.model.Queue.BlockedItem
......@@ -31,7 +32,7 @@ import hudson.model.Queue.BuildableItem
import hudson.model.Queue.WaitingItem
import hudson.triggers.SafeTimerTask
import java.text.DateFormat
import hudson.triggers.Trigger;
import java.util.concurrent.TimeUnit
/**
* Spits out the load information.
......@@ -51,7 +52,7 @@ public class LoadMonitorImpl extends SafeTimerTask {
this.dataFile = dataFile;
labels = Jenkins.getInstance().labels*.name;
printHeaders();
Trigger.timer.scheduleAtFixedRate(this,0,10*1000);
Timer.get().scheduleAtFixedRate(this,0,10*1000, TimeUnit.MILLISECONDS);
}
private String quote(Object s) { "\"${s}\""; }
......
......@@ -25,14 +25,17 @@ package hudson.model;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.init.Initializer;
import hudson.triggers.SafeTimerTask;
import hudson.triggers.Trigger;
import jenkins.model.Jenkins;
import jenkins.util.Timer;
import java.util.Random;
import java.util.Timer;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import static hudson.init.InitMilestone.JOB_LOADED;
/**
......@@ -85,12 +88,17 @@ public abstract class AperiodicWork extends SafeTimerTask implements ExtensionPo
@Override
public final void doRun() throws Exception{
doAperiodicRun();
Timer timer = Trigger.timer;
if (timer != null) {
timer.schedule(getNewInstance(), getRecurrencePeriod());
Timer.get().schedule(getNewInstance(), getRecurrencePeriod(), TimeUnit.MILLISECONDS);
}
@Initializer(after= JOB_LOADED)
public static void init() {
// start all AperidocWorks
for (AperiodicWork p : AperiodicWork.all()) {
Timer.get().schedule(p, p.getInitialDelay(), TimeUnit.MILLISECONDS);
}
}
protected abstract void doAperiodicRun();
/**
......
......@@ -28,16 +28,19 @@ import hudson.DescriptorExtensionList;
import hudson.Extension;
import hudson.Util;
import hudson.XmlFile;
import hudson.init.Initializer;
import hudson.model.Descriptor.FormException;
import hudson.model.listeners.SaveableListener;
import hudson.node_monitors.NodeMonitor;
import hudson.slaves.NodeDescriptor;
import hudson.triggers.SafeTimerTask;
import hudson.util.DescribableList;
import hudson.util.FormApply;
import hudson.util.FormValidation;
import jenkins.model.Jenkins;
import jenkins.model.ModelObjectWithChildren;
import jenkins.model.ModelObjectWithContextMenu.ContextMenu;
import jenkins.util.Timer;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.QueryParameter;
import org.kohsuke.stapler.StaplerRequest;
......@@ -54,10 +57,13 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.json.JSONObject;
import static hudson.init.InitMilestone.JOB_LOADED;
/**
* Serves as the top of {@link Computer}s in the URL hierarchy.
* <p>
......@@ -397,6 +403,16 @@ public final class ComputerSet extends AbstractModelObject implements Describabl
*/
public static void initialize() {}
@Initializer(after= JOB_LOADED)
public static void init() {
// start monitoring nodes, although there's no hurry.
Timer.get().schedule(new SafeTimerTask() {
public void doRun() {
ComputerSet.initialize();
}
}, 10, TimeUnit.SECONDS);
}
private static final Logger LOGGER = Logger.getLogger(ComputerSet.class.getName());
static {
......
......@@ -23,16 +23,20 @@
*/
package hudson.model;
import hudson.init.Initializer;
import hudson.triggers.SafeTimerTask;
import hudson.triggers.Trigger;
import hudson.ExtensionPoint;
import hudson.Extension;
import hudson.ExtensionList;
import jenkins.model.Jenkins;
import jenkins.util.Timer;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.Random;
import java.util.Timer;
import static hudson.init.InitMilestone.JOB_LOADED;
/**
* Extension point to perform a periodic task in Hudson (through {@link Timer}.)
......@@ -89,6 +93,14 @@ public abstract class PeriodicWork extends SafeTimerTask implements ExtensionPoi
return Jenkins.getInstance().getExtensionList(PeriodicWork.class);
}
@Initializer(after= JOB_LOADED)
public static void init() {
// start all PeriodicWorks
for (PeriodicWork p : PeriodicWork.all()) {
Timer.get().scheduleAtFixedRate(p, p.getInitialDelay(), p.getRecurrencePeriod(), TimeUnit.MILLISECONDS);
}
}
// time constants
protected static final long MIN = 1000*60;
protected static final long HOUR =60*MIN;
......
......@@ -65,8 +65,8 @@ import hudson.model.queue.CauseOfBlockage.BecauseLabelIsOffline;
import hudson.model.queue.CauseOfBlockage.BecauseNodeIsBusy;
import hudson.model.queue.WorkUnitContext;
import hudson.security.ACL;
import jenkins.util.Timer;
import hudson.triggers.SafeTimerTask;
import hudson.triggers.Trigger;
import hudson.util.TimeUnit2;
import hudson.util.XStream2;
import hudson.util.ConsistentHash;
......@@ -91,7 +91,6 @@ import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.Timer;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
......@@ -1986,10 +1985,7 @@ public class Queue extends ResourceController implements Saveable {
private void periodic() {
long interval = 5000;
Timer timer = Trigger.timer;
if (timer != null) {
timer.schedule(this, interval, interval);
}
Timer.get().scheduleWithFixedDelay(this, interval, interval, TimeUnit.MILLISECONDS);
}
protected void doRun() {
......
......@@ -29,16 +29,16 @@ import hudson.model.Descriptor;
import jenkins.model.Jenkins;
import hudson.model.ComputerSet;
import hudson.model.AdministrativeMonitor;
import hudson.triggers.Trigger;
import hudson.triggers.SafeTimerTask;
import hudson.slaves.OfflineCause;
import jenkins.util.Timer;
import java.io.IOException;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Timer;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -87,14 +87,12 @@ public abstract class AbstractNodeMonitorDescriptor<T> extends Descriptor<NodeMo
}
private void schedule(long interval) {
Timer timer = Trigger.timer;
if (timer != null) {
timer.scheduleAtFixedRate(new SafeTimerTask() {
Timer.get()
.scheduleAtFixedRate(new SafeTimerTask() {
public void doRun() {
triggerUpdate();
}
}, interval, interval);
}
}, interval, interval, TimeUnit.MILLISECONDS);
}
/**
......
......@@ -23,18 +23,14 @@
*/
package hudson.triggers;
import static hudson.init.InitMilestone.JOB_LOADED;
import hudson.DependencyRunner;
import hudson.DependencyRunner.ProjectRunnable;
import hudson.DescriptorExtensionList;
import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.init.Initializer;
import hudson.model.AbstractProject;
import hudson.model.Action;
import hudson.model.AperiodicWork;
import hudson.model.Build;
import hudson.model.ComputerSet;
import hudson.model.Describable;
import hudson.scheduler.Hash;
import jenkins.model.Jenkins;
......@@ -45,7 +41,6 @@ import hudson.model.TopLevelItem;
import hudson.model.TopLevelItemDescriptor;
import hudson.scheduler.CronTab;
import hudson.scheduler.CronTabList;
import hudson.util.DoubleLaunchChecker;
import java.io.InvalidObjectException;
import java.io.ObjectStreamException;
......@@ -56,7 +51,6 @@ import java.util.Collections;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.Timer;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -279,34 +273,12 @@ public abstract class Trigger<J extends Item> implements Describable<Trigger<?>>
* Initialized and cleaned up by {@link jenkins.model.Jenkins}, but value kept here for compatibility.
*
* If plugins want to run periodic jobs, they should implement {@link PeriodicWork}.
*
* @deprecated Use {@link jenkins.util.Timer#get()} instead.
*/
@SuppressWarnings("MS_SHOULD_BE_FINAL")
public static @CheckForNull Timer timer;
@Initializer(after=JOB_LOADED)
public static void init() {
new DoubleLaunchChecker().schedule();
Timer _timer = timer;
if (_timer != null) {
// start all PeridocWorks
for(PeriodicWork p : PeriodicWork.all()) {
_timer.scheduleAtFixedRate(p,p.getInitialDelay(),p.getRecurrencePeriod());
}
// start all AperidocWorks
for(AperiodicWork p : AperiodicWork.all()) {
_timer.schedule(p,p.getInitialDelay());
}
// start monitoring nodes, although there's no hurry.
_timer.schedule(new SafeTimerTask() {
public void doRun() {
ComputerSet.initialize();
}
}, 1000*10);
}
}
@Deprecated
public static @CheckForNull java.util.Timer timer;
/**
* Returns all the registered {@link Trigger} descriptors.
......
......@@ -23,24 +23,28 @@
*/
package hudson.util;
import hudson.init.Initializer;
import jenkins.model.Jenkins;
import hudson.triggers.SafeTimerTask;
import hudson.triggers.Trigger;
import jenkins.util.Timer;
import org.apache.commons.io.FileUtils;
import org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import javax.servlet.ServletContext;
import static hudson.init.InitMilestone.JOB_LOADED;
import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR;
import java.io.File;
import java.io.IOException;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import java.util.logging.Level;
import java.lang.management.ManagementFactory;
import java.lang.reflect.Method;
import java.util.Timer;
/**
* Makes sure that no other Hudson uses our <tt>JENKINS_HOME</tt> directory,
......@@ -146,14 +150,18 @@ public class DoubleLaunchChecker {
public void schedule() {
// randomize the scheduling so that multiple Hudson instances will write at the file at different time
long MINUTE = 1000*60;
Timer timer = Trigger.timer;
if (timer != null) {
timer.schedule(new SafeTimerTask() {
Timer.get()
.schedule(new SafeTimerTask() {
protected void doRun() {
execute();
}
},(random.nextInt(30)+60)*MINUTE);
}
}, (random.nextInt(30) + 60) * MINUTE, TimeUnit.MILLISECONDS);
}
@Initializer(after= JOB_LOADED)
public static void init() {
new DoubleLaunchChecker().schedule();
}
/**
......
......@@ -199,6 +199,7 @@ import jenkins.model.ProjectNamingStrategy.DefaultProjectNamingStrategy;
import jenkins.security.ConfidentialKey;
import jenkins.security.ConfidentialStore;
import jenkins.slaves.WorkspaceLocator;
import jenkins.util.Timer;
import jenkins.util.io.FileBoolean;
import net.sf.json.JSONObject;
import org.acegisecurity.AccessDeniedException;
......@@ -280,7 +281,6 @@ import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.Timer;
import java.util.TreeSet;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
......@@ -765,7 +765,7 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
// doing this early allows InitStrategy to set environment upfront
final InitStrategy is = InitStrategy.get(Thread.currentThread().getContextClassLoader());
Trigger.timer = new Timer("Jenkins cron thread");
Trigger.timer = new java.util.Timer("Jenkins cron thread");
queue = new Queue(LoadBalancer.CONSISTENT_HASH);
try {
......@@ -838,15 +838,12 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
}
dnsMultiCast = new DNSMultiCast(this);
Timer timer = Trigger.timer;
if (timer != null) {
timer.scheduleAtFixedRate(new SafeTimerTask() {
@Override
protected void doRun() throws Exception {
trimLabels();
}
}, TimeUnit2.MINUTES.toMillis(5), TimeUnit2.MINUTES.toMillis(5));
}
Timer.get().scheduleAtFixedRate(new SafeTimerTask() {
@Override
protected void doRun() throws Exception {
trimLabels();
}
}, TimeUnit2.MINUTES.toMillis(5), TimeUnit2.MINUTES.toMillis(5), TimeUnit.MILLISECONDS);
updateComputerList();
......@@ -2700,12 +2697,16 @@ public class Jenkins extends AbstractCIBase implements ModifiableTopLevelItemGro
if(dnsMultiCast!=null)
dnsMultiCast.close();
interruptReloadThread();
Timer timer = Trigger.timer;
java.util.Timer timer = Trigger.timer;
if (timer != null) {
timer.cancel();
}
// TODO: how to wait for the completion of the last job?
Trigger.timer = null;
Timer.shutdown();
if(tcpSlaveAgentListener!=null)
tcpSlaveAgentListener.shutdown();
......
package jenkins.util;
import javax.annotation.Nonnull;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Holds the {@link ScheduledExecutorService} for running all background tasks in Jenkins.
* This ExecutorService will create additional threads to execute due (enabled) tasks.
*
* Provides a minimal abstraction for locating the ScheduledExecutorService so that we
* can modify it's behavior going forward. For instance, to add managability/monitoring.
*
* This is not an @Extension because it must be available before any extensions are loaded.
*
* Plugins should probably use one of the following as they provide higher level abstractions:
* {@link hudson.model.AperiodicWork}, {@link hudson.model.PeriodicWork},
* {@link hudson.model.AsyncAperiodicWork}, {@link hudson.model.AsyncPeriodicWork}.
*
* @author Ryan Campbell
* @since 1.538
*/
public class Timer {
/**
* The scheduled executor thread pool. This is initialized lazily since it may be created/shutdown many times
* when running the test suite.
*/
private static ScheduledExecutorService executorService;
/**
* Returns the scheduled executor service used by all timed tasks in Jenkins.
*
* @return the single {@link ScheduledExecutorService}.
*/
@Nonnull
public static synchronized ScheduledExecutorService get() {
if (executorService == null) {
// corePoolSize is set to 10, but will only be created if needed.
// ScheduledThreadPoolExecutor "acts as a fixed-sized pool using corePoolSize threads"
executorService = Executors.newScheduledThreadPool(10, new ThreadFactory() {
private final AtomicInteger threadNumber = new AtomicInteger(1);
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("Jenkins-cron-thread-" + threadNumber.getAndIncrement());
t.setDaemon(true);
return t;
}
});
}
return executorService;
}
/**
* Shutdown the timer and throw it away.
*/
public static synchronized void shutdown() {
if (executorService != null) {
executorService.shutdownNow();
executorService = null;
}
}
/**
* Do not create this.
*/
private Timer() {};
}
package jenkins.util;
import hudson.triggers.SafeTimerTask;
import org.junit.Assert;
import org.junit.Test;
import org.jvnet.hudson.test.Bug;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class TimerTest {
/**
* Launch two tasks which can only complete
* by running doRun() concurrently.
*/
@Test
@Bug(19622)
public void timersArentBlocked() throws InterruptedException {
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch stopLatch = new CountDownLatch(1);
SafeTimerTask task1 = new SafeTimerTask() {
@Override
protected void doRun() throws Exception {
startLatch.countDown();
stopLatch.await();
}
};
SafeTimerTask task2 = new SafeTimerTask() {
@Override
protected void doRun() throws Exception {
stopLatch.countDown();
}
};
Timer.get().schedule(task1, 1, TimeUnit.MILLISECONDS);
startLatch.await();
Timer.get().schedule(task2, 2, TimeUnit.MILLISECONDS);
if (! stopLatch.await(10000, TimeUnit.MILLISECONDS)) {
Assert.fail("Failed to run the two tasks simultaneously");
}
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册