xref: /aosp_15_r20/external/icing/icing/result/result-state-manager.cc (revision 8b6cd535a057e39b3b86660c4aa06c99747c2136)
1*8b6cd535SAndroid Build Coastguard Worker // Copyright (C) 2019 Google LLC
2*8b6cd535SAndroid Build Coastguard Worker //
3*8b6cd535SAndroid Build Coastguard Worker // Licensed under the Apache License, Version 2.0 (the "License");
4*8b6cd535SAndroid Build Coastguard Worker // you may not use this file except in compliance with the License.
5*8b6cd535SAndroid Build Coastguard Worker // You may obtain a copy of the License at
6*8b6cd535SAndroid Build Coastguard Worker //
7*8b6cd535SAndroid Build Coastguard Worker //      http://www.apache.org/licenses/LICENSE-2.0
8*8b6cd535SAndroid Build Coastguard Worker //
9*8b6cd535SAndroid Build Coastguard Worker // Unless required by applicable law or agreed to in writing, software
10*8b6cd535SAndroid Build Coastguard Worker // distributed under the License is distributed on an "AS IS" BASIS,
11*8b6cd535SAndroid Build Coastguard Worker // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*8b6cd535SAndroid Build Coastguard Worker // See the License for the specific language governing permissions and
13*8b6cd535SAndroid Build Coastguard Worker // limitations under the License.
14*8b6cd535SAndroid Build Coastguard Worker 
15*8b6cd535SAndroid Build Coastguard Worker #include "icing/result/result-state-manager.h"
16*8b6cd535SAndroid Build Coastguard Worker 
17*8b6cd535SAndroid Build Coastguard Worker #include <memory>
18*8b6cd535SAndroid Build Coastguard Worker #include <queue>
19*8b6cd535SAndroid Build Coastguard Worker #include <utility>
20*8b6cd535SAndroid Build Coastguard Worker 
21*8b6cd535SAndroid Build Coastguard Worker #include "icing/result/page-result.h"
22*8b6cd535SAndroid Build Coastguard Worker #include "icing/result/result-adjustment-info.h"
23*8b6cd535SAndroid Build Coastguard Worker #include "icing/result/result-retriever-v2.h"
24*8b6cd535SAndroid Build Coastguard Worker #include "icing/result/result-state-v2.h"
25*8b6cd535SAndroid Build Coastguard Worker #include "icing/scoring/scored-document-hits-ranker.h"
26*8b6cd535SAndroid Build Coastguard Worker #include "icing/util/clock.h"
27*8b6cd535SAndroid Build Coastguard Worker #include "icing/util/logging.h"
28*8b6cd535SAndroid Build Coastguard Worker #include "icing/util/status-macros.h"
29*8b6cd535SAndroid Build Coastguard Worker 
30*8b6cd535SAndroid Build Coastguard Worker namespace icing {
31*8b6cd535SAndroid Build Coastguard Worker namespace lib {
32*8b6cd535SAndroid Build Coastguard Worker 
ResultStateManager(int max_total_hits,const DocumentStore & document_store)33*8b6cd535SAndroid Build Coastguard Worker ResultStateManager::ResultStateManager(int max_total_hits,
34*8b6cd535SAndroid Build Coastguard Worker                                        const DocumentStore& document_store)
35*8b6cd535SAndroid Build Coastguard Worker     : document_store_(document_store),
36*8b6cd535SAndroid Build Coastguard Worker       max_total_hits_(max_total_hits),
37*8b6cd535SAndroid Build Coastguard Worker       num_total_hits_(0),
38*8b6cd535SAndroid Build Coastguard Worker       random_generator_(GetSteadyTimeNanoseconds()) {}
39*8b6cd535SAndroid Build Coastguard Worker 
40*8b6cd535SAndroid Build Coastguard Worker libtextclassifier3::StatusOr<std::pair<uint64_t, PageResult>>
CacheAndRetrieveFirstPage(std::unique_ptr<ScoredDocumentHitsRanker> ranker,std::unique_ptr<ResultAdjustmentInfo> parent_adjustment_info,std::unique_ptr<ResultAdjustmentInfo> child_adjustment_info,const ResultSpecProto & result_spec,const DocumentStore & document_store,const ResultRetrieverV2 & result_retriever,int64_t current_time_ms)41*8b6cd535SAndroid Build Coastguard Worker ResultStateManager::CacheAndRetrieveFirstPage(
42*8b6cd535SAndroid Build Coastguard Worker     std::unique_ptr<ScoredDocumentHitsRanker> ranker,
43*8b6cd535SAndroid Build Coastguard Worker     std::unique_ptr<ResultAdjustmentInfo> parent_adjustment_info,
44*8b6cd535SAndroid Build Coastguard Worker     std::unique_ptr<ResultAdjustmentInfo> child_adjustment_info,
45*8b6cd535SAndroid Build Coastguard Worker     const ResultSpecProto& result_spec, const DocumentStore& document_store,
46*8b6cd535SAndroid Build Coastguard Worker     const ResultRetrieverV2& result_retriever, int64_t current_time_ms) {
47*8b6cd535SAndroid Build Coastguard Worker   if (ranker == nullptr) {
48*8b6cd535SAndroid Build Coastguard Worker     return absl_ports::InvalidArgumentError("Should not provide null ranker");
49*8b6cd535SAndroid Build Coastguard Worker   }
50*8b6cd535SAndroid Build Coastguard Worker 
51*8b6cd535SAndroid Build Coastguard Worker   // Create shared pointer of ResultState.
52*8b6cd535SAndroid Build Coastguard Worker   // ResultState should be created by ResultStateManager only.
53*8b6cd535SAndroid Build Coastguard Worker   std::shared_ptr<ResultStateV2> result_state = std::make_shared<ResultStateV2>(
54*8b6cd535SAndroid Build Coastguard Worker       std::move(ranker), std::move(parent_adjustment_info),
55*8b6cd535SAndroid Build Coastguard Worker       std::move(child_adjustment_info), result_spec, document_store);
56*8b6cd535SAndroid Build Coastguard Worker 
57*8b6cd535SAndroid Build Coastguard Worker   // Retrieve docs outside of ResultStateManager critical section.
58*8b6cd535SAndroid Build Coastguard Worker   // Will enter ResultState critical section inside ResultRetriever.
59*8b6cd535SAndroid Build Coastguard Worker   auto [page_result, has_more_results] =
60*8b6cd535SAndroid Build Coastguard Worker       result_retriever.RetrieveNextPage(*result_state, current_time_ms);
61*8b6cd535SAndroid Build Coastguard Worker   if (!has_more_results) {
62*8b6cd535SAndroid Build Coastguard Worker     // No more pages, won't store ResultState, returns directly
63*8b6cd535SAndroid Build Coastguard Worker     return std::make_pair(kInvalidNextPageToken, std::move(page_result));
64*8b6cd535SAndroid Build Coastguard Worker   }
65*8b6cd535SAndroid Build Coastguard Worker 
66*8b6cd535SAndroid Build Coastguard Worker   // ResultState has multiple pages, storing it
67*8b6cd535SAndroid Build Coastguard Worker   int num_hits_to_add = 0;
68*8b6cd535SAndroid Build Coastguard Worker   {
69*8b6cd535SAndroid Build Coastguard Worker     // ResultState critical section
70*8b6cd535SAndroid Build Coastguard Worker     absl_ports::unique_lock l(&result_state->mutex);
71*8b6cd535SAndroid Build Coastguard Worker 
72*8b6cd535SAndroid Build Coastguard Worker     result_state->scored_document_hits_ranker->TruncateHitsTo(max_total_hits_);
73*8b6cd535SAndroid Build Coastguard Worker     result_state->RegisterNumTotalHits(&num_total_hits_);
74*8b6cd535SAndroid Build Coastguard Worker     num_hits_to_add = result_state->scored_document_hits_ranker->size();
75*8b6cd535SAndroid Build Coastguard Worker   }
76*8b6cd535SAndroid Build Coastguard Worker 
77*8b6cd535SAndroid Build Coastguard Worker   // It is fine to exit ResultState critical section, since it is just created
78*8b6cd535SAndroid Build Coastguard Worker   // above and only this thread (this call stack) has access to it. Thus, it
79*8b6cd535SAndroid Build Coastguard Worker   // won't be changed during the gap before we enter ResultStateManager critical
80*8b6cd535SAndroid Build Coastguard Worker   // section.
81*8b6cd535SAndroid Build Coastguard Worker   uint64_t next_page_token = kInvalidNextPageToken;
82*8b6cd535SAndroid Build Coastguard Worker   {
83*8b6cd535SAndroid Build Coastguard Worker     // ResultStateManager critical section
84*8b6cd535SAndroid Build Coastguard Worker     absl_ports::unique_lock l(&mutex_);
85*8b6cd535SAndroid Build Coastguard Worker 
86*8b6cd535SAndroid Build Coastguard Worker     // Remove expired result states first.
87*8b6cd535SAndroid Build Coastguard Worker     InternalInvalidateExpiredResultStates(kDefaultResultStateTtlInMs,
88*8b6cd535SAndroid Build Coastguard Worker                                           current_time_ms);
89*8b6cd535SAndroid Build Coastguard Worker     // Remove states to make room for this new state.
90*8b6cd535SAndroid Build Coastguard Worker     RemoveStatesIfNeeded(num_hits_to_add);
91*8b6cd535SAndroid Build Coastguard Worker     // Generate a new unique token and add it into result_state_map_.
92*8b6cd535SAndroid Build Coastguard Worker     next_page_token = Add(std::move(result_state), current_time_ms);
93*8b6cd535SAndroid Build Coastguard Worker   }
94*8b6cd535SAndroid Build Coastguard Worker 
95*8b6cd535SAndroid Build Coastguard Worker   return std::make_pair(next_page_token, std::move(page_result));
96*8b6cd535SAndroid Build Coastguard Worker }
97*8b6cd535SAndroid Build Coastguard Worker 
Add(std::shared_ptr<ResultStateV2> result_state,int64_t current_time_ms)98*8b6cd535SAndroid Build Coastguard Worker uint64_t ResultStateManager::Add(std::shared_ptr<ResultStateV2> result_state,
99*8b6cd535SAndroid Build Coastguard Worker                                  int64_t current_time_ms) {
100*8b6cd535SAndroid Build Coastguard Worker   uint64_t new_token = GetUniqueToken();
101*8b6cd535SAndroid Build Coastguard Worker 
102*8b6cd535SAndroid Build Coastguard Worker   result_state_map_.emplace(new_token, std::move(result_state));
103*8b6cd535SAndroid Build Coastguard Worker   // Tracks the insertion order
104*8b6cd535SAndroid Build Coastguard Worker   token_queue_.push(std::make_pair(new_token, current_time_ms));
105*8b6cd535SAndroid Build Coastguard Worker 
106*8b6cd535SAndroid Build Coastguard Worker   return new_token;
107*8b6cd535SAndroid Build Coastguard Worker }
108*8b6cd535SAndroid Build Coastguard Worker 
109*8b6cd535SAndroid Build Coastguard Worker libtextclassifier3::StatusOr<std::pair<uint64_t, PageResult>>
GetNextPage(uint64_t next_page_token,const ResultRetrieverV2 & result_retriever,int64_t current_time_ms)110*8b6cd535SAndroid Build Coastguard Worker ResultStateManager::GetNextPage(uint64_t next_page_token,
111*8b6cd535SAndroid Build Coastguard Worker                                 const ResultRetrieverV2& result_retriever,
112*8b6cd535SAndroid Build Coastguard Worker                                 int64_t current_time_ms) {
113*8b6cd535SAndroid Build Coastguard Worker   std::shared_ptr<ResultStateV2> result_state = nullptr;
114*8b6cd535SAndroid Build Coastguard Worker   {
115*8b6cd535SAndroid Build Coastguard Worker     // ResultStateManager critical section
116*8b6cd535SAndroid Build Coastguard Worker     absl_ports::unique_lock l(&mutex_);
117*8b6cd535SAndroid Build Coastguard Worker 
118*8b6cd535SAndroid Build Coastguard Worker     // Remove expired result states before fetching
119*8b6cd535SAndroid Build Coastguard Worker     InternalInvalidateExpiredResultStates(kDefaultResultStateTtlInMs,
120*8b6cd535SAndroid Build Coastguard Worker                                           current_time_ms);
121*8b6cd535SAndroid Build Coastguard Worker 
122*8b6cd535SAndroid Build Coastguard Worker     const auto& state_iterator = result_state_map_.find(next_page_token);
123*8b6cd535SAndroid Build Coastguard Worker     if (state_iterator == result_state_map_.end()) {
124*8b6cd535SAndroid Build Coastguard Worker       return absl_ports::NotFoundError("next_page_token not found");
125*8b6cd535SAndroid Build Coastguard Worker     }
126*8b6cd535SAndroid Build Coastguard Worker     result_state = state_iterator->second;
127*8b6cd535SAndroid Build Coastguard Worker   }
128*8b6cd535SAndroid Build Coastguard Worker 
129*8b6cd535SAndroid Build Coastguard Worker   // Retrieve docs outside of ResultStateManager critical section.
130*8b6cd535SAndroid Build Coastguard Worker   // Will enter ResultState critical section inside ResultRetriever.
131*8b6cd535SAndroid Build Coastguard Worker   auto [page_result, has_more_results] =
132*8b6cd535SAndroid Build Coastguard Worker       result_retriever.RetrieveNextPage(*result_state, current_time_ms);
133*8b6cd535SAndroid Build Coastguard Worker 
134*8b6cd535SAndroid Build Coastguard Worker   if (!has_more_results) {
135*8b6cd535SAndroid Build Coastguard Worker     {
136*8b6cd535SAndroid Build Coastguard Worker       // ResultStateManager critical section
137*8b6cd535SAndroid Build Coastguard Worker       absl_ports::unique_lock l(&mutex_);
138*8b6cd535SAndroid Build Coastguard Worker 
139*8b6cd535SAndroid Build Coastguard Worker       InternalInvalidateResultState(next_page_token);
140*8b6cd535SAndroid Build Coastguard Worker     }
141*8b6cd535SAndroid Build Coastguard Worker 
142*8b6cd535SAndroid Build Coastguard Worker     next_page_token = kInvalidNextPageToken;
143*8b6cd535SAndroid Build Coastguard Worker   }
144*8b6cd535SAndroid Build Coastguard Worker   return std::make_pair(next_page_token, std::move(page_result));
145*8b6cd535SAndroid Build Coastguard Worker }
146*8b6cd535SAndroid Build Coastguard Worker 
InvalidateResultState(uint64_t next_page_token)147*8b6cd535SAndroid Build Coastguard Worker void ResultStateManager::InvalidateResultState(uint64_t next_page_token) {
148*8b6cd535SAndroid Build Coastguard Worker   if (next_page_token == kInvalidNextPageToken) {
149*8b6cd535SAndroid Build Coastguard Worker     return;
150*8b6cd535SAndroid Build Coastguard Worker   }
151*8b6cd535SAndroid Build Coastguard Worker 
152*8b6cd535SAndroid Build Coastguard Worker   absl_ports::unique_lock l(&mutex_);
153*8b6cd535SAndroid Build Coastguard Worker 
154*8b6cd535SAndroid Build Coastguard Worker   InternalInvalidateResultState(next_page_token);
155*8b6cd535SAndroid Build Coastguard Worker }
156*8b6cd535SAndroid Build Coastguard Worker 
InvalidateAllResultStates()157*8b6cd535SAndroid Build Coastguard Worker void ResultStateManager::InvalidateAllResultStates() {
158*8b6cd535SAndroid Build Coastguard Worker   absl_ports::unique_lock l(&mutex_);
159*8b6cd535SAndroid Build Coastguard Worker   InternalInvalidateAllResultStates();
160*8b6cd535SAndroid Build Coastguard Worker }
161*8b6cd535SAndroid Build Coastguard Worker 
InternalInvalidateAllResultStates()162*8b6cd535SAndroid Build Coastguard Worker void ResultStateManager::InternalInvalidateAllResultStates() {
163*8b6cd535SAndroid Build Coastguard Worker   // We don't have to reset num_total_hits_ (to 0) here, since clearing
164*8b6cd535SAndroid Build Coastguard Worker   // result_state_map_ will "eventually" invoke the destructor of ResultState
165*8b6cd535SAndroid Build Coastguard Worker   // (which decrements num_total_hits_) and num_total_hits_ will become 0.
166*8b6cd535SAndroid Build Coastguard Worker   result_state_map_.clear();
167*8b6cd535SAndroid Build Coastguard Worker   invalidated_token_set_.clear();
168*8b6cd535SAndroid Build Coastguard Worker   token_queue_ = std::queue<std::pair<uint64_t, int64_t>>();
169*8b6cd535SAndroid Build Coastguard Worker }
170*8b6cd535SAndroid Build Coastguard Worker 
GetUniqueToken()171*8b6cd535SAndroid Build Coastguard Worker uint64_t ResultStateManager::GetUniqueToken() {
172*8b6cd535SAndroid Build Coastguard Worker   uint64_t new_token = random_generator_();
173*8b6cd535SAndroid Build Coastguard Worker   // There's a small chance of collision between the random numbers, here we're
174*8b6cd535SAndroid Build Coastguard Worker   // trying to avoid any collisions by checking the keys.
175*8b6cd535SAndroid Build Coastguard Worker   while (result_state_map_.find(new_token) != result_state_map_.end() ||
176*8b6cd535SAndroid Build Coastguard Worker          invalidated_token_set_.find(new_token) !=
177*8b6cd535SAndroid Build Coastguard Worker              invalidated_token_set_.end() ||
178*8b6cd535SAndroid Build Coastguard Worker          new_token == kInvalidNextPageToken) {
179*8b6cd535SAndroid Build Coastguard Worker     new_token = random_generator_();
180*8b6cd535SAndroid Build Coastguard Worker   }
181*8b6cd535SAndroid Build Coastguard Worker   return new_token;
182*8b6cd535SAndroid Build Coastguard Worker }
183*8b6cd535SAndroid Build Coastguard Worker 
RemoveStatesIfNeeded(int num_hits_to_add)184*8b6cd535SAndroid Build Coastguard Worker void ResultStateManager::RemoveStatesIfNeeded(int num_hits_to_add) {
185*8b6cd535SAndroid Build Coastguard Worker   if (result_state_map_.empty() || token_queue_.empty()) {
186*8b6cd535SAndroid Build Coastguard Worker     return;
187*8b6cd535SAndroid Build Coastguard Worker   }
188*8b6cd535SAndroid Build Coastguard Worker 
189*8b6cd535SAndroid Build Coastguard Worker   // 1. Check if this new result_state would take up the entire result state
190*8b6cd535SAndroid Build Coastguard Worker   // manager budget.
191*8b6cd535SAndroid Build Coastguard Worker   if (num_hits_to_add > max_total_hits_) {
192*8b6cd535SAndroid Build Coastguard Worker     // This single result state will exceed our budget. Drop everything else to
193*8b6cd535SAndroid Build Coastguard Worker     // accomodate it.
194*8b6cd535SAndroid Build Coastguard Worker     InternalInvalidateAllResultStates();
195*8b6cd535SAndroid Build Coastguard Worker     return;
196*8b6cd535SAndroid Build Coastguard Worker   }
197*8b6cd535SAndroid Build Coastguard Worker 
198*8b6cd535SAndroid Build Coastguard Worker   // 2. Remove any tokens that were previously invalidated.
199*8b6cd535SAndroid Build Coastguard Worker   while (!token_queue_.empty() &&
200*8b6cd535SAndroid Build Coastguard Worker          invalidated_token_set_.find(token_queue_.front().first) !=
201*8b6cd535SAndroid Build Coastguard Worker              invalidated_token_set_.end()) {
202*8b6cd535SAndroid Build Coastguard Worker     invalidated_token_set_.erase(token_queue_.front().first);
203*8b6cd535SAndroid Build Coastguard Worker     token_queue_.pop();
204*8b6cd535SAndroid Build Coastguard Worker   }
205*8b6cd535SAndroid Build Coastguard Worker 
206*8b6cd535SAndroid Build Coastguard Worker   // 3. If we're over budget, remove states from oldest to newest until we fit
207*8b6cd535SAndroid Build Coastguard Worker   // into our budget.
208*8b6cd535SAndroid Build Coastguard Worker   // Note: num_total_hits_ may not be decremented immediately after invalidating
209*8b6cd535SAndroid Build Coastguard Worker   // a result state, since other threads may still hold the shared pointer.
210*8b6cd535SAndroid Build Coastguard Worker   // Thus, we have to check if token_queue_ is empty or not, since it is
211*8b6cd535SAndroid Build Coastguard Worker   // possible that num_total_hits_ is non-zero and still greater than
212*8b6cd535SAndroid Build Coastguard Worker   // max_total_hits_ when token_queue_ is empty. Still "eventually" it will be
213*8b6cd535SAndroid Build Coastguard Worker   // decremented after the last thread releases the shared pointer.
214*8b6cd535SAndroid Build Coastguard Worker   while (!token_queue_.empty() && num_total_hits_ > max_total_hits_) {
215*8b6cd535SAndroid Build Coastguard Worker     InternalInvalidateResultState(token_queue_.front().first);
216*8b6cd535SAndroid Build Coastguard Worker     token_queue_.pop();
217*8b6cd535SAndroid Build Coastguard Worker   }
218*8b6cd535SAndroid Build Coastguard Worker   invalidated_token_set_.clear();
219*8b6cd535SAndroid Build Coastguard Worker }
220*8b6cd535SAndroid Build Coastguard Worker 
InternalInvalidateResultState(uint64_t token)221*8b6cd535SAndroid Build Coastguard Worker void ResultStateManager::InternalInvalidateResultState(uint64_t token) {
222*8b6cd535SAndroid Build Coastguard Worker   // Removes the entry in result_state_map_ and insert the token into
223*8b6cd535SAndroid Build Coastguard Worker   // invalidated_token_set_. The entry in token_queue_ can't be easily removed
224*8b6cd535SAndroid Build Coastguard Worker   // right now (may need O(n) time), so we leave it there and later completely
225*8b6cd535SAndroid Build Coastguard Worker   // remove the token in RemoveStatesIfNeeded().
226*8b6cd535SAndroid Build Coastguard Worker   auto itr = result_state_map_.find(token);
227*8b6cd535SAndroid Build Coastguard Worker   if (itr != result_state_map_.end()) {
228*8b6cd535SAndroid Build Coastguard Worker     // We don't have to decrement num_total_hits_ here, since erasing the shared
229*8b6cd535SAndroid Build Coastguard Worker     // ptr instance will "eventually" invoke the destructor of ResultState and
230*8b6cd535SAndroid Build Coastguard Worker     // it will handle this.
231*8b6cd535SAndroid Build Coastguard Worker     result_state_map_.erase(itr);
232*8b6cd535SAndroid Build Coastguard Worker     invalidated_token_set_.insert(token);
233*8b6cd535SAndroid Build Coastguard Worker   }
234*8b6cd535SAndroid Build Coastguard Worker }
235*8b6cd535SAndroid Build Coastguard Worker 
InternalInvalidateExpiredResultStates(int64_t result_state_ttl,int64_t current_time_ms)236*8b6cd535SAndroid Build Coastguard Worker void ResultStateManager::InternalInvalidateExpiredResultStates(
237*8b6cd535SAndroid Build Coastguard Worker     int64_t result_state_ttl, int64_t current_time_ms) {
238*8b6cd535SAndroid Build Coastguard Worker   while (!token_queue_.empty() &&
239*8b6cd535SAndroid Build Coastguard Worker          current_time_ms - token_queue_.front().second >= result_state_ttl) {
240*8b6cd535SAndroid Build Coastguard Worker     auto itr = result_state_map_.find(token_queue_.front().first);
241*8b6cd535SAndroid Build Coastguard Worker     if (itr != result_state_map_.end()) {
242*8b6cd535SAndroid Build Coastguard Worker       // We don't have to decrement num_total_hits_ here, since erasing the
243*8b6cd535SAndroid Build Coastguard Worker       // shared ptr instance will "eventually" invoke the destructor of
244*8b6cd535SAndroid Build Coastguard Worker       // ResultState and it will handle this.
245*8b6cd535SAndroid Build Coastguard Worker       result_state_map_.erase(itr);
246*8b6cd535SAndroid Build Coastguard Worker     } else {
247*8b6cd535SAndroid Build Coastguard Worker       // Since result_state_map_ and invalidated_token_set_ are mutually
248*8b6cd535SAndroid Build Coastguard Worker       // exclusive, we remove the token from invalidated_token_set_ only if it
249*8b6cd535SAndroid Build Coastguard Worker       // isn't present in result_state_map_.
250*8b6cd535SAndroid Build Coastguard Worker       invalidated_token_set_.erase(token_queue_.front().first);
251*8b6cd535SAndroid Build Coastguard Worker     }
252*8b6cd535SAndroid Build Coastguard Worker     token_queue_.pop();
253*8b6cd535SAndroid Build Coastguard Worker   }
254*8b6cd535SAndroid Build Coastguard Worker }
255*8b6cd535SAndroid Build Coastguard Worker 
256*8b6cd535SAndroid Build Coastguard Worker }  // namespace lib
257*8b6cd535SAndroid Build Coastguard Worker }  // namespace icing
258