code: public void addCompletionListener(ICompletionListener listener, Object state) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.entry(tc, "addCompletionListener, listener " + listener);
          }
  
          boolean alreadyComplete = true;
          // DBG - the fullyCompleted flag doesn't get set to true until completion listener called, so why check?
          // also, why fully completed? if partial completed, it will still
          // be completed = true, and will bail on next check
          if (!this.fullyCompleted) {
              synchronized (this.completedSemaphore) {
                  // check if the receiver is already completed.
                  if (!this.completed) {
                      alreadyComplete = false;
  
                      // need to set listener in sync so future cannot be completed
                      // before listener is added
                      this.firstListener = listener;
                      this.firstListenerState = state;
                  }
              }
          }
  
          // if already complete, listener would not have been notified
          if (alreadyComplete) {
              if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                  Tr.debug(tc, "request already complete - notifying listener");
              }
              invokeCallback(listener, this, state);
          }
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.exit(tc, "addCompletionListener, listener " + listener);
          }
      }
docs: /*
  @see com.ibm.io.async.AbstractAsyncFuture#addCompletionListener(com.ibm.io.async.ICompletionListener, java.lang.Object)