1111import org .apache .lucene .search .FieldDoc ;
1212import org .apache .lucene .search .Query ;
1313import org .apache .lucene .search .TotalHits ;
14+ import org .elasticsearch .action .ActionListener ;
1415import org .elasticsearch .action .search .SearchType ;
16+ import org .elasticsearch .action .support .SubscribableListener ;
1517import org .elasticsearch .common .breaker .CircuitBreaker ;
1618import org .elasticsearch .core .Assertions ;
1719import org .elasticsearch .core .Nullable ;
1820import org .elasticsearch .core .Releasable ;
19- import org .elasticsearch .core .Releasables ;
2021import org .elasticsearch .core .TimeValue ;
2122import org .elasticsearch .index .cache .bitset .BitsetFilterCache ;
2223import org .elasticsearch .index .mapper .IdLoader ;
5657import java .util .List ;
5758import java .util .Map ;
5859import java .util .Set ;
59- import java .util .concurrent .CopyOnWriteArrayList ;
60- import java .util .concurrent .atomic .AtomicBoolean ;
6160
6261/**
6362 * This class encapsulates the state needed to execute a search. It holds a reference to the
@@ -71,13 +70,16 @@ public abstract class SearchContext implements Releasable {
7170 public static final int TRACK_TOTAL_HITS_DISABLED = -1 ;
7271 public static final int DEFAULT_TRACK_TOTAL_HITS_UP_TO = 10000 ;
7372
74- protected final List <Releasable > releasables = new CopyOnWriteArrayList <>();
75-
76- private final AtomicBoolean closed = new AtomicBoolean (false );
73+ protected final SubscribableListener <Void > closeFuture = new SubscribableListener <>();
7774
7875 {
7976 if (Assertions .ENABLED ) {
80- releasables .add (LeakTracker .wrap (() -> { assert closed .get (); }));
77+ closeFuture .addListener (ActionListener .releasing (LeakTracker .wrap (new Releasable () {
78+ @ Override
79+ public void close () {
80+ // empty instance that will actually get GC'ed so that the leak tracker works
81+ }
82+ })));
8183 }
8284 }
8385 private InnerHitsContext innerHitsContext ;
@@ -109,9 +111,7 @@ public final List<Runnable> getCancellationChecks() {
109111
110112 @ Override
111113 public final void close () {
112- if (closed .compareAndSet (false , true )) {
113- Releasables .close (releasables );
114- }
114+ closeFuture .onResponse (null );
115115 }
116116
117117 /**
@@ -399,8 +399,8 @@ public final boolean checkRealMemoryCB(int locallyAccumulatedBytes, String label
399399 * Adds a releasable that will be freed when this context is closed.
400400 */
401401 public void addReleasable (Releasable releasable ) { // TODO most Releasables are managed by their callers. We probably don't need this.
402- assert closed . get () == false ;
403- releasables . add ( releasable );
402+ assert closeFuture . isDone () == false ;
403+ closeFuture . addListener ( ActionListener . releasing ( releasable ) );
404404 }
405405
406406 /**
0 commit comments