| 94 | } |
| 95 | |
| 96 | Status RamFileBlockCache::MaybeFetch(const Key& key, |
| 97 | const std::shared_ptr<Block>& block) { |
| 98 | bool downloaded_block = false; |
| 99 | auto reconcile_state = |
| 100 | gtl::MakeCleanup([this, &downloaded_block, &key, &block] { |
| 101 | // Perform this action in a cleanup callback to avoid locking mu_ after |
| 102 | // locking block->mu. |
| 103 | if (downloaded_block) { |
| 104 | mutex_lock l(mu_); |
| 105 | // Do not update state if the block is already to be evicted. |
| 106 | if (block->timestamp != 0) { |
| 107 | // Use capacity() instead of size() to account for all memory |
| 108 | // used by the cache. |
| 109 | cache_size_ += block->data.capacity(); |
| 110 | // Put to beginning of LRA list. |
| 111 | lra_list_.erase(block->lra_iterator); |
| 112 | lra_list_.push_front(key); |
| 113 | block->lra_iterator = lra_list_.begin(); |
| 114 | block->timestamp = env_->NowSeconds(); |
| 115 | } |
| 116 | } |
| 117 | }); |
| 118 | // Loop until either block content is successfully fetched, or our request |
| 119 | // encounters an error. |
| 120 | mutex_lock l(block->mu); |
| 121 | Status status = Status::OK(); |
| 122 | while (true) { |
| 123 | switch (block->state) { |
| 124 | case FetchState::ERROR: |
| 125 | TF_FALLTHROUGH_INTENDED; |
| 126 | case FetchState::CREATED: |
| 127 | block->state = FetchState::FETCHING; |
| 128 | block->mu.unlock(); // Release the lock while making the API call. |
| 129 | block->data.clear(); |
| 130 | block->data.resize(block_size_, 0); |
| 131 | size_t bytes_transferred; |
| 132 | status.Update(block_fetcher_(key.first, key.second, block_size_, |
| 133 | block->data.data(), &bytes_transferred)); |
| 134 | block->mu.lock(); // Reacquire the lock immediately afterwards |
| 135 | if (status.ok()) { |
| 136 | block->data.resize(bytes_transferred, 0); |
| 137 | // Shrink the data capacity to the actual size used. |
| 138 | // NOLINTNEXTLINE: shrink_to_fit() may not shrink the capacity. |
| 139 | std::vector<char>(block->data).swap(block->data); |
| 140 | downloaded_block = true; |
| 141 | block->state = FetchState::FINISHED; |
| 142 | } else { |
| 143 | block->state = FetchState::ERROR; |
| 144 | } |
| 145 | block->cond_var.notify_all(); |
| 146 | return status; |
| 147 | case FetchState::FETCHING: |
| 148 | block->cond_var.wait_for(l, std::chrono::seconds(60)); |
| 149 | if (block->state == FetchState::FINISHED) { |
| 150 | return Status::OK(); |
| 151 | } |
| 152 | // Re-loop in case of errors. |
| 153 | break; |
nothing calls this directly
no test coverage detected