| 282 | } |
| 283 | |
| 284 | bool 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); |
nothing calls this directly
no test coverage detected