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

Method TransferReservationTo

be/src/runtime/bufferpool/reservation-tracker.cc:284–369  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

282}
283
284bool ReservationTracker::TransferReservationTo(ReservationTracker* other, int64_t bytes) {
285 if (other == this) return true;
286 // Find the path to the root from both. The root is guaranteed to be a common ancestor.
287 vector<ReservationTracker*> path_to_common = FindPathToRoot();
288 vector<ReservationTracker*> other_path_to_common = other->FindPathToRoot();
289 DCHECK_EQ(path_to_common.back(), other_path_to_common.back());
290 ReservationTracker* common_ancestor = path_to_common.back();
291 // Remove any common ancestors - they do not need to be updated for this transfer.
292 while (!path_to_common.empty() && !other_path_to_common.empty()
293 && path_to_common.back() == other_path_to_common.back()) {
294 common_ancestor = path_to_common.back();
295 path_to_common.pop_back();
296 other_path_to_common.pop_back();
297 }
298
299 // At this point, we have three cases:
300 // 1. 'common_ancestor' == 'other'. 'other_path_to_common' is empty because 'other' is
301 // the lowest common ancestor. To transfer, we decrease the reservation on the
302 // trackers under 'other', down to 'this'.
303 // 2. 'common_ancestor' == 'this'. 'path_to_common' is empty because 'this' is the
304 // lowest common ancestor. To transfer, we increase the reservation on the trackers
305 // under 'this', down to 'other'.
306 // 3. Neither is an ancestor of the other. Both 'other_path_to_common' and
307 // 'path_to_common' are non-empty. We increase the reservation on trackers from
308 // 'other' up to one below the common ancestor (checking limits as needed) and if
309 // successful, decrease reservations on trackers from 'this' up to one below the
310 // common ancestor.
311
312 // Lock all of the trackers so we can do the update atomically. Need to be careful to
313 // lock subtrees in the correct order.
314 vector<unique_lock<SpinLock>> locks;
315 bool lock_first = path_to_common.empty() || other_path_to_common.empty()
316 || lock_sibling_subtree_first(path_to_common.back(), other_path_to_common.back());
317 if (lock_first) {
318 for (ReservationTracker* tracker : path_to_common) locks.emplace_back(tracker->lock_);
319 }
320 for (ReservationTracker* tracker : other_path_to_common) {
321 locks.emplace_back(tracker->lock_);
322 }
323 if (!lock_first) {
324 for (ReservationTracker* tracker : path_to_common) locks.emplace_back(tracker->lock_);
325 }
326
327 // Check reservation limits will not be violated before applying any updates.
328 for (ReservationTracker* tracker : other_path_to_common) {
329 if (tracker->reservation_.Load() + bytes > tracker->reservation_limit_.Load()) {
330 return false;
331 }
332 }
333
334 // Do the updates now that we have checked the limits. We're holding all the locks
335 // so this is all atomic.
336 for (ReservationTracker* tracker : other_path_to_common) {
337 tracker->UpdateReservation(bytes);
338 // We don't handle MemTrackers with limit in this function - this should always
339 // succeed.
340 DCHECK(tracker->mem_tracker_ == nullptr || !tracker->mem_tracker_->has_limit());
341 bool success = tracker->TryConsumeFromMemTracker(bytes, MemLimit::HARD);

Callers

nothing calls this directly

Calls 9

FindPathToRootMethod · 0.80
UpdateReservationMethod · 0.80
ReleaseToMemTrackerMethod · 0.80
emptyMethod · 0.45
LoadMethod · 0.45
has_limitMethod · 0.45
AddMethod · 0.45
CheckConsistencyMethod · 0.45

Tested by

no test coverage detected