| 384 | } |
| 385 | |
| 386 | int64_t BufferPool::BufferAllocator::ScavengeBuffers( |
| 387 | bool slow_but_sure, int current_core, int64_t target_bytes) { |
| 388 | // There are two strategies for scavenging buffers: |
| 389 | // 1) Fast, opportunistic: Each arena is searched in succession. Although reservations |
| 390 | // guarantee that the memory we need is available somewhere, this may fail if we |
| 391 | // we race with another thread that returned buffers to an arena that we've already |
| 392 | // searched and took the buffers from an arena we haven't yet searched. |
| 393 | // 2) Slow, guaranteed to succeed: In order to ensure that we can find the memory in a |
| 394 | // single pass, we hold locks for all arenas we've already examined. That way, other |
| 395 | // threads can't take the memory that we need from an arena that we haven't yet |
| 396 | // examined (or from 'system_bytes_available_') because in order to do so, it would |
| 397 | // have had to return the equivalent amount of memory to an earlier arena or added |
| 398 | // it back into 'systems_bytes_reamining_'. The former can't happen since we're |
| 399 | // still holding those locks, and the latter is solved by trying to decrease |
| 400 | // system_bytes_remaining_ with DecreaseBytesRemaining() at the end. |
| 401 | DCHECK_GT(target_bytes, 0); |
| 402 | // First make sure we've used up all the headroom in the buffer limit. |
| 403 | int64_t bytes_found = |
| 404 | DecreaseBytesRemaining(target_bytes, false, &system_bytes_remaining_); |
| 405 | if (bytes_found == target_bytes) return bytes_found; |
| 406 | |
| 407 | // In 'slow_but_sure' mode, we will hold locks for multiple arenas at the same time and |
| 408 | // therefore must start at 0 to respect the lock order. Otherwise we start with the |
| 409 | // current core's arena for locality and to avoid excessive contention on arena 0. |
| 410 | int start_core = slow_but_sure ? 0 : current_core; |
| 411 | vector<std::unique_lock<SpinLock>> arena_locks; |
| 412 | if (slow_but_sure) arena_locks.resize(per_core_arenas_.size()); |
| 413 | |
| 414 | for (int i = 0; i < per_core_arenas_.size(); ++i) { |
| 415 | int core_to_check = (start_core + i) % per_core_arenas_.size(); |
| 416 | FreeBufferArena* arena = per_core_arenas_[core_to_check].get(); |
| 417 | int64_t bytes_needed = target_bytes - bytes_found; |
| 418 | bytes_found += arena->FreeSystemMemory(bytes_needed, bytes_needed, |
| 419 | slow_but_sure ? &arena_locks[i] : nullptr).second; |
| 420 | if (bytes_found == target_bytes) break; |
| 421 | } |
| 422 | DCHECK_LE(bytes_found, target_bytes); |
| 423 | |
| 424 | // Decrement 'system_bytes_remaining_' while still holding the arena locks to avoid |
| 425 | // the window for a race with another thread that removes a buffer from a list and |
| 426 | // then increments 'system_bytes_remaining_'. The race is prevented because the other |
| 427 | // thread holds the lock while decrementing 'system_bytes_remaining_' in the cases |
| 428 | // where it may not have reservation corresponding to that memory. |
| 429 | if (slow_but_sure && bytes_found < target_bytes) { |
| 430 | bytes_found += DecreaseBytesRemaining( |
| 431 | target_bytes - bytes_found, true, &system_bytes_remaining_); |
| 432 | DCHECK_EQ(bytes_found, target_bytes) << DebugString(); |
| 433 | } |
| 434 | return bytes_found; |
| 435 | } |
| 436 | |
| 437 | void BufferPool::BufferAllocator::Free(BufferHandle&& handle) { |
| 438 | DCHECK(handle.is_open()); |
nothing calls this directly
no test coverage detected