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