private void execCompletionCleanup(Procedure<TEnvironment> proc) {
    final TEnvironment env = getEnvironment();
    if (proc.hasLock()) {
      LOG.warn("Usually this should not happen, we will release the lock before if the procedure"
        + " is finished, even if the holdLock is true, arrive here means we have some holes where"
        + " we do not release the lock. And the releaseLock below may fail since the procedure may"
        + " have already been deleted from the procedure store.");
      releaseLock(proc, true);
    }
    try {
      proc.completionCleanup(env);
    } catch (Throwable e) {
      // Catch NullPointerExceptions or similar errors...
      LOG.error("CODE-BUG: uncaught runtime exception for procedure: " + proc, e);
    }

    // call schedulers completion cleanup, we have some special clean up logic in this method so if
    // it throws any exceptions, we can not just ignore it like the above procedure's cleanup
    scheduler.completionCleanup(proc);
  }
Executes <code>procedure</code>
<ul>
<li>Calls the doExecute() of the procedure
<li>If the procedure execution didn't fail (i.e. valid user input)
<ul>
<li>...and returned subprocedures
<ul>
<li>The subprocedures are initialized.
<li>The subprocedures are added to the store
<li>The subprocedures are added to the runnable queue
<li>The procedure is now in a WAITING state, waiting for the subprocedures to complete
</ul>
</li>
<li>...if there are no subprocedure
<ul>
<li>the procedure completed successfully
<li>if there is a parent (WAITING)
<li>the parent state will be set to RUNNABLE
</ul>
</li>
</ul>
</li>
<li>In case of failure
<ul>
<li>The store is updated with the new state</li>
<li>The executor (caller of this method) will start the rollback of the procedure</li>
</ul>
</li>
</ul>
hbase-procedure/src/main/java/org/apache/hadoop/hbase/procedure2/ProcedureExecutor.java