1 /* 2 * Copyright 2021 The gRPC Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package io.grpc.xds; 18 19 import com.google.common.annotations.VisibleForTesting; 20 import com.google.common.base.Preconditions; 21 import io.grpc.xds.FilterChainMatchingProtocolNegotiators.FilterChainMatchingHandler.FilterChainSelector; 22 import java.util.Comparator; 23 import java.util.TreeSet; 24 import java.util.concurrent.atomic.AtomicLong; 25 import javax.annotation.concurrent.GuardedBy; 26 27 /** 28 * Maintains the current xDS selector and any resources using that selector. When the selector 29 * changes, old resources are closed to avoid old config usages. 30 */ 31 final class FilterChainSelectorManager { 32 private static final AtomicLong closerId = new AtomicLong(); 33 34 private final Object lock = new Object(); 35 @GuardedBy("lock") 36 private FilterChainSelector selector; 37 // Avoid HashSet since it does not decrease in size, forming a high water mark. 38 @GuardedBy("lock") 39 private TreeSet<Closer> closers = new TreeSet<Closer>(new CloserComparator()); 40 register(Closer closer)41 public FilterChainSelector register(Closer closer) { 42 synchronized (lock) { 43 Preconditions.checkState(closers.add(closer), "closer already registered"); 44 return selector; 45 } 46 } 47 deregister(Closer closer)48 public void deregister(Closer closer) { 49 synchronized (lock) { 50 closers.remove(closer); 51 } 52 } 53 54 /** Only safe to be called by code that is responsible for updating the selector. */ getSelectorToUpdateSelector()55 public FilterChainSelector getSelectorToUpdateSelector() { 56 synchronized (lock) { 57 return selector; 58 } 59 } 60 updateSelector(FilterChainSelector newSelector)61 public void updateSelector(FilterChainSelector newSelector) { 62 TreeSet<Closer> oldClosers; 63 synchronized (lock) { 64 oldClosers = closers; 65 closers = new TreeSet<Closer>(closers.comparator()); 66 selector = newSelector; 67 } 68 for (Closer closer : oldClosers) { 69 closer.closer.run(); 70 } 71 } 72 73 @VisibleForTesting getRegisterCount()74 int getRegisterCount() { 75 synchronized (lock) { 76 return closers.size(); 77 } 78 } 79 80 public static final class Closer { 81 private final long id = closerId.getAndIncrement(); 82 private final Runnable closer; 83 84 /** {@code closer} may be run multiple times. */ Closer(Runnable closer)85 public Closer(Runnable closer) { 86 this.closer = Preconditions.checkNotNull(closer, "closer"); 87 } 88 } 89 90 private static class CloserComparator implements Comparator<Closer> { compare(Closer c1, Closer c2)91 @Override public int compare(Closer c1, Closer c2) { 92 return Long.compare(c1.id, c2.id); 93 } 94 } 95 } 96