public void delete()

in core/src/main/java/hudson/model/AbstractItem.java [691:792]


    public void delete() throws IOException, InterruptedException {
        checkPermission(DELETE);
        boolean responsibleForAbortingBuilds = !ItemDeletion.contains(this);
        boolean ownsRegistration = ItemDeletion.register(this);
        if (!ownsRegistration && ItemDeletion.isRegistered(this)) {
            // we are not the owning thread and somebody else is concurrently deleting this exact item
            throw new Failure(Messages.AbstractItem_BeingDeleted(getPronoun()));
        }
        try {
            // if a build is in progress. Cancel it.
            if (responsibleForAbortingBuilds || ownsRegistration) {
                Queue queue = Queue.getInstance();
                if (this instanceof Queue.Task) {
                    // clear any items in the queue so they do not get picked up
                    queue.cancel((Queue.Task) this);
                }
                // now cancel any child items - this happens after ItemDeletion registration, so we can use a snapshot
                for (Queue.Item i : queue.getItems()) {
                    Item item = Tasks.getItemOf(i.task);
                    while (item != null) {
                        if (item == this) {
                            queue.cancel(i);
                            break;
                        }
                        if (item.getParent() instanceof Item) {
                            item = (Item) item.getParent();
                        } else {
                            break;
                        }
                    }
                }
                // interrupt any builds in progress (and this should be a recursive test so that folders do not pay
                // the 15 second delay for every child item). This happens after queue cancellation, so will be
                // a complete set of builds in flight
                Map<Executor, Queue.Executable> buildsInProgress = new LinkedHashMap<>();
                for (Computer c : Jenkins.get().getComputers()) {
                    for (Executor e : c.getAllExecutors()) {
                        final WorkUnit workUnit = e.getCurrentWorkUnit();
                        final Executable executable = workUnit != null ? workUnit.getExecutable() : null;
                        final SubTask subtask = executable != null ? getParentOf(executable) : null;
                                
                        if (subtask != null) {        
                            Item item = Tasks.getItemOf(subtask);
                            while (item != null) {
                                if (item == this) {
                                    buildsInProgress.put(e, e.getCurrentExecutable());
                                    e.interrupt(Result.ABORTED);
                                    break;
                                }
                                if (item.getParent() instanceof Item) {
                                    item = (Item) item.getParent();
                                } else {
                                    break;
                                }
                            }
                        }
                    }
                }
                if (!buildsInProgress.isEmpty()) {
                    // give them 15 seconds or so to respond to the interrupt
                    long expiration = System.nanoTime() + TimeUnit.SECONDS.toNanos(15);
                    // comparison with executor.getCurrentExecutable() == computation currently should always be true
                    // as we no longer recycle Executors, but safer to future-proof in case we ever revisit recycling
                    while (!buildsInProgress.isEmpty() && expiration - System.nanoTime() > 0L) {
                        // we know that ItemDeletion will prevent any new builds in the queue
                        // ItemDeletion happens-before Queue.cancel so we know that the Queue will stay clear
                        // Queue.cancel happens-before collecting the buildsInProgress list
                        // thus buildsInProgress contains the complete set we need to interrupt and wait for
                        for (Iterator<Map.Entry<Executor, Queue.Executable>> iterator =
                             buildsInProgress.entrySet().iterator();
                             iterator.hasNext(); ) {
                            Map.Entry<Executor, Queue.Executable> entry = iterator.next();
                            // comparison with executor.getCurrentExecutable() == executable currently should always be
                            // true as we no longer recycle Executors, but safer to future-proof in case we ever
                            // revisit recycling.
                            if (!entry.getKey().isAlive()
                                    || entry.getValue() != entry.getKey().getCurrentExecutable()) {
                                iterator.remove();
                            }
                            // I don't know why, but we have to keep interrupting
                            entry.getKey().interrupt(Result.ABORTED);
                        }
                        Thread.sleep(50L);
                    }
                    if (!buildsInProgress.isEmpty()) {
                        throw new Failure(Messages.AbstractItem_FailureToStopBuilds(
                                buildsInProgress.size(), getFullDisplayName()
                        ));
                    }
                }
            }
            synchronized (this) { // could just make performDelete synchronized but overriders might not honor that
                performDelete();
            } // JENKINS-19446: leave synch block, but JENKINS-22001: still notify synchronously
        } finally {
            if (ownsRegistration) {
                ItemDeletion.deregister(this);
            }
        }
        getParent().onDeleted(AbstractItem.this);
        Jenkins.get().rebuildDependencyGraphAsync();
    }