code: public final void close(final int retryCloseTimeoutMs, final Consumer<Thread> closeFailAction)
      {
          isRunning = false;
  
          final Thread thread = this.thread.getAndSet(TOMBSTONE);
          if (null == thread)
          {
              try
              {
                  agent.onClose();
              }
              catch (final Throwable throwable)
              {
                  errorHandler.onError(throwable);
              }
              finally
              {
                  isClosed = true;
              }
          }
          else if (TOMBSTONE != thread)
          {
              while (true)
              {
                  try
                  {
                      thread.join(retryCloseTimeoutMs);
  
                      if (!thread.isAlive() || isClosed)
                      {
                          return;
                      }
  
                      if (null == closeFailAction)
                      {
                          System.err.println(
                              "timeout waiting for agent '" + agent.roleName() + "' to close, " + "retrying...");
                      }
                      else
                      {
                          closeFailAction.accept(thread);
                      }
  
                      if (!thread.isInterrupted())
                      {
                          thread.interrupt();
                      }
                  }
                  catch (final InterruptedException ignore)
                  {
                      if (null == closeFailAction)
                      {
                          System.err.println(
                              "close failed for agent '" + agent.roleName() + "' due to InterruptedException");
                      }
                      else
                      {
                          closeFailAction.accept(thread);
                      }
  
                      Thread.currentThread().interrupt();
                      return;
                  }
              }
          }
      }
docs: Stop the running Agent and cleanup.
  <p>
  This will wait for the work loop to exit. The close timeout parameter
  controls how long we should wait before retrying to stop the agent by
  interrupting the thread. If the calling thread has its interrupt flag
  set then this method can return early before waiting for the running
  agent to close.
  <p>
  An optional action can be invoked whenever we time out while waiting
  which accepts the agent runner thread as the parameter (e.g. to obtain
  and log a stack trace from the thread). If the action is null, a message
  is written to stderr. Please note  that a retry close timeout of zero
  waits indefinitely, in which case the fail action is only called on interrupt.
  
  @param retryCloseTimeoutMs how long to wait before retrying
  @param closeFailAction     function to invoke before retrying after close timeout