@VisibleForTesting
  boolean doClose(String reason, Throwable systemFailureCause, boolean keepAlive,
      boolean keepDS, boolean skipAwait) {
    securityService.close();

    if (waitIfClosing(skipAwait)) {
      return false;
    }

    if (!keepDS && systemFailureCause == null
        && (isReconnecting() || system.getReconnectedSystem() != null)) {
      logger.debug(
          "Cache is shutting down distributed system connection. isReconnecting={} reconnectedSystem={} keepAlive={} keepDS={}",
          isReconnecting(), system.getReconnectedSystem(), keepAlive, keepDS);

      system.stopReconnectingNoDisconnect();
      if (system.getReconnectedSystem() != null) {
        system.getReconnectedSystem().disconnect();
      }
      return false;
    }

    synchronized (GemFireCacheImpl.class) {
      // ALL CODE FOR CLOSE SHOULD NOW BE UNDER STATIC SYNCHRONIZATION OF GemFireCacheImpl.class
      // static synchronization is necessary due to static resources
      if (waitIfClosing(skipAwait)) {
        return false;
      }

      boolean isDebugEnabled = logger.isDebugEnabled();

      for (GatewaySender sender : allGatewaySenders) {
        try {
          ((InternalGatewaySender) sender).prepareForStop();
        } catch (Exception exception) {
          if (isDebugEnabled) {
            logger.debug("When calling Prepare for stop gw sender, ignore exception " + exception);
          }
        }
      }

      CLOSING_THREAD.set(Thread.currentThread());
      try {

        // First close the ManagementService
        system.handleResourceEvent(ResourceEvent.CACHE_REMOVE, this);
        if (resourceEventsListener != null) {
          system.removeResourceListener(resourceEventsListener);
          resourceEventsListener = null;
        }

        if (systemFailureCause != null) {
          forcedDisconnect = systemFailureCause instanceof ForcedDisconnectException;
          if (forcedDisconnect) {
            disconnectCause = new ForcedDisconnectException(reason);
          } else {
            disconnectCause = systemFailureCause;
          }
        }

        this.keepAlive = keepAlive;
        isClosing = true;
        logger.info("{}: Now closing.", this);

        // we don't clear the prID map if there is a system failure. Other
        // threads may be hung trying to communicate with the map locked
        if (systemFailureCause == null) {
          PartitionedRegion.clearPRIdMap();
        }

        TXStateProxy tx = null;
        try {
          if (transactionManager != null) {
            tx = transactionManager.pauseTransaction();
          }

          // do this before closing regions
          resourceManager.close();

          try {
            resourceAdvisor.close();
          } catch (CancelException ignore) {
          }
          try {
            jmxAdvisor.close();
          } catch (CancelException ignore) {
          }

          for (GatewaySender sender : allGatewaySenders) {
            try {
              sender.stop();
              GatewaySenderAdvisor advisor = ((AbstractGatewaySender) sender).getSenderAdvisor();
              if (advisor != null) {
                if (isDebugEnabled) {
                  logger.debug("Stopping the GatewaySender advisor");
                }
                advisor.close();
              }
            } catch (CancelException ignore) {
            }
          }

          destroyGatewaySenderLockService();

          if (eventThreadPool != null) {
            if (isDebugEnabled) {
              logger.debug("{}: stopping event thread pool...", this);
            }
            eventThreadPool.shutdown();
          }

          // IMPORTANT: any operation during shut down that can time out (create a CancelException)
          // must be inside of this try block. If all else fails, we *must* ensure that the cache
          // gets
          // closed!
          try {
            stopServers();

            stopServices();

            // no need to track PR instances
            if (isDebugEnabled) {
              logger.debug("{}: clearing partitioned regions...", this);
            }
            synchronized (partitionedRegions) {
              int prSize = -partitionedRegions.size();
              partitionedRegions.clear();
              getCachePerfStats().incPartitionedRegions(prSize);
            }

            prepareDiskStoresForClose();

            Operation op;
            if (forcedDisconnect) {
              op = Operation.FORCED_DISCONNECT;
            } else if (isReconnecting()) {
              op = Operation.CACHE_RECONNECT;
            } else {
              op = Operation.CACHE_CLOSE;
            }

            InternalRegion prRoot = null;

            for (InternalRegion lr : rootRegions.values()) {
              if (isDebugEnabled) {
                logger.debug("{}: processing region {}", this, lr.getFullPath());
              }
              if (PartitionedRegionHelper.PR_ROOT_REGION_NAME.equals(lr.getName())) {
                prRoot = lr;
              } else {
                if (lr.getName().contains(ParallelGatewaySenderQueue.QSTRING)) {
                  // this region will be closed internally by parent region
                  continue;
                }
                if (isDebugEnabled) {
                  logger.debug("{}: closing region {}...", this, lr.getFullPath());
                }
                try {
                  lr.handleCacheClose(op);
                } catch (RuntimeException e) {
                  if (isDebugEnabled || !forcedDisconnect) {
                    logger
                        .warn(String.format("%s: error closing region %s", this, lr.getFullPath()),
                            e);
                  }
                }
              }
            }

            try {
              if (isDebugEnabled) {
                logger.debug("{}: finishing partitioned region close...", this);
              }
              PartitionedRegion.afterRegionsClosedByCacheClose(this);
              if (prRoot != null) {
                // do the PR meta root region last
                prRoot.handleCacheClose(op);
              }
            } catch (CancelException e) {
              logger.warn(
                  String.format("%s: error in last stage of PartitionedRegion cache close", this),
                  e);
            }
            destroyPartitionedRegionLockService();

            closeDiskStores();
            diskMonitor.close();

            // Close the CqService Handle.
            try {
              if (isDebugEnabled) {
                logger.debug("{}: closing CQ service...", this);
              }
              cqService.close();
            } catch (RuntimeException ignore) {
              logger.info("Failed to get the CqService, to close during cache close (1).");
            }

            PoolManager.close(keepAlive);

            if (isDebugEnabled) {
              logger.debug("{}: notifying admins of close...", this);
            }
            try {
              SystemMemberCacheEventProcessor.send(this, Operation.CACHE_CLOSE);
            } catch (CancelException ignore) {
              if (logger.isDebugEnabled()) {
                logger.debug("Ignored cancellation while notifying admins");
              }
            }

            if (isDebugEnabled) {
              logger.debug("{}: stopping destroyed entries processor...", this);
            }
            tombstoneService.stop();

            // NOTICE: the CloseCache message is the *last* message you can send!
            DistributionManager distributionManager = null;
            try {
              distributionManager = system.getDistributionManager();
              distributionManager.removeMembershipListener(transactionManager);
            } catch (CancelException ignore) {
            }

            if (distributionManager != null) {
              // Send CacheClosedMessage (and NOTHING ELSE) here
              if (isDebugEnabled) {
                logger.debug("{}: sending CloseCache to peers...", this);
              }
              Set<InternalDistributedMember> otherMembers =
                  distributionManager.getOtherDistributionManagerIds();
              ReplyProcessor21 processor = replyProcessor21Factory.create(system, otherMembers);
              CloseCacheMessage msg = new CloseCacheMessage();
              msg.setRecipients(otherMembers);
              msg.setProcessorId(processor.getProcessorId());
              distributionManager.putOutgoing(msg);

              try {
                processor.waitForReplies();
              } catch (InterruptedException ignore) {
                // TODO: reset interrupt flag later?
                // Keep going, make best effort to shut down.
              } catch (ReplyException ignore) {
                // keep going
              }
              // set closed state after telling others and getting responses to avoid complications
              // with others still in the process of sending messages
            }
            // NO MORE Distributed Messaging AFTER THIS POINT!!!!

            ClientMetadataService cms = clientMetadataService;
            if (cms != null) {
              cms.close();
            }
            closeHeapEvictor();
            closeOffHeapEvictor();
          } catch (CancelException ignore) {
            // make sure the disk stores get closed
            closeDiskStores();
            // NO DISTRIBUTED MESSAGING CAN BE DONE HERE!
          }

          // Close the CqService Handle.
          try {
            cqService.close();
          } catch (RuntimeException ignore) {
            logger.info("Failed to get the CqService, to close during cache close (2).");
          }

          cachePerfStats.close();
          TXLockService.destroyServices();
          getEventTrackerTask().cancel();

          synchronized (ccpTimerMutex) {
            if (ccpTimer != null) {
              ccpTimer.cancel();
            }
          }

          expirationScheduler.cancel();

          // Stop QueryMonitor if running.
          if (queryMonitor != null) {
            queryMonitor.stopMonitoring();
          }

        } finally {
          // NO DISTRIBUTED MESSAGING CAN BE DONE HERE!
          if (transactionManager != null) {
            transactionManager.close();
          }
          ((DynamicRegionFactoryImpl) DynamicRegionFactory.get()).close();
          if (transactionManager != null) {
            transactionManager.unpauseTransaction(tx);
          }
          TXCommitMessage.getTracker().clearForCacheClose();
        }

        // Added to close the TransactionManager's cleanup thread
        TransactionManagerImpl.refresh();

        if (!keepDS) {
          // keepDS is used by ShutdownAll. It will override disableDisconnectDsOnCacheClose
          if (!disableDisconnectDsOnCacheClose) {
            system.disconnect();
          }
        }

        typeRegistryClose.run();
        typeRegistrySetPdxSerializer.accept(null);

        for (CacheLifecycleListener listener : cacheLifecycleListeners) {
          listener.cacheClosed(this);
        }

        SequenceLoggerImpl.signalCacheClose();
        SystemFailure.signalCacheClose();

        isClosedLatch.countDown();
      } finally {
        CLOSING_THREAD.remove();
        ClassPathLoader.setLatestToDefault(null);
      }
      return true;
    }
  }
Returns true if the caller performed the actual closing of the cache. Returns false if the
caller simply waited for another thread to perform the close.
geode-core/src/main/java/org/apache/geode/internal/cache/GemFireCacheImpl.java