@Override
  public Set<InternalDistributedMember> send(List<InternalDistributedMember> destinations,
      DistributionMessage msg) throws NotSerializableException {
    Set<InternalDistributedMember> result;
    boolean allDestinations = msg.forAll();

    checkCancelled();

    membership.waitIfPlayingDead();

    if (membership.isJoining()) {
      // If we get here, we are starting up, so just report a failure.
      if (allDestinations) {
        return null;
      } else {
        return new HashSet<>(destinations);
      }
    }

    if (msg instanceof AdminMessageType && shutdownInProgress()) {
      // no admin messages while shutting down - this can cause threads to hang
      return new HashSet<>(msg.getRecipients());
    }

    // Handle trivial cases
    if (destinations == null) {
      if (logger.isTraceEnabled()) {
        logger.trace("Membership: Message send: returning early because null set passed in: '{}'",
            msg);
      }
      return null; // trivially: all recipients received the message
    }
    if (destinations.isEmpty()) {
      if (logger.isTraceEnabled()) {
        logger.trace(
            "Membership: Message send: returning early because empty destination list passed in: '{}'",
            msg);
      }
      return null; // trivially: all recipients received the message
    }

    msg.setSender(getLocalMember());

    msg.setBreadcrumbsInSender();
    Breadcrumbs.setProblem(null);

    boolean useMcast = false;
    if (mcastEnabled) {
      useMcast = (msg.getMulticast() || allDestinations);
    }

    boolean sendViaMessenger = isForceUDPCommunications() || (msg instanceof ShutdownMessage);

    if (useMcast || tcpDisabled || sendViaMessenger) {
      result = membership.send(destinations.toArray(EMPTY_MEMBER_ARRAY), msg);
    } else {
      result = directChannelSend(destinations, msg);
    }

    // If the message was a broadcast, don't enumerate failures.
    if (allDestinations) {
      return null;
    } else {
      return result;
    }
  }
thread-local used to force use of Messenger for communications, usually to avoid deadlock when
conserve-sockets=true. Use of this should be removed when connection pools are implemented in
the direct-channel
geode-core/src/main/java/org/apache/geode/distributed/internal/DistributionImpl.java