docs: @Override
      public void run() {
          boolean cancelledDone = false;
          boolean forceSelect = false;
          long selectTimeoutValue = 0L;
          int numEmptySelects = 0;
          int numExceptions = 0;
          boolean nothingTimedOut = true;
          long firstEmptySelectorTime = 0L;
          int numKeysOnEmptySelect = -1;
          long lastEmptySelectorFFDCTime = 0L;
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
              Tr.debug(this, tc, "selector thread started for " + Thread.currentThread().getName());
          }
  
          nextTimeoutTime = currentTime + TCPFactoryConfiguration.getChannelSelectorIdleTimeout();
  
          while (!quit) {
              currentTime = CHFWBundle.getApproxTime();
              try {
                  nothingTimedOut = true;
                  // yielding increases performance by not doing too many selects
                  // this gives 10% performance boost on Daves system, 10% performance
                  // loss on Tony's
                  if (selectorYield) {
                      Thread.yield();
                  }
  
                  wakeupPending = false;
                  if (checkCancel && forceSelect) {
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "selectNow() forced");
                      }
                      selector.selectNow();
                      forceSelect = false;
                      numEmptySelects = 0;
                  } else if (nextTimeoutTime > currentTime) {
                      // don't use a timeout of less than 1 second, since thats the
                      // granularity of ApproxTime
                      selectTimeoutValue = nextTimeoutTime - currentTime;
                      if (selectTimeoutValue < 1000L) {
                          selectTimeoutValue = 1000L;
                      }
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "select() with timeout = " + selectTimeoutValue);
                      }
                      selector.select(selectTimeoutValue);
                  } else {
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(this, tc, "selectNow()");
                      }
                      selector.selectNow();
                      nothingTimedOut = false;
                      numEmptySelects = 0;
                  }
                  wakeupPending = false;
                  // if any keys were cancelled on the last time through
                  // the selector should now be updated, so notfiy anyone
                  // anyone waiting on the cancels
                  if (checkCancel && cancelledDone) {
                      notifyCancelRequests();
                      cancelledDone = false;
                  }
  
                  currentTime = CHFWBundle.getApproxTime();
  
                  // need to process cancel requests before add requests, because we might
                  // be trying to add one back in that we previously cancelled. We also don't want
                  // to include keys that have been cancelled in our next-to-timeout logic
                  if (checkCancel) {
                      cancelledDone = processCancelRequests();
                      if (cancelledDone) {
                          forceSelect = true;
                          continue;
                      }
                  }
  
                  // the following logic is for detecting looping problems in the JDK
                  // it will log an ffdc event if the selector fires in quick succession
                  if (selector.selectedKeys().isEmpty() && nothingTimedOut && areQueuesEmpty()) {
                      if (numEmptySelects == 0) {
                          firstEmptySelectorTime = currentTime;
                          numKeysOnEmptySelect = selector.keys().size();
                      }
                      numEmptySelects++;
                      if (numEmptySelects == 40) {
                          // selector fired 40 times with nothing selected, see if it was in
                          // quick succession
                          // and key set hasn't changed
                          if (currentTime < (firstEmptySelectorTime + 10000L) && selector.keys().size() == numKeysOnEmptySelect) {
                              if (TraceComponent.isAnyTracingEnabled() && tc.isEventEnabled()) {
                                  Tr.event(this, tc, "Selector fired 20 times in a row with no keys selected - possible loop in JDK detected");
                              }
                              // TODO seems to happen sometimes if the socket is closed but not
                              // removed from the keys (Windows JDK 1.6 SR7 at least)... could scan and remove
                              // closed sockets the SocketChannel.close() is supposed to remove it from
                              // selectors but TCP has a checkcancel config option to manually do it (since it's
                              // been broken in the past apparently)
  
                              // create an exception to be logged in FFDC, but limit to once
                              // every 5 minutes
                              if (currentTime > (lastEmptySelectorFFDCTime + 300000L)) {
                                  ChannelException e = new ChannelException("TCP Channel detected a possible loop on thread: " + Thread.currentThread().getName());
                                  FFDCFilter.processException(e,
                                                              getClass().getName(),
                                                              "186",
                                                              this,
                                                              new Object[] { "Last FFDC time=" + lastEmptySelectorFFDCTime, "Current time=" + currentTime,
                                                                            "Next timeout time=" + nextTimeoutTime,
                                                                            "First empty select time=" + firstEmptySelectorTime, "First empty select keys=" + numKeysOnEmptySelect,
                                                                            "Number empty selects=" + numEmptySelects, "Thread interrupted=" + Thread.interrupted(), this.workQueue1,
                                                                            this.workQueue2 });
                                  lastEmptySelectorFFDCTime = currentTime;
                              }
                          }
                          numEmptySelects = 0;
                      } // end-if-20
                  } else {
                      numEmptySelects = 0;
                  }
                  // end of loop detection code
  
                  if (performRequest()) {
                      // connect logic cancels keys and returns true
                      continue;
                  }
  
                  updateCount();
  
                  checkForTimeouts();
                  updateSelector();
  
                  // if we got here, we didn't get an IOException
                  numExceptions = 0;
              } catch (IOException e) {
                  // an IOException can happen due to jdk errors or if we run out of sockets
                  // Don't log the exception during server shutdown
                  if (!!!com.ibm.wsspi.kernel.service.utils.FrameworkState.isStopping()) {
                      FFDCFilter.processException(e, getClass().getName(), "288", this);
                  }
                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                      Tr.debug(this, tc, "IOException (" + numExceptions + ") in run " + e);
                  }
                  numExceptions++;
  
              } catch (Throwable unexpectedException) {
                  // Don't log the exception during server shutdown
                  if (!!!com.ibm.wsspi.kernel.service.utils.FrameworkState.isStopping()) {
                      FFDCFilter.processException(unexpectedException, getClass().getName(), "254", this);
                  }
                  if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                      Tr.debug(this, tc, "Exception (" + numExceptions + ") in run " + unexpectedException);
                  }
                  // force selector to fire immediately, since an event or wakeup call
                  // could be missed
                  nextTimeoutTime = currentTime;
                  numExceptions++;
              }
  
              // if we don't recover after 5 minutes, stop accepting for a long-ish while.
              // allow for recovery of the system, reconfiguration, or server stop.
              if (numExceptions >= 400) {
                  if (pauseAccept()) {
                      numExceptions = 0; // start counting over again
                  }
              } else if (numExceptions >= 100) {
                  // if we get exceptions repeatedly, wait a second to give other
                  // threads a chance to close sockets or update selector
                  try {
                      Thread.sleep(1000);
                  } catch (InterruptedException ie) {
                      // ignore it
                  }
              }
          } // end-while
  
          channelSelectorClose();
      }