| 94 | } |
| 95 | |
| 96 | bool MemPool::FindChunk(int64_t min_size, bool check_limits) noexcept { |
| 97 | // Try to allocate from a free chunk. We may have free chunks after the current chunk |
| 98 | // if Clear() was called. The current chunk may be free if ReturnPartialAllocation() |
| 99 | // was called. The first free chunk (if there is one) can therefore be either the |
| 100 | // current chunk or the chunk immediately after the current chunk. |
| 101 | int first_free_idx; |
| 102 | if (current_chunk_idx_ == -1) { |
| 103 | first_free_idx = 0; |
| 104 | } else { |
| 105 | DCHECK_GE(current_chunk_idx_, 0); |
| 106 | first_free_idx = current_chunk_idx_ + |
| 107 | (chunks_[current_chunk_idx_].allocated_bytes > 0); |
| 108 | } |
| 109 | for (int idx = current_chunk_idx_ + 1; idx < chunks_.size(); ++idx) { |
| 110 | // All chunks after 'current_chunk_idx_' should be free. |
| 111 | DCHECK_EQ(chunks_[idx].allocated_bytes, 0); |
| 112 | if (chunks_[idx].size >= min_size) { |
| 113 | // This chunk is big enough. Move it before the other free chunks. |
| 114 | if (idx != first_free_idx) std::swap(chunks_[idx], chunks_[first_free_idx]); |
| 115 | current_chunk_idx_ = first_free_idx; |
| 116 | DCHECK(CheckIntegrity(true)); |
| 117 | return true; |
| 118 | } |
| 119 | } |
| 120 | |
| 121 | // Didn't find a big enough free chunk - need to allocate new chunk. |
| 122 | int64_t chunk_size; |
| 123 | DCHECK_LE(next_chunk_size_, MAX_CHUNK_SIZE); |
| 124 | DCHECK_GE(next_chunk_size_, INITIAL_CHUNK_SIZE); |
| 125 | chunk_size = max<int64_t>(min_size, next_chunk_size_); |
| 126 | if (enforce_binary_chunk_sizes_) chunk_size = BitUtil::RoundUpToPowerOfTwo(chunk_size); |
| 127 | if (check_limits) { |
| 128 | if (!mem_tracker_->TryConsume(chunk_size)) return false; |
| 129 | } else { |
| 130 | mem_tracker_->Consume(chunk_size); |
| 131 | } |
| 132 | |
| 133 | MonotonicStopWatch sys_alloc_sw; |
| 134 | sys_alloc_sw.Start(); |
| 135 | // Allocate a new chunk. Return early if malloc fails. |
| 136 | uint8_t* buf = reinterpret_cast<uint8_t*>(malloc(chunk_size)); |
| 137 | uint64_t duration = sys_alloc_sw.ElapsedTime(); |
| 138 | counters_.sys_alloc_duration.UpdateCounter(duration); |
| 139 | if (UNLIKELY(buf == NULL)) { |
| 140 | mem_tracker_->Release(chunk_size); |
| 141 | return false; |
| 142 | } |
| 143 | counters_.allocated_bytes.UpdateCounter(chunk_size); |
| 144 | ASAN_POISON_MEMORY_REGION(buf, chunk_size); |
| 145 | |
| 146 | // Put it before the first free chunk. If no free chunks, it goes at the end. |
| 147 | if (first_free_idx == static_cast<int>(chunks_.size())) { |
| 148 | chunks_.push_back(ChunkInfo(chunk_size, buf)); |
| 149 | } else { |
| 150 | chunks_.insert(chunks_.begin() + first_free_idx, ChunkInfo(chunk_size, buf)); |
| 151 | } |
| 152 | current_chunk_idx_ = first_free_idx; |
| 153 | total_reserved_bytes_ += chunk_size; |
nothing calls this directly
no test coverage detected