|
@@ -99,7 +99,6 @@ |
|
|
import java.util.concurrent.Future; |
|
|
import java.util.concurrent.atomic.AtomicLong; |
|
|
import java.util.concurrent.locks.Condition; |
|
|
import java.util.concurrent.locks.Lock; |
|
|
import java.util.concurrent.locks.ReentrantLock; |
|
|
import java.util.logging.Level; |
|
|
import java.util.logging.Logger; |
|
@@ -113,6 +112,7 @@ |
|
|
import org.acegisecurity.AccessDeniedException; |
|
|
import org.acegisecurity.Authentication; |
|
|
import org.jenkinsci.bytecode.AdaptField; |
|
|
import org.jenkinsci.remoting.RoleChecker; |
|
|
import org.kohsuke.stapler.HttpResponse; |
|
|
import org.kohsuke.stapler.HttpResponses; |
|
|
import org.kohsuke.stapler.export.Exported; |
|
@@ -337,7 +337,7 @@ public Void call() throws Exception { |
|
|
} |
|
|
}); |
|
|
|
|
|
private transient final Lock lock = new ReentrantLock(); |
|
|
private transient final ReentrantLock lock = new ReentrantLock(); |
|
|
|
|
|
private transient final Condition condition = lock.newCondition(); |
|
|
|
|
@@ -1179,7 +1179,7 @@ public static void withLock(Runnable runnable) { |
|
|
} |
|
|
|
|
|
/** |
|
|
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather |
|
|
* Some operations require the {@link Queue} lock held. Use one of these methods rather |
|
|
* than locking directly on Queue in order to allow for future refactoring. |
|
|
* |
|
|
* @param callable the operation to perform. |
|
@@ -1219,6 +1219,59 @@ public static void withLock(Runnable runnable) { |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Invokes the supplied {@link Runnable} if the {@link Queue} lock was obtained without blocking. |
|
|
* |
|
|
* @param runnable the operation to perform. |
|
|
* @return {@code true} if the lock was available and the operation was performed. |
|
|
* @since 1.618 |
|
|
*/ |
|
|
public static boolean tryWithLock(Runnable runnable) { |
|
|
final Jenkins jenkins = Jenkins.getInstance(); |
|
|
final Queue queue = jenkins == null ? null : jenkins.getQueue(); |
|
|
if (queue == null) { |
|
|
runnable.run(); |
|
|
return true; |
|
|
} else { |
|
|
return queue._tryWithLock(runnable); |
|
|
} |
|
|
} |
|
|
/** |
|
|
* Wraps a {@link Runnable} with the {@link Queue} lock held. |
|
|
* |
|
|
* @param runnable the operation to wrap. |
|
|
* @since 1.618 |
|
|
*/ |
|
|
public static Runnable wrapWithLock(Runnable runnable) { |
|
|
final Jenkins jenkins = Jenkins.getInstance(); |
|
|
final Queue queue = jenkins == null ? null : jenkins.getQueue(); |
|
|
return queue == null ? runnable : new LockedRunnable(runnable); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Wraps a {@link hudson.remoting.Callable} with the {@link Queue} lock held. |
|
|
* |
|
|
* @param callable the operation to wrap. |
|
|
* @since 1.618 |
|
|
*/ |
|
|
public static <V, T extends Throwable> hudson.remoting.Callable<V, T> wrapWithLock(hudson.remoting.Callable<V, T> callable) { |
|
|
final Jenkins jenkins = Jenkins.getInstance(); |
|
|
final Queue queue = jenkins == null ? null : jenkins.getQueue(); |
|
|
return queue == null ? callable : new LockedHRCallable<>(callable); |
|
|
} |
|
|
|
|
|
/** |
|
|
* Wraps a {@link java.util.concurrent.Callable} with the {@link Queue} lock held. |
|
|
* |
|
|
* @param callable the operation to wrap. |
|
|
* @since 1.618 |
|
|
*/ |
|
|
public static <V> java.util.concurrent.Callable<V> wrapWithLock(java.util.concurrent.Callable<V> callable) { |
|
|
final Jenkins jenkins = Jenkins.getInstance(); |
|
|
final Queue queue = jenkins == null ? null : jenkins.getQueue(); |
|
|
return queue == null ? callable : new LockedJUCCallable<V>(callable); |
|
|
} |
|
|
|
|
|
@Override |
|
|
protected void _await() throws InterruptedException { |
|
|
condition.await(); |
|
@@ -1244,6 +1297,26 @@ protected void _withLock(Runnable runnable) { |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Invokes the supplied {@link Runnable} if the {@link Queue} lock was obtained without blocking. |
|
|
* |
|
|
* @param runnable the operation to perform. |
|
|
* @return {@code true} if the lock was available and the operation was performed. |
|
|
* @since 1.618 |
|
|
*/ |
|
|
protected boolean _tryWithLock(Runnable runnable) { |
|
|
if (lock.tryLock()) { |
|
|
try { |
|
|
runnable.run(); |
|
|
} finally { |
|
|
lock.unlock(); |
|
|
} |
|
|
return true; |
|
|
} else { |
|
|
return false; |
|
|
} |
|
|
} |
|
|
|
|
|
/** |
|
|
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather |
|
|
* than locking directly on Queue in order to allow for future refactoring. |
|
@@ -1308,6 +1381,13 @@ public void maintain() { |
|
|
List<BuildableItem> lostPendings = new ArrayList<BuildableItem>(pendings); |
|
|
for (Computer c : Jenkins.getInstance().getComputers()) { |
|
|
for (Executor e : c.getExecutors()) { |
|
|
if (e.isInterrupted()) { |
|
|
// JENKINS-28840 we will deadlock if we try to touch this executor while interrupt flag set |
|
|
// we need to clear lost pendings as we cannot know what work unit was on this executor |
|
|
// while it is interrupted. (All this dancing is a result of Executor extending Thread) |
|
|
lostPendings.clear(); // we'll get them next time around when the flag is cleared. |
|
|
continue; |
|
|
} |
|
|
if (e.isParking()) { |
|
|
parked.put(e, new JobOffer(e)); |
|
|
} |
|
@@ -2571,6 +2651,51 @@ public Snapshot(Set<WaitingItem> waitingList, List<BlockedItem> blockedProjects, |
|
|
this.pendings = new ArrayList<BuildableItem>(pendings); |
|
|
} |
|
|
} |
|
|
|
|
|
private static class LockedRunnable implements Runnable { |
|
|
private final Runnable delegate; |
|
|
|
|
|
private LockedRunnable(Runnable delegate) { |
|
|
this.delegate = delegate; |
|
|
} |
|
|
|
|
|
@Override |
|
|
public void run() { |
|
|
withLock(delegate); |
|
|
} |
|
|
} |
|
|
|
|
|
private static class LockedJUCCallable<V> implements java.util.concurrent.Callable<V> { |
|
|
private final java.util.concurrent.Callable<V> delegate; |
|
|
|
|
|
private LockedJUCCallable(java.util.concurrent.Callable<V> delegate) { |
|
|
this.delegate = delegate; |
|
|
} |
|
|
|
|
|
@Override |
|
|
public V call() throws Exception { |
|
|
return withLock(delegate); |
|
|
} |
|
|
} |
|
|
|
|
|
private static class LockedHRCallable<V,T extends Throwable> implements hudson.remoting.Callable<V,T> { |
|
|
private static final long serialVersionUID = 1L; |
|
|
private final hudson.remoting.Callable<V,T> delegate; |
|
|
|
|
|
private LockedHRCallable(hudson.remoting.Callable<V,T> delegate) { |
|
|
this.delegate = delegate; |
|
|
} |
|
|
|
|
|
@Override |
|
|
public V call() throws T { |
|
|
return withLock(delegate); |
|
|
} |
|
|
|
|
|
@Override |
|
|
public void checkRoles(RoleChecker checker) throws SecurityException { |
|
|
delegate.checkRoles(checker); |
|
|
} |
|
|
} |
|
|
|
|
|
@CLIResolver |
|
|
public static Queue getInstance() { |
|
|