MCPcopy Create free account
hub / github.com/apache/impala / ThreadTokenAvailableCb

Method ThreadTokenAvailableCb

be/src/exec/hdfs-scan-node.cc:255–368  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

253}
254
255void HdfsScanNode::ThreadTokenAvailableCb(ThreadResourcePool* pool) {
256 // This is called to start up new scanner threads. It's not a big deal if we
257 // spin up more than strictly necessary since they will go through and terminate
258 // promptly. However, we want to minimize that by checking a conditions.
259 // 1. Don't start up if the ScanNode is done
260 // 2. Don't start up if all the ranges have been taken by another thread.
261 // 3. Don't start up if the number of ranges left is less than the number of
262 // active scanner threads.
263 // 4. Don't start up if no initial ranges have been issued (see IMPALA-1722).
264 // 5. Don't start up a ScannerThread if the row batch queue is full since
265 // we are not scanner bound.
266 // 6. Don't start up a thread if there is not enough memory available for the
267 // estimated memory consumption (include reservation and non-reserved memory).
268 // 7. Don't start up a thread if it is an extra thread and we can't reserve another
269 // minimum reservation's worth of memory for the thread.
270 // 8. Don't start up more than maximum number of scanner threads configured.
271 // 9. Don't start up if there are no thread tokens.
272
273 // Case 4. We have not issued the initial ranges so don't start a scanner thread.
274 // Issuing ranges will call this function and we'll start the scanner threads then.
275 // TODO: It would be good to have a test case for that.
276 if (!initial_ranges_issued_.Load()) return;
277
278 ScannerMemLimiter* scanner_mem_limiter =
279 runtime_state_->query_state()->scanner_mem_limiter();
280 Status status = Status::OK();
281 while (true) {
282 // The lock must be given up between loops in order to give writers to done_,
283 // all_ranges_started_ etc. a chance to grab the lock.
284 // IMPALA-8322: Another thread can hold this lock for significant periods of time
285 // if the scan node is being cancelled. Since this function can be called while
286 // holding other locks that can block other threads (e.g. ThreadResourceMgr::lock_),
287 // avoid blocking unnecessarily. There are two remedies. First, we do a check of
288 // done() to try to avoid acquiring the lock_, as there is nothing to do if
289 // the scan node is done. Second, this uses a timeout of 10 milliseconds when
290 // acquiring the lock_ to allow a periodic check of done(). The 10 millisecond
291 // timeout is arbitrary.
292 // TODO: This still leans heavily on starvation-free locks, come up with a more
293 // correct way to communicate between this method and ScannerThread().
294 if (done()) break;
295 unique_lock<timed_mutex> lock(lock_, std::chrono::milliseconds(10));
296 if (!lock.owns_lock()) {
297 continue;
298 }
299
300 const int64_t num_active_scanner_threads = thread_state_.GetNumActive();
301 const bool first_thread = num_active_scanner_threads == 0;
302 const int64_t est_mem = thread_state_.estimated_per_thread_mem();
303 const int64_t scanner_thread_reservation = resource_profile_.min_reservation;
304 // Cases 1, 2, 3.
305 if (done() || all_ranges_started_ ||
306 num_active_scanner_threads >= shared_state_->progress().remaining()) {
307 break;
308 }
309
310 if (!first_thread) {
311 // Cases 5, 6 and 7.
312 if (thread_state_.batch_queue()->IsFull()) break;

Callers

nothing calls this directly

Calls 15

ScannerThreadMethod · 0.95
OKFunction · 0.85
SubstituteFunction · 0.85
PrintIdFunction · 0.85
GetThreadDebugInfoFunction · 0.85
CreateClass · 0.85
discard_resultFunction · 0.85
moveFunction · 0.85
scanner_mem_limiterMethod · 0.80
owns_lockMethod · 0.80
GetNumActiveMethod · 0.80

Tested by

no test coverage detected