docs: 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.getInstance().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);
                              if (item != null) {
                                  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.getInstance().rebuildDependencyGraphAsync();
      }