|
26 | 26 | import java.util.logging.Level;
|
27 | 27 | import java.util.logging.Logger;
|
28 | 28 |
|
| 29 | +import hudson.model.TaskListener; |
29 | 30 | import jenkins.model.GlobalConfiguration;
|
30 | 31 | import jenkins.model.Jenkins;
|
31 | 32 | import net.sf.json.JSONException;
|
32 | 33 | import net.sf.json.JSONObject;
|
33 | 34 |
|
| 35 | +import org.apache.commons.lang.StringUtils; |
34 | 36 | import org.jenkins.plugins.lockableresources.queue.LockableResourcesStruct;
|
35 | 37 | import org.jenkinsci.plugins.scriptsecurity.sandbox.groovy.SecureGroovyScript;
|
36 | 38 | import org.jenkins.plugins.lockableresources.queue.QueuedContextStruct;
|
@@ -362,7 +364,7 @@ public synchronized void unlockNames(@Nullable List<String> resourceNamesToUnLoc
|
362 | 364 |
|
363 | 365 | // remove context from queue and process it
|
364 | 366 | requiredResourceForNextContext = checkResourcesAvailability(nextContext.getResources(), null, resourceNamesToUnLock);
|
365 |
| - this.queuedContexts.remove(nextContext); |
| 367 | + unqueueContext(nextContext.getContext()); |
366 | 368 |
|
367 | 369 | // resourceNamesToUnlock contains the names of the previous resources.
|
368 | 370 | // requiredResourceForNextContext contains the resource objects which are required for the next context.
|
@@ -504,9 +506,86 @@ public synchronized boolean reserve(List<LockableResource> resources,
|
504 | 506 | return true;
|
505 | 507 | }
|
506 | 508 |
|
| 509 | + private void unreserveResources(@Nonnull List<LockableResource> resources) { |
| 510 | + for (LockableResource l : resources) { |
| 511 | + l.unReserve(); |
| 512 | + } |
| 513 | + save(); |
| 514 | + } |
507 | 515 | public synchronized void unreserve(List<LockableResource> resources) {
|
| 516 | + // make sure there is a list of resources to unreserve |
| 517 | + if (resources == null || (resources.size() == 0)) { |
| 518 | + return; |
| 519 | + } |
| 520 | + List<String> resourceNamesToUnreserve = new ArrayList<>(); |
508 | 521 | for (LockableResource r : resources) {
|
509 |
| - r.unReserve(); |
| 522 | + resourceNamesToUnreserve.add(r.getName()); |
| 523 | + } |
| 524 | + |
| 525 | + // check if there are resources which can be unlocked (and shall not be unlocked) |
| 526 | + List<LockableResource> requiredResourceForNextContext = null; |
| 527 | + QueuedContextStruct nextContext = this.getNextQueuedContext(resourceNamesToUnreserve, false); |
| 528 | + |
| 529 | + // no context is queued which can be started once these resources are free'd. |
| 530 | + if (nextContext == null) { |
| 531 | + LOGGER.log(Level.FINER, "No context queued for resources " + StringUtils.join(resourceNamesToUnreserve, ", ") + " so unreserving and proceeding."); |
| 532 | + unreserveResources(resources); |
| 533 | + return; |
| 534 | + } |
| 535 | + |
| 536 | + PrintStream nextContextLogger = null; |
| 537 | + try { |
| 538 | + TaskListener nextContextTaskListener = nextContext.getContext().get(TaskListener.class); |
| 539 | + if (nextContextTaskListener != null) { |
| 540 | + nextContextLogger = nextContextTaskListener.getLogger(); |
| 541 | + } |
| 542 | + } catch (IOException | InterruptedException e) { |
| 543 | + LOGGER.log(Level.FINE, "Could not get logger for next context: " + e, e); |
| 544 | + } |
| 545 | + |
| 546 | + // remove context from queue and process it |
| 547 | + requiredResourceForNextContext = checkResourcesAvailability(nextContext.getResources(), |
| 548 | + nextContextLogger, |
| 549 | + resourceNamesToUnreserve); |
| 550 | + this.queuedContexts.remove(nextContext); |
| 551 | + |
| 552 | + // resourceNamesToUnreserve contains the names of the previous resources. |
| 553 | + // requiredResourceForNextContext contains the resource objects which are required for the next context. |
| 554 | + // It is guaranteed that there is an overlap between the two - the resources which are to be reused. |
| 555 | + boolean needToWait = false; |
| 556 | + for (LockableResource requiredResource : requiredResourceForNextContext) { |
| 557 | + if (!resourceNamesToUnreserve.contains(requiredResource.getName())) { |
| 558 | + if (requiredResource.isReserved() || requiredResource.isLocked()) { |
| 559 | + needToWait = true; |
| 560 | + break; |
| 561 | + } |
| 562 | + } |
| 563 | + } |
| 564 | + |
| 565 | + if (needToWait) { |
| 566 | + unreserveResources(resources); |
| 567 | + return; |
| 568 | + } else { |
| 569 | + unreserveResources(resources); |
| 570 | + List<String> resourceNamesToLock = new ArrayList<String>(); |
| 571 | + |
| 572 | + // lock all (old and new resources) |
| 573 | + for (LockableResource requiredResource : requiredResourceForNextContext) { |
| 574 | + try { |
| 575 | + requiredResource.setBuild(nextContext.getContext().get(Run.class)); |
| 576 | + resourceNamesToLock.add(requiredResource.getName()); |
| 577 | + } catch (Exception e) { |
| 578 | + // skip this context, as the build cannot be retrieved (maybe it was deleted while running?) |
| 579 | + LOGGER.log(Level.WARNING, "Skipping queued context for lock. Can not get the Run object from the context to proceed with lock, " + |
| 580 | + "this could be a legitimate status if the build waiting for the lock was deleted or" + |
| 581 | + " hard killed. More information at Level.FINE if debug is needed."); |
| 582 | + LOGGER.log(Level.FINE, "Can not get the Run object from the context to proceed with lock", e); |
| 583 | + return; |
| 584 | + } |
| 585 | + } |
| 586 | + |
| 587 | + // continue with next context |
| 588 | + LockStepExecution.proceed(resourceNamesToLock, nextContext.getContext(), nextContext.getResourceDescription(), null, false); |
510 | 589 | }
|
511 | 590 | save();
|
512 | 591 | }
|
|
0 commit comments