docs: private void stopInternal (boolean notifypeer) throws SISessionUnavailableException, SISessionDroppedException,
                                                           SIConnectionUnavailableException, SIConnectionDroppedException,
                                                           SIResourceException, SIConnectionLostException,
                                                           SIErrorException
     {
        if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) SibTr.entry(this, tc, "stopInternal","notifypeer="+notifypeer);
  
        if (executingOnCallbackThread())
        {
           if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) SibTr.debug(this, tc, "called from within asynch consumer callback, callbackThreadState="+callbackThreadState);
  
           if (callbackThreadState == CallbackThreadState.CLOSED || state == StateEnum.CLOSED || state == StateEnum.CLOSING)
           {
              throw new SISessionUnavailableException(nls.getFormattedMessage("SESSION_CLOSED_SICO1013", null, null));
           }
           else if (state == StateEnum.STOPPING || state == StateEnum.STOPPED)
           {
             if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) SibTr.debug(this, tc, "Session already stopping/stopped");
           }
           else if (callbackThreadState == CallbackThreadState.STARTED_DEREGISTERED)
           {
              callbackThreadState = CallbackThreadState.STOPPED_DEREGISTERED;
           }
           else if (callbackThreadState == CallbackThreadState.STARTED_REGISTERED)
           {
              callbackThreadState = CallbackThreadState.STOPPED_REGISTERED;
           }
        }
        else
        {
          boolean stopProxyQueue = false;
  
          synchronized (callbackLock) {
  
             if (state == StateEnum.CLOSED || state == StateEnum.CLOSING) {
               throw new SISessionUnavailableException(nls.getFormattedMessage("SESSION_CLOSED_SICO1013", null, null));
             }
  
             // Take the synchronous lock so that we may change the state of this session without other threads being able to do the same
             synchronized(synchLock)
             {
                if (state != StateEnum.STOPPED && state != StateEnum.STOPPING && state != StateEnum.STOPPING_THEN_STARTED)
                {
                   // Set state to stopping.  This will block anyone
                   // attempting to start the session until we can fully
                   // transition into the stopped state.
                   state = StateEnum.STOPPING;
  
                   // If we have a proxy queue, notify it of our intention
                   // to stop.  This should prevent any new asynchronous
                   // delivery attempts being made (any in progress will,
                   // however, run to completion).
                   if (proxyQueue != null) stopProxyQueue = true;
                   else
                   {
                      // We do not have a proxy queue (and hence cannot have
                      // an asynchronous consumer) - send a stop session flow
                      // to our peer.
                      state = StateEnum.STOPPED;
  
                      if (notifypeer) {
                        CommsByteBuffer request = getCommsByteBuffer();
  
                        // Build Message Header
                        request.putShort(getConnectionObjectID());
                        request.putShort(getProxyID());
  
                        // Pass on call to server
                        CommsByteBuffer reply = jfapExchange(request,
                                                             JFapChannelConstants.SEG_STOP_SESS,
                                                             JFapChannelConstants.PRIORITY_MEDIUM,
                                                             true);
  
                        try
                        {
                           short err = reply.getCommandCompletionCode(JFapChannelConstants.SEG_STOP_SESS_R);
                           if (err != CommsConstants.SI_NO_EXCEPTION)
                           {
                              checkFor_SISessionUnavailableException(reply, err);
                              checkFor_SISessionDroppedException(reply, err);
                              checkFor_SIConnectionUnavailableException(reply, err);
                              checkFor_SIConnectionDroppedException(reply, err);
                              checkFor_SIResourceException(reply, err);
                              checkFor_SIConnectionLostException(reply, err);
                              checkFor_SIErrorException(reply, err);
                              defaultChecker(reply, err);
                           }
                        }
                        finally
                        {
                           if (reply != null) reply.release();
                        }
                      }
                   }
                }
             }
          } // synchronized (callbackLock)
  
          // Transition proxy queue into stopping state outside of synchronized blocks. This is
          // because the call can block while any outstanding proxy messages are delivered to the
          // asynch consumer callbacks.
          if (stopProxyQueue) proxyQueue.stopping(notifypeer);
  
          synchronized (callbackLock) {
             // Take the syncLock so that we can change the state of the session
             synchronized(synchLock)
             {
                boolean doStart = (state == StateEnum.STOPPING_THEN_STARTED);
  
                if ((state == StateEnum.STOPPING) || (state == StateEnum.STOPPING_THEN_STARTED))
                {
                   state = StateEnum.STOPPED;
                   // If we have a proxy queue, notify it that we are
                   // stopped - so that it may choose to send a stop flow
                   // to our peer.
                   if (proxyQueue != null) proxyQueue.stopped();
                }
  
                if (doStart)
                {
                   startInternal();
                }
             }
          } // synchronized (callbackLock)
        }
  
        if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) SibTr.exit(this, tc, "stopInternal");
     }