OLD | NEW |
---|---|
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "storage/browser/blob/blob_async_builder_host.h" | 5 #include "storage/browser/blob/blob_async_builder_host.h" |
6 | 6 |
7 #include <stddef.h> | 7 #include <stddef.h> |
8 #include <stdint.h> | 8 #include <stdint.h> |
9 | 9 |
10 #include <memory> | 10 #include <memory> |
11 #include <utility> | 11 #include <utility> |
12 | 12 |
13 #include "base/bind.h" | 13 #include "base/bind.h" |
14 #include "base/memory/ptr_util.h" | 14 #include "base/memory/ptr_util.h" |
15 #include "base/memory/shared_memory.h" | 15 #include "base/memory/shared_memory.h" |
16 #include "storage/browser/blob/blob_data_handle.h" | 16 #include "storage/browser/blob/blob_data_handle.h" |
17 #include "storage/browser/blob/blob_memory_controller.h" | |
17 #include "storage/browser/blob/blob_storage_context.h" | 18 #include "storage/browser/blob/blob_storage_context.h" |
18 | 19 |
19 namespace storage { | 20 namespace storage { |
20 namespace { | 21 namespace { |
22 using MemoryStrategyResult = BlobMemoryController::MemoryStrategyResult; | |
23 using MemoryItemRequest = | |
24 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; | |
21 | 25 |
22 bool CalculateBlobMemorySize(const std::vector<DataElement>& elements, | 26 bool CalculateBlobMemorySize(const std::vector<DataElement>& elements, |
23 size_t* shortcut_bytes, | 27 size_t* shortcut_bytes, |
24 uint64_t* total_bytes) { | 28 uint64_t* total_bytes) { |
25 DCHECK(shortcut_bytes); | 29 DCHECK(shortcut_bytes); |
26 DCHECK(total_bytes); | 30 DCHECK(total_bytes); |
31 | |
27 base::CheckedNumeric<uint64_t> total_size_checked = 0; | 32 base::CheckedNumeric<uint64_t> total_size_checked = 0; |
28 base::CheckedNumeric<size_t> shortcut_size_checked = 0; | 33 base::CheckedNumeric<size_t> shortcut_size_checked = 0; |
29 for (const auto& e : elements) { | 34 for (const auto& e : elements) { |
30 if (e.type() == DataElement::TYPE_BYTES) { | 35 if (e.type() == DataElement::TYPE_BYTES) { |
31 total_size_checked += e.length(); | 36 total_size_checked += e.length(); |
32 shortcut_size_checked += e.length(); | 37 shortcut_size_checked += e.length(); |
33 } else if (e.type() == DataElement::TYPE_BYTES_DESCRIPTION) { | 38 } else if (e.type() == DataElement::TYPE_BYTES_DESCRIPTION) { |
34 total_size_checked += e.length(); | 39 total_size_checked += e.length(); |
35 } else { | 40 } else { |
36 continue; | 41 continue; |
37 } | 42 } |
38 if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid()) { | 43 if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid()) |
39 return false; | 44 return false; |
40 } | |
41 } | 45 } |
42 *shortcut_bytes = shortcut_size_checked.ValueOrDie(); | 46 *shortcut_bytes = shortcut_size_checked.ValueOrDie(); |
43 *total_bytes = total_size_checked.ValueOrDie(); | 47 *total_bytes = total_size_checked.ValueOrDie(); |
44 return true; | 48 return true; |
45 } | 49 } |
46 | |
47 IPCBlobCreationCancelCode ConvertReferencedBlobErrorToConstructingError( | |
48 IPCBlobCreationCancelCode referenced_blob_error) { | |
49 switch (referenced_blob_error) { | |
50 // For most cases we propagate the error. | |
51 case IPCBlobCreationCancelCode::FILE_WRITE_FAILED: | |
52 case IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT: | |
53 case IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN: | |
54 case IPCBlobCreationCancelCode::OUT_OF_MEMORY: | |
55 return referenced_blob_error; | |
56 // Others we report that the referenced blob is broken, as we don't know | |
57 // why (the BLOB_DEREFERENCED_WHILE_BUILDING should never happen, as we hold | |
58 // onto the reference of the blobs we're using). | |
59 case IPCBlobCreationCancelCode::BLOB_DEREFERENCED_WHILE_BUILDING: | |
60 DCHECK(false) << "Referenced blob should never be dereferenced while we " | |
61 << "are depending on it, as our system holds a handle."; | |
62 case IPCBlobCreationCancelCode::UNKNOWN: | |
63 return IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; | |
64 } | |
65 NOTREACHED(); | |
66 return IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN; | |
67 } | |
68 | |
69 } // namespace | 50 } // namespace |
70 | 51 |
71 using MemoryItemRequest = | |
72 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; | |
73 | |
74 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( | 52 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( |
75 const std::string& uuid, | 53 const std::string& uuid) |
76 std::set<std::string> referenced_blob_uuids, | 54 : data_builder(uuid) {} |
77 std::vector<std::unique_ptr<BlobDataHandle>>* referenced_blob_handles) | |
78 : data_builder(uuid), | |
79 referenced_blob_uuids(referenced_blob_uuids), | |
80 referenced_blob_handles(std::move(*referenced_blob_handles)) {} | |
81 | 55 |
82 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} | 56 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} |
83 | 57 |
84 BlobAsyncBuilderHost::BlobAsyncBuilderHost() : ptr_factory_(this) {} | 58 BlobAsyncBuilderHost::BlobAsyncBuilderHost() : ptr_factory_(this) {} |
85 | 59 |
86 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} | 60 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} |
87 | 61 |
88 BlobTransportResult BlobAsyncBuilderHost::RegisterBlobUUID( | 62 BlobStatus BlobAsyncBuilderHost::RegisterBlob( |
89 const std::string& uuid, | 63 const std::string& uuid, |
90 const std::string& content_type, | 64 const std::string& content_type, |
91 const std::string& content_disposition, | 65 const std::string& content_disposition, |
92 const std::set<std::string>& referenced_blob_uuids, | |
93 BlobStorageContext* context) { | |
94 if (async_blob_map_.find(uuid) != async_blob_map_.end()) | |
95 return BlobTransportResult::BAD_IPC; | |
96 if (referenced_blob_uuids.find(uuid) != referenced_blob_uuids.end()) | |
97 return BlobTransportResult::BAD_IPC; | |
98 context->CreatePendingBlob(uuid, content_type, content_disposition); | |
99 std::vector<std::unique_ptr<BlobDataHandle>> handles; | |
100 for (const std::string& referenced_uuid : referenced_blob_uuids) { | |
101 std::unique_ptr<BlobDataHandle> handle = | |
102 context->GetBlobDataFromUUID(referenced_uuid); | |
103 if (!handle || handle->IsBroken()) { | |
104 // We cancel the blob right away, and don't bother storing our state. | |
105 context->CancelPendingBlob( | |
106 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); | |
107 return BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN; | |
108 } | |
109 handles.emplace_back(std::move(handle)); | |
110 } | |
111 async_blob_map_[uuid] = base::WrapUnique( | |
112 new BlobBuildingState(uuid, referenced_blob_uuids, &handles)); | |
113 return BlobTransportResult::DONE; | |
114 } | |
115 | |
116 BlobTransportResult BlobAsyncBuilderHost::StartBuildingBlob( | |
117 const std::string& uuid, | |
118 const std::vector<DataElement>& elements, | 66 const std::vector<DataElement>& elements, |
119 size_t memory_available, | |
120 BlobStorageContext* context, | 67 BlobStorageContext* context, |
121 const RequestMemoryCallback& request_memory) { | 68 const RequestMemoryCallback& request_memory, |
69 const BlobStatusCallback& status_callback) { | |
michaeln
2016/08/15 22:44:43
does this get called more than once? if not, calli
dmurph
2016/08/19 00:18:32
Done.
| |
122 DCHECK(context); | 70 DCHECK(context); |
123 DCHECK(async_blob_map_.find(uuid) != async_blob_map_.end()); | 71 if (async_blob_map_.find(uuid) != async_blob_map_.end()) { |
124 | 72 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
125 // Step 1: Get the sizes. | 73 } |
126 size_t shortcut_memory_size_bytes = 0; | 74 |
127 uint64_t total_memory_size_bytes = 0; | 75 const BlobMemoryController& memory_controller = context->memory_controller(); |
128 if (!CalculateBlobMemorySize(elements, &shortcut_memory_size_bytes, | 76 uint64_t transport_memory_size = 0; |
129 &total_memory_size_bytes)) { | 77 size_t shortcut_size = 0; |
130 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 78 if (!CalculateBlobMemorySize(elements, &shortcut_size, |
131 return BlobTransportResult::BAD_IPC; | 79 &transport_memory_size)) |
132 } | 80 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
133 | 81 |
134 // Step 2: Check if we have enough memory to store the blob. | 82 MemoryStrategyResult memory_strategy = |
135 if (total_memory_size_bytes > memory_available) { | 83 memory_controller.DecideBlobTransportationMemoryStrategy( |
136 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, context); | 84 shortcut_size, transport_memory_size); |
137 return BlobTransportResult::CANCEL_MEMORY_FULL; | 85 |
138 } | 86 // Validate that our referenced blobs aren't us. |
139 | 87 for (const DataElement& e : elements) { |
140 // From here on, we know we can fit the blob in memory. | 88 if (e.type() == DataElement::TYPE_BLOB && e.blob_uuid() == uuid) |
141 BlobBuildingState* state_ptr = async_blob_map_[uuid].get(); | 89 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
142 if (!state_ptr->request_builder.requests().empty()) { | 90 } |
143 // Check that we're not a duplicate call. | 91 |
144 return BlobTransportResult::BAD_IPC; | 92 std::unique_ptr<BlobBuildingState> state(new BlobBuildingState(uuid)); |
145 } | 93 state->data_builder.set_content_type(content_type); |
94 state->data_builder.set_content_disposition(content_disposition); | |
95 state->request_memory_callback = request_memory; | |
96 | |
97 std::unique_ptr<BlobDataHandle> temp_handle; | |
98 | |
99 switch (memory_strategy) { | |
100 case MemoryStrategyResult::TOO_LARGE: | |
101 temp_handle = | |
102 context->AddBrokenBlob(uuid, content_type, content_disposition, | |
103 BlobStatus::ERR_OUT_OF_MEMORY); | |
104 context->IncrementBlobRefCount(uuid); | |
105 return BlobStatus::ERR_OUT_OF_MEMORY; | |
106 case MemoryStrategyResult::NONE_NEEDED: { | |
107 for (const DataElement& e : elements) { | |
108 DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION); | |
109 state->data_builder.AppendIPCDataElement(e); | |
110 } | |
111 temp_handle = | |
112 context->BuildBlob(state->data_builder, | |
113 BlobStorageContext::PopulatationAllowedCallback()); | |
114 context->IncrementBlobRefCount(uuid); | |
115 return BlobStatus::DONE; | |
116 } | |
117 case MemoryStrategyResult::IPC: | |
118 state->strategy = IPCBlobItemRequestStrategy::IPC; | |
119 state->request_builder.InitializeForIPCRequests( | |
120 memory_controller.max_ipc_memory_size(), transport_memory_size, | |
121 elements, &(state->data_builder)); | |
122 break; | |
123 case MemoryStrategyResult::SHARED_MEMORY: | |
124 state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY; | |
125 state->request_builder.InitializeForSharedMemoryRequests( | |
126 memory_controller.max_shared_memory_size(), transport_memory_size, | |
127 elements, &(state->data_builder)); | |
128 break; | |
129 case MemoryStrategyResult::FILE: | |
130 state->strategy = IPCBlobItemRequestStrategy::FILE; | |
131 state->request_builder.InitializeForFileRequests( | |
132 memory_controller.max_file_size(), transport_memory_size, elements, | |
133 &(state->data_builder)); | |
134 break; | |
135 } | |
136 // We initialize our requests received state now that they are populated. | |
137 state->request_received.resize(state->request_builder.requests().size(), | |
138 false); | |
139 | |
140 BlobBuildingState* state_ptr = state.get(); | |
141 async_blob_map_[uuid] = std::move(state); | |
142 | |
146 state_ptr->request_memory_callback = request_memory; | 143 state_ptr->request_memory_callback = request_memory; |
147 | 144 state_ptr->status_callback = status_callback; |
michaeln
2016/08/15 22:44:43
comletion callback?
dmurph
2016/08/19 00:18:32
Done.
| |
148 // Step 3: Check to make sure the referenced blob information we received | 145 |
149 // earlier is correct: | 146 // We special case the file strategy, where we know they don't have to fit in |
150 std::set<std::string> extracted_blob_uuids; | 147 // memory, and we create the temporary files right away. |
michaeln
2016/08/15 22:44:43
confusing comment since the block of code under th
dmurph
2016/08/19 00:18:32
Done.
| |
151 for (const DataElement& e : elements) { | 148 if (memory_strategy == MemoryStrategyResult::FILE) { |
152 if (e.type() == DataElement::TYPE_BLOB) { | 149 LOG(ERROR) << "file!"; |
153 extracted_blob_uuids.insert(e.blob_uuid()); | 150 } |
154 // We can't depend on ourselves. | 151 |
155 if (e.blob_uuid() == uuid) { | 152 temp_handle = context->BuildBlob( |
156 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 153 state_ptr->data_builder, |
157 return BlobTransportResult::BAD_IPC; | 154 base::Bind(&BlobAsyncBuilderHost::OnCanStartBuildingBlob, |
158 } | 155 ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr())); |
159 } | 156 context->IncrementBlobRefCount(uuid); |
160 } | 157 |
161 if (extracted_blob_uuids != state_ptr->referenced_blob_uuids) { | 158 if (temp_handle->IsBroken()) { |
162 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 159 async_blob_map_.erase(uuid); |
163 return BlobTransportResult::BAD_IPC; | 160 } |
164 } | 161 return temp_handle->GetBlobStatus(); |
165 | 162 } |
166 // Step 4: Decide if we're using the shortcut method. This will also catch | 163 |
167 // the case where we don't have any memory items. | 164 BlobStatus BlobAsyncBuilderHost::OnMemoryResponses( |
168 if (shortcut_memory_size_bytes == total_memory_size_bytes && | |
169 shortcut_memory_size_bytes <= memory_available) { | |
170 for (const DataElement& e : elements) { | |
171 state_ptr->data_builder.AppendIPCDataElement(e); | |
172 } | |
173 FinishBuildingBlob(state_ptr, context); | |
174 return BlobTransportResult::DONE; | |
175 } | |
176 | |
177 // From here on, we know the blob's size is less than |memory_available|, | |
178 // so we know we're < max(size_t). | |
179 // Step 5: Decide if we're using shared memory. | |
180 if (total_memory_size_bytes > max_ipc_memory_size_) { | |
181 state_ptr->request_builder.InitializeForSharedMemoryRequests( | |
182 max_shared_memory_size_, total_memory_size_bytes, elements, | |
183 &(state_ptr->data_builder)); | |
184 } else { | |
185 // Step 6: We can fit in IPC. | |
186 state_ptr->request_builder.InitializeForIPCRequests( | |
187 max_ipc_memory_size_, total_memory_size_bytes, elements, | |
188 &(state_ptr->data_builder)); | |
189 } | |
190 // We initialize our requests received state now that they are populated. | |
191 state_ptr->request_received.resize( | |
192 state_ptr->request_builder.requests().size(), false); | |
193 return ContinueBlobMemoryRequests(uuid, context); | |
194 } | |
195 | |
196 BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( | |
197 const std::string& uuid, | 165 const std::string& uuid, |
198 const std::vector<BlobItemBytesResponse>& responses, | 166 const std::vector<BlobItemBytesResponse>& responses, |
199 BlobStorageContext* context) { | 167 BlobStorageContext* context) { |
200 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | 168 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
201 if (state_it == async_blob_map_.end()) { | 169 if (state_it == async_blob_map_.end()) { |
202 DVLOG(1) << "Could not find blob " << uuid; | 170 DVLOG(1) << "Could not find blob " << uuid; |
203 return BlobTransportResult::BAD_IPC; | 171 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
204 } | 172 } |
205 if (responses.empty()) { | 173 if (responses.empty()) { |
206 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 174 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
207 return BlobTransportResult::BAD_IPC; | 175 context); |
208 } | 176 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
177 } | |
178 | |
179 // Validate response sanity: it should refer to a legal request number, and | |
180 // we shouldn't have received an answer for that request yet. | |
209 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | 181 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
210 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | 182 const auto& requests = state->request_builder.requests(); |
211 const auto& requests = request_builder.requests(); | |
212 for (const BlobItemBytesResponse& response : responses) { | 183 for (const BlobItemBytesResponse& response : responses) { |
213 if (response.request_number >= requests.size()) { | 184 if (response.request_number >= requests.size()) { |
214 // Bad IPC, so we delete our record and ignore. | 185 // Bad IPC, so we delete our record and ignore. |
215 DVLOG(1) << "Invalid request number " << response.request_number; | 186 DVLOG(1) << "Invalid request number " << response.request_number; |
216 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 187 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
217 return BlobTransportResult::BAD_IPC; | 188 context); |
189 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
218 } | 190 } |
219 DCHECK_LT(response.request_number, state->request_received.size()); | 191 DCHECK_LT(response.request_number, state->request_received.size()); |
220 const MemoryItemRequest& request = requests[response.request_number]; | |
221 if (state->request_received[response.request_number]) { | 192 if (state->request_received[response.request_number]) { |
222 // Bad IPC, so we delete our record. | 193 // Bad IPC, so we delete our record. |
223 DVLOG(1) << "Already received response for that request."; | 194 DVLOG(1) << "Already received response for that request."; |
224 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | 195 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
225 return BlobTransportResult::BAD_IPC; | 196 context); |
197 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
226 } | 198 } |
227 state->request_received[response.request_number] = true; | 199 state->request_received[response.request_number] = true; |
228 bool invalid_ipc = false; | 200 } |
229 bool memory_error = false; | 201 switch (state->strategy) { |
230 switch (request.message.transport_strategy) { | 202 case IPCBlobItemRequestStrategy::IPC: |
231 case IPCBlobItemRequestStrategy::IPC: | 203 return OnIPCResponses(uuid, state, responses, context); |
232 if (response.inline_data.size() < request.message.size) { | 204 case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
233 DVLOG(1) << "Invalid data size " << response.inline_data.size() | 205 return OnSharedMemoryResponses(uuid, state, responses, context); |
234 << " vs requested size of " << request.message.size; | 206 case IPCBlobItemRequestStrategy::FILE: |
235 invalid_ipc = true; | 207 return OnFileResponses(uuid, state, responses, context); |
236 break; | 208 case IPCBlobItemRequestStrategy::UNKNOWN: |
237 } | 209 break; |
238 invalid_ipc = !state->data_builder.PopulateFutureData( | 210 } |
239 request.browser_item_index, &response.inline_data[0], | 211 NOTREACHED(); |
240 request.browser_item_offset, request.message.size); | 212 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
241 break; | |
242 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
243 if (state->num_shared_memory_requests == 0) { | |
244 DVLOG(1) << "Received too many responses for shared memory."; | |
245 invalid_ipc = true; | |
246 break; | |
247 } | |
248 state->num_shared_memory_requests--; | |
249 if (!state->shared_memory_block->memory()) { | |
250 // We just map the whole block, as we'll probably be accessing the | |
251 // whole thing in this group of responses. Another option is to use | |
252 // MapAt, remove the mapped boolean, and then exclude the | |
253 // handle_offset below. | |
254 size_t handle_size = request_builder.shared_memory_sizes() | |
255 [state->current_shared_memory_handle_index]; | |
256 if (!state->shared_memory_block->Map(handle_size)) { | |
257 DVLOG(1) << "Unable to map memory to size " << handle_size; | |
258 memory_error = true; | |
259 break; | |
260 } | |
261 } | |
262 | |
263 invalid_ipc = !state->data_builder.PopulateFutureData( | |
264 request.browser_item_index, | |
265 static_cast<const char*>(state->shared_memory_block->memory()) + | |
266 request.message.handle_offset, | |
267 request.browser_item_offset, request.message.size); | |
268 break; | |
269 case IPCBlobItemRequestStrategy::FILE: | |
270 case IPCBlobItemRequestStrategy::UNKNOWN: | |
271 DVLOG(1) << "Not implemented."; | |
272 invalid_ipc = true; | |
273 break; | |
274 } | |
275 if (invalid_ipc) { | |
276 // Bad IPC, so we delete our record and return false. | |
277 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); | |
278 return BlobTransportResult::BAD_IPC; | |
279 } | |
280 if (memory_error) { | |
281 DVLOG(1) << "Shared memory error."; | |
282 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, | |
283 context); | |
284 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
285 } | |
286 state->num_fulfilled_requests++; | |
287 } | |
288 return ContinueBlobMemoryRequests(uuid, context); | |
289 } | 213 } |
290 | 214 |
291 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, | 215 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, |
292 IPCBlobCreationCancelCode code, | 216 BlobStatus code, |
293 BlobStorageContext* context) { | 217 BlobStorageContext* context) { |
294 DCHECK(context); | 218 DCHECK(context); |
219 DCHECK(BlobStatusIsError(code)); | |
295 auto state_it = async_blob_map_.find(uuid); | 220 auto state_it = async_blob_map_.find(uuid); |
296 if (state_it == async_blob_map_.end()) { | 221 if (state_it == async_blob_map_.end()) |
297 return; | 222 return; |
298 } | |
299 // We can have the blob dereferenced by the renderer, but have it still being | 223 // We can have the blob dereferenced by the renderer, but have it still being |
300 // 'built'. In this case, it's destructed in the context, but we still have | 224 // 'built'. In this case, it's destructed in the context, but we still have |
301 // it in our map. Hence we make sure the context has the entry before | 225 // it in our map. Hence we make sure the context has the entry before |
302 // calling cancel. | 226 // calling cancel. |
227 async_blob_map_.erase(state_it); | |
303 if (context->registry().HasEntry(uuid)) | 228 if (context->registry().HasEntry(uuid)) |
304 context->CancelPendingBlob(uuid, code); | 229 context->BreakAndFinishPendingBlob(uuid, code); |
305 async_blob_map_.erase(state_it); | |
306 } | 230 } |
307 | 231 |
308 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { | 232 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { |
309 DCHECK(context); | 233 DCHECK(context); |
310 // If the blob still exists in the context (and is being built), then we know | 234 // If the blob still exists in the context, then we know that someone else is |
311 // that someone else is expecting our blob, and we need to cancel it to let | 235 // expecting our blob, and we need to cancel it to let the dependency know |
312 // the dependency know it's gone. | 236 // it's gone. |
313 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs; | 237 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs; |
314 for (const auto& uuid_state_pair : async_blob_map_) { | 238 for (const auto& uuid_state_pair : async_blob_map_) { |
315 if (context->IsBeingBuilt(uuid_state_pair.first)) { | 239 std::unique_ptr<BlobDataHandle> handle = |
316 referenced_pending_blobs.emplace_back( | 240 context->GetBlobDataFromUUID(uuid_state_pair.first); |
241 if (handle) { | |
242 referenced_pending_blobs.push_back( | |
317 context->GetBlobDataFromUUID(uuid_state_pair.first)); | 243 context->GetBlobDataFromUUID(uuid_state_pair.first)); |
318 } | 244 } |
319 } | 245 } |
320 // We clear the map before canceling them to prevent any strange reentry into | 246 // We clear the map before canceling them to prevent any strange reentry into |
321 // our class (see ReferencedBlobFinished) if any blobs were waiting for others | 247 // our class (see OnCanStartBuildingBlob) if any blobs were waiting for others |
322 // to construct. | 248 // to construct. |
323 async_blob_map_.clear(); | 249 async_blob_map_.clear(); |
324 for (const std::unique_ptr<BlobDataHandle>& handle : | 250 for (const std::unique_ptr<BlobDataHandle>& handle : |
325 referenced_pending_blobs) { | 251 referenced_pending_blobs) { |
326 context->CancelPendingBlob( | 252 context->BreakAndFinishPendingBlob(handle->uuid(), |
327 handle->uuid(), IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); | 253 BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT); |
328 } | 254 } |
329 } | 255 } |
330 | 256 |
331 BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( | 257 BlobStatus BlobAsyncBuilderHost::StartRequests( |
332 const std::string& uuid, | 258 const std::string& uuid, |
259 BlobBuildingState* state, | |
260 BlobStorageContext* context, | |
261 std::vector<BlobMemoryController::FileCreationInfo> file_infos) { | |
262 switch (state->strategy) { | |
263 case IPCBlobItemRequestStrategy::IPC: | |
264 DCHECK(file_infos.empty()); | |
265 SendIPCRequests(state, context); | |
266 return BlobStatus::PENDING; | |
267 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
268 DCHECK(file_infos.empty()); | |
269 return ContinueSharedMemoryRequests(uuid, state, context); | |
270 case IPCBlobItemRequestStrategy::FILE: | |
271 DCHECK(!file_infos.empty()); | |
272 SendFileRequests(state, context, std::move(file_infos)); | |
273 return BlobStatus::PENDING; | |
274 case IPCBlobItemRequestStrategy::UNKNOWN: | |
275 break; | |
276 } | |
277 NOTREACHED(); | |
278 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
279 } | |
280 | |
281 // Note: This can be called when we cancel a blob in the context. | |
282 void BlobAsyncBuilderHost::OnCanStartBuildingBlob( | |
283 const std::string& uuid, | |
284 base::WeakPtr<BlobStorageContext> context, | |
285 BlobStatus status, | |
286 std::vector<BlobMemoryController::FileCreationInfo> file_infos) { | |
287 LOG(ERROR) << "We can fit now!"; | |
288 if (!context) { | |
289 async_blob_map_.erase(uuid); | |
290 return; | |
291 } | |
292 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | |
293 if (state_it == async_blob_map_.end()) | |
294 return; | |
295 | |
296 BlobBuildingState* state = state_it->second.get(); | |
297 if (status == BlobStatus::PENDING) { | |
298 status = StartRequests(uuid, state, context.get(), std::move(file_infos)); | |
299 if (status == BlobStatus::PENDING) | |
300 return; | |
301 } else { | |
302 LOG(ERROR) << "Got error state!"; | |
303 } | |
304 BlobStatusCallback status_callback = state->status_callback; | |
305 async_blob_map_.erase(state_it); | |
306 status_callback.Run(status); | |
307 } | |
308 | |
309 void BlobAsyncBuilderHost::SendIPCRequests(BlobBuildingState* state, | |
310 BlobStorageContext* context) { | |
311 const std::vector<MemoryItemRequest>& requests = | |
312 state->request_builder.requests(); | |
313 std::vector<BlobItemBytesRequest> byte_requests; | |
314 | |
315 DCHECK(!requests.empty()); | |
316 for (const MemoryItemRequest& request : requests) { | |
317 byte_requests.push_back(request.message); | |
318 } | |
319 | |
320 state->request_memory_callback.Run(std::move(byte_requests), | |
321 std::vector<base::SharedMemoryHandle>(), | |
322 std::vector<base::File>()); | |
323 } | |
324 | |
325 BlobStatus BlobAsyncBuilderHost::OnIPCResponses( | |
326 const std::string& uuid, | |
327 BlobBuildingState* state, | |
328 const std::vector<BlobItemBytesResponse>& responses, | |
333 BlobStorageContext* context) { | 329 BlobStorageContext* context) { |
334 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | 330 const auto& requests = state->request_builder.requests(); |
335 DCHECK(state_it != async_blob_map_.end()); | 331 size_t num_requests = requests.size(); |
336 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | 332 for (const BlobItemBytesResponse& response : responses) { |
337 | 333 const MemoryItemRequest& request = requests[response.request_number]; |
334 if (response.inline_data.size() < request.message.size) { | |
335 DVLOG(1) << "Invalid data size " << response.inline_data.size() | |
336 << " vs requested size of " << request.message.size; | |
337 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, | |
338 context); | |
339 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
340 } | |
341 bool success = state->data_builder.PopulateFutureData( | |
342 request.browser_item_index, response.inline_data.data(), | |
343 request.browser_item_offset, request.message.size); | |
344 if (!success) { | |
345 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, | |
346 context); | |
347 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
348 } | |
349 state->num_fulfilled_requests++; | |
350 } | |
351 if (state->num_fulfilled_requests == num_requests) { | |
352 FinishBuildingBlob(state, context); | |
353 return BlobStatus::DONE; | |
354 } | |
355 return BlobStatus::PENDING; | |
356 } | |
357 | |
358 BlobStatus BlobAsyncBuilderHost::ContinueSharedMemoryRequests( | |
359 const std::string& uuid, | |
360 BlobBuildingState* state, | |
361 BlobStorageContext* context) { | |
338 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | 362 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
339 const std::vector<MemoryItemRequest>& requests = request_builder.requests(); | 363 const std::vector<MemoryItemRequest>& requests = request_builder.requests(); |
340 size_t num_requests = requests.size(); | 364 size_t num_requests = requests.size(); |
341 if (state->num_fulfilled_requests == num_requests) { | |
342 FinishBuildingBlob(state, context); | |
343 return BlobTransportResult::DONE; | |
344 } | |
345 DCHECK_LT(state->num_fulfilled_requests, num_requests); | 365 DCHECK_LT(state->num_fulfilled_requests, num_requests); |
346 if (state->next_request == num_requests) { | 366 if (state->next_request == num_requests) { |
347 // We are still waiting on other requests to come back. | 367 // We are still waiting on other requests to come back. |
348 return BlobTransportResult::PENDING_RESPONSES; | 368 return BlobStatus::PENDING; |
349 } | 369 } |
350 | 370 |
351 std::unique_ptr<std::vector<BlobItemBytesRequest>> byte_requests( | 371 std::vector<BlobItemBytesRequest> byte_requests; |
352 new std::vector<BlobItemBytesRequest>()); | 372 std::vector<base::SharedMemoryHandle> shared_memory; |
353 std::unique_ptr<std::vector<base::SharedMemoryHandle>> shared_memory( | |
354 new std::vector<base::SharedMemoryHandle>()); | |
355 | 373 |
356 for (; state->next_request < num_requests; ++state->next_request) { | 374 for (; state->next_request < num_requests; ++state->next_request) { |
357 const MemoryItemRequest& request = requests[state->next_request]; | 375 const MemoryItemRequest& request = requests[state->next_request]; |
358 | |
359 bool stop_accumulating = false; | |
360 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; | 376 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; |
361 switch (request.message.transport_strategy) { | 377 if (using_shared_memory_handle && |
362 case IPCBlobItemRequestStrategy::IPC: | 378 state->current_shared_memory_handle_index != |
363 byte_requests->push_back(request.message); | 379 request.message.handle_index) { |
364 break; | 380 // We only want one shared memory per requesting blob. |
365 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | 381 break; |
366 if (using_shared_memory_handle && | 382 } |
367 state->current_shared_memory_handle_index != | 383 state->current_shared_memory_handle_index = request.message.handle_index; |
368 request.message.handle_index) { | 384 state->num_shared_memory_requests++; |
369 // We only want one shared memory per requesting blob. | 385 |
370 stop_accumulating = true; | 386 if (!state->shared_memory_block) { |
371 break; | 387 state->shared_memory_block.reset(new base::SharedMemory()); |
372 } | 388 size_t size = |
373 using_shared_memory_handle = true; | 389 request_builder.shared_memory_sizes()[request.message.handle_index]; |
374 state->current_shared_memory_handle_index = | 390 if (!state->shared_memory_block->CreateAnonymous(size)) { |
375 request.message.handle_index; | 391 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; |
376 state->num_shared_memory_requests++; | 392 return BlobStatus::ERR_OUT_OF_MEMORY; |
377 | 393 } |
378 if (!state->shared_memory_block) { | 394 } |
379 state->shared_memory_block.reset(new base::SharedMemory()); | 395 shared_memory.push_back(state->shared_memory_block->handle()); |
380 size_t size = | 396 byte_requests.push_back(request.message); |
381 request_builder | 397 // Since we are only using one handle at a time, transform our handle |
382 .shared_memory_sizes()[request.message.handle_index]; | 398 // index correctly back to 0. |
383 if (!state->shared_memory_block->CreateAnonymous(size)) { | 399 byte_requests.back().handle_index = 0; |
384 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; | |
385 return BlobTransportResult::CANCEL_MEMORY_FULL; | |
386 } | |
387 } | |
388 shared_memory->push_back(state->shared_memory_block->handle()); | |
389 byte_requests->push_back(request.message); | |
390 // Since we are only using one handle at a time, transform our handle | |
391 // index correctly back to 0. | |
392 byte_requests->back().handle_index = 0; | |
393 break; | |
394 case IPCBlobItemRequestStrategy::FILE: | |
395 case IPCBlobItemRequestStrategy::UNKNOWN: | |
396 NOTREACHED() << "Not implemented yet."; | |
397 break; | |
398 } | |
399 if (stop_accumulating) { | |
400 break; | |
401 } | |
402 } | 400 } |
403 DCHECK(!requests.empty()); | 401 DCHECK(!requests.empty()); |
404 | 402 |
405 state->request_memory_callback.Run( | 403 state->request_memory_callback.Run(std::move(byte_requests), |
406 std::move(byte_requests), std::move(shared_memory), | 404 std::move(shared_memory), |
407 base::WrapUnique(new std::vector<base::File>())); | 405 std::vector<base::File>()); |
408 return BlobTransportResult::PENDING_RESPONSES; | 406 return BlobStatus::PENDING; |
409 } | 407 } |
410 | 408 |
411 void BlobAsyncBuilderHost::ReferencedBlobFinished( | 409 BlobStatus BlobAsyncBuilderHost::OnSharedMemoryResponses( |
412 const std::string& owning_blob_uuid, | 410 const std::string& uuid, |
413 base::WeakPtr<BlobStorageContext> context, | 411 BlobBuildingState* state, |
414 bool construction_success, | 412 const std::vector<BlobItemBytesResponse>& responses, |
415 IPCBlobCreationCancelCode reason) { | 413 BlobStorageContext* context) { |
416 if (!context) { | 414 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
417 return; | 415 const auto& requests = request_builder.requests(); |
418 } | 416 for (const BlobItemBytesResponse& response : responses) { |
419 auto state_it = async_blob_map_.find(owning_blob_uuid); | 417 const MemoryItemRequest& request = requests[response.request_number]; |
420 if (state_it == async_blob_map_.end()) { | 418 if (state->num_shared_memory_requests == 0) { |
421 return; | 419 DVLOG(1) << "Received too many responses for shared memory."; |
422 } | 420 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, |
423 if (!construction_success) { | 421 context); |
424 CancelBuildingBlob(owning_blob_uuid, | 422 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; |
425 ConvertReferencedBlobErrorToConstructingError(reason), | 423 } |
426 context.get()); | 424 state->num_shared_memory_requests--; |
427 return; | 425 if (!state->shared_memory_block->memory()) { |
428 } | 426 // We just map the whole block, as we'll probably be accessing the |
429 BlobBuildingState* state = state_it->second.get(); | 427 // whole thing in this group of responses. |
430 DCHECK_GT(state->num_referenced_blobs_building, 0u); | 428 size_t handle_size = |
431 if (--state->num_referenced_blobs_building == 0) { | 429 request_builder |
432 context->CompletePendingBlob(state->data_builder); | 430 .shared_memory_sizes()[state->current_shared_memory_handle_index]; |
433 async_blob_map_.erase(state->data_builder.uuid()); | 431 if (!state->shared_memory_block->Map(handle_size)) { |
434 } | 432 DVLOG(1) << "Unable to map memory to size " << handle_size; |
433 CancelBuildingBlob(uuid, BlobStatus::ERR_OUT_OF_MEMORY, context); | |
434 return BlobStatus::ERR_OUT_OF_MEMORY; | |
435 } | |
436 } | |
437 | |
438 bool success = state->data_builder.PopulateFutureData( | |
439 request.browser_item_index, | |
440 static_cast<const char*>(state->shared_memory_block->memory()) + | |
441 request.message.handle_offset, | |
442 request.browser_item_offset, request.message.size); | |
443 | |
444 if (!success) { | |
445 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, | |
446 context); | |
447 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
448 } | |
449 state->num_fulfilled_requests++; | |
450 } | |
451 if (state->num_fulfilled_requests == requests.size()) { | |
452 FinishBuildingBlob(state, context); | |
453 return BlobStatus::DONE; | |
454 } | |
455 return ContinueSharedMemoryRequests(uuid, state, context); | |
456 } | |
457 | |
458 void BlobAsyncBuilderHost::SendFileRequests( | |
459 BlobBuildingState* state, | |
460 BlobStorageContext* context, | |
461 std::vector<BlobMemoryController::FileCreationInfo> file_infos) { | |
462 LOG(ERROR) << "Files created for renderer."; | |
463 | |
464 std::vector<base::File> files; | |
465 | |
466 for (BlobMemoryController::FileCreationInfo& file_info : file_infos) { | |
467 state->files.push_back(std::move(file_info.file_reference)); | |
468 files.push_back(std::move(file_info.file)); | |
469 } | |
470 | |
471 const std::vector<MemoryItemRequest>& requests = | |
472 state->request_builder.requests(); | |
473 std::vector<BlobItemBytesRequest> byte_requests; | |
474 | |
475 DCHECK(!requests.empty()); | |
476 for (const MemoryItemRequest& request : requests) { | |
477 byte_requests.push_back(request.message); | |
478 } | |
479 | |
480 state->request_memory_callback.Run(std::move(byte_requests), | |
481 std::vector<base::SharedMemoryHandle>(), | |
482 std::move(files)); | |
483 } | |
484 | |
485 BlobStatus BlobAsyncBuilderHost::OnFileResponses( | |
486 const std::string& uuid, | |
487 BlobBuildingState* state, | |
488 const std::vector<BlobItemBytesResponse>& responses, | |
489 BlobStorageContext* context) { | |
490 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; | |
491 const auto& requests = request_builder.requests(); | |
492 for (const BlobItemBytesResponse& response : responses) { | |
493 const MemoryItemRequest& request = requests[response.request_number]; | |
494 const scoped_refptr<ShareableFileReference>& file_ref = | |
495 state->files[request.message.handle_index]; | |
496 bool success = state->data_builder.PopulateFutureFile( | |
497 request.browser_item_index, file_ref, response.time_file_modified); | |
498 if (!success) { | |
499 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS, | |
500 context); | |
501 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS; | |
502 } | |
503 state->num_fulfilled_requests++; | |
504 } | |
505 if (state->num_fulfilled_requests == requests.size()) { | |
506 FinishBuildingBlob(state, context); | |
507 return BlobStatus::DONE; | |
508 } | |
509 return BlobStatus::PENDING; | |
435 } | 510 } |
436 | 511 |
437 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, | 512 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, |
438 BlobStorageContext* context) { | 513 BlobStorageContext* context) { |
439 if (!state->referenced_blob_uuids.empty()) { | 514 std::string uuid = state->data_builder.uuid(); |
440 DCHECK_EQ(0u, state->num_referenced_blobs_building); | |
441 state->num_referenced_blobs_building = 0; | |
442 // We assume re-entry is not possible, as RunOnConstructionComplete | |
443 // will schedule a task when the blob is being built. Thus we can't have the | |
444 // case where |num_referenced_blobs_building| reaches 0 in the | |
445 // ReferencedBlobFinished method before we're finished looping. | |
446 for (const std::string& referenced_uuid : state->referenced_blob_uuids) { | |
447 if (context->IsBeingBuilt(referenced_uuid)) { | |
448 state->num_referenced_blobs_building++; | |
449 context->RunOnConstructionComplete( | |
450 referenced_uuid, | |
451 base::Bind(&BlobAsyncBuilderHost::ReferencedBlobFinished, | |
452 ptr_factory_.GetWeakPtr(), state->data_builder.uuid(), | |
453 context->AsWeakPtr())); | |
454 } | |
455 } | |
456 if (state->num_referenced_blobs_building > 0) { | |
457 // We wait until referenced blobs are done. | |
458 return; | |
459 } | |
460 } | |
461 context->CompletePendingBlob(state->data_builder); | |
462 async_blob_map_.erase(state->data_builder.uuid()); | 515 async_blob_map_.erase(state->data_builder.uuid()); |
516 context->FinishedPopulatingPendingBlob(uuid); | |
463 } | 517 } |
464 | 518 |
465 } // namespace storage | 519 } // namespace storage |
OLD | NEW |