# 회전 — 읽고 판단한 뒤 조건 없이 쓴다
    AtomicReference<GrpcChannelRuntime> holder = current.get(next.profileName());
    if (holder == null || holder.get() == null) {
      return install(next);
    }
    GrpcChannelRuntime previous = holder.get();
    if (!previous.generation().supersededBy(next)) {
      throw new IllegalArgumentException(
          "generation "
    GrpcChannelRuntime replacement = new GrpcChannelRuntime(next);
    holder.set(replacement);
    previous.beginDrain();
    draining
#   같은 클래스의 첫 설치는 비교 후 교체를 쓴다
    AtomicReference<GrpcChannelRuntime> holder =
        current.computeIfAbsent(generation.profileName(), key -> new AtomicReference<>());
    if (!holder.compareAndSet(null, runtime)) {
#   그 파일의 compareAndSet 매치 수: 1
#   덮인 대체본을 담는 곳 — draining 에 add 되는 값: 108:        .add(previous);

# 이 세대가 들고 있는 것은 채널이 아니다
17:  private final GrpcChannelGeneration generation;
18:  private final AtomicInteger inFlightUnaryCalls = new AtomicInteger();
19:  private final AtomicInteger openStreams = new AtomicInteger();
20:  private volatile boolean draining;
#   이 모듈 main 의 io.grpc 참조: 0

# 계수기 — 확인하고 별도 연산으로 줄인다
  public void finishUnaryCall() {
    if (inFlightUnaryCalls.get() > 0) {
      inFlightUnaryCalls.decrementAndGet();
    }
  }
  public void closeStream() {
    if (openStreams.get() > 0) {
      openStreams.decrementAndGet();
    }
  }
#   이 파일의 compareAndSet / updateAndGet 매치: 0
#   올리는 쪽은 배수가 시작되면 거절한다
  public boolean startUnaryCall() {
    if (draining) {
      return false;
    }
    inFlightUnaryCalls.incrementAndGet();
#   조용함 판정
  public boolean quiescent() {
    return inFlightUnaryCalls.get() == 0 && openStreams.get() == 0;
  }
#   finishUnaryCall 의 프로덕션 호출자: 0

# 회수 — 동기화 목록을 잠그지 않고 순회한다
107:            next.profileName(), key -> java.util.Collections.synchronizedList(new ArrayList<>()))
  public List<GrpcChannelRuntime> draining(GrpcChannelProfileName profileName) {
    return List.copyOf(draining.getOrDefault(profileName, List.of()));
  }
  public int retireQuiescent(GrpcChannelProfileName profileName) {
    List<GrpcChannelRuntime> runtimes = draining.get(profileName);
    if (runtimes == null) {
      return 0;
    }
    List<GrpcChannelRuntime> quiescent =
        runtimes.stream().filter(GrpcChannelRuntime::quiescent).toList();
    runtimes.removeAll(quiescent);
    return quiescent.size();
  }
#   retireQuiescent 의 프로덕션 호출자: 0

# 옆 모듈 — 배수 완료는 판단조차 없다
  /** Forgets the draining generation once its work has finished or been cancelled. */
  public void completeDrain() {
    State observed = state.get();
    state.set(new State(observed.current(), null, null));
  }
#   그 파일의 compareAndSet / updateAndGet / synchronized 매치: 0

# 두 모듈의 런타임 소속: grpc-policy=[] grpc-client=[]
