Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(412)

Side by Side Diff: storage/browser/blob/blob_async_builder_host.cc

Issue 2448353002: [BlobAsync] Moving async handling into BlobStorageContext & quota out. (Closed)
Patch Set: compile fix Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 MemoryStrategy = BlobMemoryController::Strategy;
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 void 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::MakeUnique<BlobBuildingState>(
112 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& completion_callback) {
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 completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
125 // Step 1: Get the sizes. 73 return;
126 size_t shortcut_memory_size_bytes = 0; 74 }
127 uint64_t total_memory_size_bytes = 0; 75
128 if (!CalculateBlobMemorySize(elements, &shortcut_memory_size_bytes, 76 const BlobMemoryController& memory_controller = context->memory_controller();
michaeln 2016/10/28 19:38:29 This local can be declared closer to where it's fi
dmurph 2016/10/28 22:13:55 Done.
129 &total_memory_size_bytes)) { 77 uint64_t transport_memory_size = 0;
130 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); 78 size_t shortcut_size = 0;
131 return BlobTransportResult::BAD_IPC; 79 if (!CalculateBlobMemorySize(elements, &shortcut_size,
132 } 80 &transport_memory_size)) {
133 81 completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
134 // Step 2: Check if we have enough memory to store the blob. 82 return;
135 if (total_memory_size_bytes > memory_available) { 83 }
136 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, context); 84
137 return BlobTransportResult::CANCEL_MEMORY_FULL; 85 MemoryStrategy memory_strategy =
138 } 86 memory_controller.DetermineStrategy(shortcut_size, transport_memory_size);
139 87
140 // From here on, we know we can fit the blob in memory. 88 // Validate that our referenced blobs aren't us.
141 BlobBuildingState* state_ptr = async_blob_map_[uuid].get(); 89 for (const DataElement& e : elements) {
142 if (!state_ptr->request_builder.requests().empty()) { 90 if (e.type() == DataElement::TYPE_BLOB && e.blob_uuid() == uuid) {
143 // Check that we're not a duplicate call. 91 completion_callback.Run(BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS);
144 return BlobTransportResult::BAD_IPC; 92 return;
145 } 93 }
94 }
95
96 std::unique_ptr<BlobBuildingState> state(new BlobBuildingState(uuid));
97 state->data_builder.set_content_type(content_type);
98 state->data_builder.set_content_disposition(content_disposition);
99 state->request_memory_callback = request_memory;
100
101 std::unique_ptr<BlobDataHandle> temp_handle;
michaeln 2016/10/28 19:38:29 looks like you could use a scoped object that will
dmurph 2016/10/28 22:13:56 But then there's a possible race (is destruction o
michaeln 2016/11/07 21:47:04 The order is guaranteed so it's not racy, given...
dmurph 2016/11/08 21:19:58 Done.
102
103 switch (memory_strategy) {
104 case MemoryStrategy::TOO_LARGE:
105 temp_handle =
106 context->AddBrokenBlob(uuid, content_type, content_disposition,
107 BlobStatus::ERR_OUT_OF_MEMORY);
108 context->IncrementBlobRefCount(uuid);
109 completion_callback.Run(BlobStatus::ERR_OUT_OF_MEMORY);
110 return;
111 case MemoryStrategy::NONE_NEEDED: {
112 for (const DataElement& e : elements) {
113 DCHECK_NE(e.type(), DataElement::TYPE_BYTES_DESCRIPTION);
114 state->data_builder.AppendIPCDataElement(e);
115 }
116 temp_handle =
117 context->BuildBlob(state->data_builder,
118 BlobStorageContext::PopulatationAllowedCallback());
119 context->IncrementBlobRefCount(uuid);
120 completion_callback.Run(BlobStatus::DONE);
121 return;
122 }
123 case MemoryStrategy::IPC:
124 state->strategy = IPCBlobItemRequestStrategy::IPC;
125 state->request_builder.InitializeForIPCRequests(
126 memory_controller.limits().max_ipc_memory_size, transport_memory_size,
127 elements, &(state->data_builder));
128 break;
129 case MemoryStrategy::SHARED_MEMORY:
130 state->strategy = IPCBlobItemRequestStrategy::SHARED_MEMORY;
131 state->request_builder.InitializeForSharedMemoryRequests(
132 memory_controller.limits().max_shared_memory_size,
133 transport_memory_size, elements, &(state->data_builder));
134 break;
135 case MemoryStrategy::FILE:
136 state->strategy = IPCBlobItemRequestStrategy::FILE;
137 state->request_builder.InitializeForFileRequests(
138 memory_controller.limits().max_file_size, transport_memory_size,
139 elements, &(state->data_builder));
140 break;
141 }
142 // We initialize our requests received state now that they are populated.
143 state->request_received.resize(state->request_builder.requests().size(),
144 false);
145
146 BlobBuildingState* state_ptr = state.get();
147 async_blob_map_[uuid] = std::move(state);
148
146 state_ptr->request_memory_callback = request_memory; 149 state_ptr->request_memory_callback = request_memory;
147 150 state_ptr->completion_callback = completion_callback;
michaeln 2016/10/28 19:38:29 is there any reasons to not init these fields earl
dmurph 2016/10/28 22:13:56 Done.
148 // Step 3: Check to make sure the referenced blob information we received 151
149 // earlier is correct: 152 temp_handle = context->BuildBlob(
150 std::set<std::string> extracted_blob_uuids; 153 state_ptr->data_builder,
151 for (const DataElement& e : elements) { 154 base::Bind(&BlobAsyncBuilderHost::OnCanStartBuildingBlob,
152 if (e.type() == DataElement::TYPE_BLOB) { 155 ptr_factory_.GetWeakPtr(), uuid, context->AsWeakPtr()));
153 extracted_blob_uuids.insert(e.blob_uuid()); 156 context->IncrementBlobRefCount(uuid);
154 // We can't depend on ourselves. 157
155 if (e.blob_uuid() == uuid) { 158 BlobStatus status = temp_handle->GetBlobStatus();
156 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); 159 if (BlobStatusIsError(status))
157 return BlobTransportResult::BAD_IPC; 160 async_blob_map_.erase(uuid);
158 } 161 if (!BlobStatusIsPending(status))
159 } 162 completion_callback.Run(temp_handle->GetBlobStatus());
160 } 163 return;
161 if (extracted_blob_uuids != state_ptr->referenced_blob_uuids) { 164 }
162 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); 165
163 return BlobTransportResult::BAD_IPC; 166 BlobStatus BlobAsyncBuilderHost::OnMemoryResponses(
164 }
165
166 // Step 4: Decide if we're using the shortcut method. This will also catch
167 // the case where we don't have any memory items.
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, 167 const std::string& uuid,
198 const std::vector<BlobItemBytesResponse>& responses, 168 const std::vector<BlobItemBytesResponse>& responses,
199 BlobStorageContext* context) { 169 BlobStorageContext* context) {
200 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); 170 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
201 if (state_it == async_blob_map_.end()) { 171 if (state_it == async_blob_map_.end()) {
202 DVLOG(1) << "Could not find blob " << uuid; 172 DVLOG(1) << "Could not find blob " << uuid;
203 return BlobTransportResult::BAD_IPC; 173 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
204 } 174 }
205 if (responses.empty()) { 175 if (responses.empty()) {
206 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); 176 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
207 return BlobTransportResult::BAD_IPC; 177 context);
208 } 178 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
179 }
180
181 // Validate response sanity: it should refer to a legal request number, and
182 // we shouldn't have received an answer for that request yet.
209 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); 183 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get();
210 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; 184 const auto& requests = state->request_builder.requests();
211 const auto& requests = request_builder.requests();
212 for (const BlobItemBytesResponse& response : responses) { 185 for (const BlobItemBytesResponse& response : responses) {
213 if (response.request_number >= requests.size()) { 186 if (response.request_number >= requests.size()) {
214 // Bad IPC, so we delete our record and ignore. 187 // Bad IPC, so we delete our record and ignore.
215 DVLOG(1) << "Invalid request number " << response.request_number; 188 DVLOG(1) << "Invalid request number " << response.request_number;
216 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); 189 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
217 return BlobTransportResult::BAD_IPC; 190 context);
191 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
218 } 192 }
219 DCHECK_LT(response.request_number, state->request_received.size()); 193 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]) { 194 if (state->request_received[response.request_number]) {
222 // Bad IPC, so we delete our record. 195 // Bad IPC, so we delete our record.
223 DVLOG(1) << "Already received response for that request."; 196 DVLOG(1) << "Already received response for that request.";
224 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); 197 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
225 return BlobTransportResult::BAD_IPC; 198 context);
199 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
226 } 200 }
227 state->request_received[response.request_number] = true; 201 state->request_received[response.request_number] = true;
228 bool invalid_ipc = false; 202 }
229 bool memory_error = false; 203 switch (state->strategy) {
230 switch (request.message.transport_strategy) { 204 case IPCBlobItemRequestStrategy::IPC:
231 case IPCBlobItemRequestStrategy::IPC: 205 return OnIPCResponses(uuid, state, responses, context);
232 if (response.inline_data.size() < request.message.size) { 206 case IPCBlobItemRequestStrategy::SHARED_MEMORY:
233 DVLOG(1) << "Invalid data size " << response.inline_data.size() 207 return OnSharedMemoryResponses(uuid, state, responses, context);
234 << " vs requested size of " << request.message.size; 208 case IPCBlobItemRequestStrategy::FILE:
235 invalid_ipc = true; 209 return OnFileResponses(uuid, state, responses, context);
236 break; 210 case IPCBlobItemRequestStrategy::UNKNOWN:
237 } 211 break;
238 invalid_ipc = !state->data_builder.PopulateFutureData( 212 }
239 request.browser_item_index, &response.inline_data[0], 213 NOTREACHED();
240 request.browser_item_offset, request.message.size); 214 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 } 215 }
290 216
291 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, 217 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid,
292 IPCBlobCreationCancelCode code, 218 BlobStatus code,
293 BlobStorageContext* context) { 219 BlobStorageContext* context) {
294 DCHECK(context); 220 DCHECK(context);
221 DCHECK(BlobStatusIsError(code));
295 auto state_it = async_blob_map_.find(uuid); 222 auto state_it = async_blob_map_.find(uuid);
296 if (state_it == async_blob_map_.end()) { 223 if (state_it == async_blob_map_.end())
297 return; 224 return;
298 }
299 // We can have the blob dereferenced by the renderer, but have it still being 225 // 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 226 // '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 227 // it in our map. Hence we make sure the context has the entry before
302 // calling cancel. 228 // calling cancel.
229 async_blob_map_.erase(state_it);
303 if (context->registry().HasEntry(uuid)) 230 if (context->registry().HasEntry(uuid))
304 context->CancelPendingBlob(uuid, code); 231 context->BreakAndFinishPendingBlob(uuid, code);
305 async_blob_map_.erase(state_it);
306 } 232 }
307 233
308 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { 234 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) {
309 DCHECK(context); 235 DCHECK(context);
310 // If the blob still exists in the context (and is being built), then we know 236 // 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 237 // expecting our blob, and we need to cancel it to let the dependency know
312 // the dependency know it's gone. 238 // it's gone.
313 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs; 239 std::vector<std::unique_ptr<BlobDataHandle>> referenced_pending_blobs;
314 for (const auto& uuid_state_pair : async_blob_map_) { 240 for (const auto& uuid_state_pair : async_blob_map_) {
315 if (context->IsBeingBuilt(uuid_state_pair.first)) { 241 std::unique_ptr<BlobDataHandle> handle =
316 referenced_pending_blobs.emplace_back( 242 context->GetBlobDataFromUUID(uuid_state_pair.first);
243 if (handle) {
244 referenced_pending_blobs.push_back(
317 context->GetBlobDataFromUUID(uuid_state_pair.first)); 245 context->GetBlobDataFromUUID(uuid_state_pair.first));
318 } 246 }
319 } 247 }
320 // We clear the map before canceling them to prevent any strange reentry into 248 // 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 249 // our class (see OnCanStartBuildingBlob) if any blobs were waiting for others
322 // to construct. 250 // to construct.
323 async_blob_map_.clear(); 251 async_blob_map_.clear();
324 for (const std::unique_ptr<BlobDataHandle>& handle : 252 for (const std::unique_ptr<BlobDataHandle>& handle :
325 referenced_pending_blobs) { 253 referenced_pending_blobs) {
326 context->CancelPendingBlob( 254 context->BreakAndFinishPendingBlob(handle->uuid(),
327 handle->uuid(), IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); 255 BlobStatus::ERR_SOURCE_DIED_IN_TRANSIT);
328 } 256 }
329 } 257 }
330 258
331 BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( 259 BlobStatus BlobAsyncBuilderHost::StartRequests(
332 const std::string& uuid, 260 const std::string& uuid,
261 BlobBuildingState* state,
262 BlobStorageContext* context,
263 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
264 switch (state->strategy) {
265 case IPCBlobItemRequestStrategy::IPC:
266 DCHECK(file_infos.empty());
267 SendIPCRequests(state, context);
268 return BlobStatus::PENDING_TRANSPORT;
269 case IPCBlobItemRequestStrategy::SHARED_MEMORY:
270 DCHECK(file_infos.empty());
271 return ContinueSharedMemoryRequests(uuid, state, context);
272 case IPCBlobItemRequestStrategy::FILE:
273 DCHECK(!file_infos.empty());
274 SendFileRequests(state, context, std::move(file_infos));
275 return BlobStatus::PENDING_TRANSPORT;
276 case IPCBlobItemRequestStrategy::UNKNOWN:
277 break;
278 }
279 NOTREACHED();
280 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
281 }
282
283 // Note: This can be called when we cancel a blob in the context.
284 void BlobAsyncBuilderHost::OnCanStartBuildingBlob(
michaeln 2016/10/28 19:38:29 but... we're already building" the blob so how can
dmurph 2016/10/28 22:13:56 Done.
285 const std::string& uuid,
286 base::WeakPtr<BlobStorageContext> context,
287 BlobStatus status,
288 std::vector<BlobMemoryController::FileCreationInfo> file_infos) {
289 if (!context) {
290 async_blob_map_.erase(uuid);
291 return;
292 }
293 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid);
294 if (state_it == async_blob_map_.end())
295 return;
296
297 BlobBuildingState* state = state_it->second.get();
298 if (BlobStatusIsPending(status)) {
299 DCHECK(status == BlobStatus::PENDING_TRANSPORT);
300 status = StartRequests(uuid, state, context.get(), std::move(file_infos));
301 if (BlobStatusIsPending(status))
302 return;
303 }
304 BlobStatusCallback completion_callback = state->completion_callback;
305 async_blob_map_.erase(state_it);
306 completion_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_TRANSPORT;
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_TRANSPORT;
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::MakeUnique<std::vector<base::File>>()); 405 std::vector<base::File>());
408 return BlobTransportResult::PENDING_RESPONSES; 406 return BlobStatus::PENDING_TRANSPORT;
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 std::vector<base::File> files;
463
464 for (BlobMemoryController::FileCreationInfo& file_info : file_infos) {
465 state->files.push_back(std::move(file_info.file_reference));
466 files.push_back(std::move(file_info.file));
467 }
468
469 const std::vector<MemoryItemRequest>& requests =
470 state->request_builder.requests();
471 std::vector<BlobItemBytesRequest> byte_requests;
472
473 DCHECK(!requests.empty());
474 for (const MemoryItemRequest& request : requests) {
475 byte_requests.push_back(request.message);
476 }
477
478 state->request_memory_callback.Run(std::move(byte_requests),
479 std::vector<base::SharedMemoryHandle>(),
480 std::move(files));
481 }
482
483 BlobStatus BlobAsyncBuilderHost::OnFileResponses(
484 const std::string& uuid,
485 BlobBuildingState* state,
486 const std::vector<BlobItemBytesResponse>& responses,
487 BlobStorageContext* context) {
488 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder;
489 const auto& requests = request_builder.requests();
490 for (const BlobItemBytesResponse& response : responses) {
491 const MemoryItemRequest& request = requests[response.request_number];
492 const scoped_refptr<ShareableFileReference>& file_ref =
493 state->files[request.message.handle_index];
494 bool success = state->data_builder.PopulateFutureFile(
495 request.browser_item_index, file_ref, response.time_file_modified);
496 if (!success) {
497 CancelBuildingBlob(uuid, BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS,
498 context);
499 return BlobStatus::ERR_INVALID_CONSTRUCTION_ARGUMENTS;
500 }
501 state->num_fulfilled_requests++;
502 }
503 if (state->num_fulfilled_requests == requests.size()) {
504 FinishBuildingBlob(state, context);
505 return BlobStatus::DONE;
506 }
507 return BlobStatus::PENDING_TRANSPORT;
435 } 508 }
436 509
437 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, 510 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state,
438 BlobStorageContext* context) { 511 BlobStorageContext* context) {
439 if (!state->referenced_blob_uuids.empty()) { 512 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()); 513 async_blob_map_.erase(state->data_builder.uuid());
514 context->FinishedPopulatingPendingBlob(uuid);
463 } 515 }
464 516
465 } // namespace storage 517 } // namespace storage
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698