/**
   * Assign a ${@link Queue.Executable} to this OneShotSlave. By design, only one Queue.Executable
   * can be assigned, then slave is shut down. This method has to be called just as the ${@link Run}
   * as been created. It run the actual launch of the executor and use Run's ${@link
   * hudson.model.BuildListener} as computer launcher listener to collect the startup log as part of
   * the build.
   *
   * <p>Delaying launch of the executor until the Run is actually started allows to fail the build
   * on launch failure, so we have a strong 1:1 relation between a Run and it's Executor.
   *
   * @param listener
   */
  synchronized void provision(TaskListener listener) {
    if (executable != null) {
      // already provisioned
      return;
    }

    final Executor executor = Executor.currentExecutor();
    if (executor == null) {
      throw new IllegalStateException("running task without associated executor thread");
    }

    try {
      realLauncher.launch(this.getComputer(), listener);

      if (getComputer().isActuallyOffline()) {
        provisionFailed(new IllegalStateException("Computer is offline after launch"));
      }
    } catch (Exception e) {
      provisionFailed(e);
    }
    executable = executor.getCurrentExecutable();
  }