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 <utility> | 10 #include <utility> |
11 | 11 |
| 12 #include "base/bind.h" |
12 #include "base/memory/shared_memory.h" | 13 #include "base/memory/shared_memory.h" |
| 14 #include "storage/browser/blob/blob_data_handle.h" |
| 15 #include "storage/browser/blob/blob_storage_context.h" |
13 | 16 |
14 namespace storage { | 17 namespace storage { |
15 | 18 namespace { |
16 using MemoryItemRequest = BlobAsyncTransportStrategy::RendererMemoryItemRequest; | 19 bool CalculateBlobMemorySize(const std::vector<DataElement>& elements, |
17 | 20 size_t* shortcut_bytes, |
18 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState() | 21 uint64_t* total_bytes) { |
19 : next_request(0), | 22 DCHECK(shortcut_bytes); |
20 num_fulfilled_requests(0), | 23 DCHECK(total_bytes); |
21 num_shared_memory_requests(0), | 24 base::CheckedNumeric<uint64_t> total_size_checked = 0; |
22 current_shared_memory_handle_index(0) {} | 25 base::CheckedNumeric<size_t> shortcut_size_checked = 0; |
| 26 for (const auto& e : elements) { |
| 27 if (e.type() == DataElement::TYPE_BYTES) { |
| 28 total_size_checked += e.length(); |
| 29 shortcut_size_checked += e.length(); |
| 30 } else if (e.type() == DataElement::TYPE_BYTES_DESCRIPTION) { |
| 31 total_size_checked += e.length(); |
| 32 } else { |
| 33 continue; |
| 34 } |
| 35 if (!total_size_checked.IsValid() || !shortcut_size_checked.IsValid()) { |
| 36 return false; |
| 37 } |
| 38 } |
| 39 *shortcut_bytes = shortcut_size_checked.ValueOrDie(); |
| 40 *total_bytes = total_size_checked.ValueOrDie(); |
| 41 return true; |
| 42 } |
| 43 } // namespace |
| 44 |
| 45 using MemoryItemRequest = |
| 46 BlobAsyncTransportRequestBuilder::RendererMemoryItemRequest; |
| 47 |
| 48 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState( |
| 49 const std::string& uuid, |
| 50 std::set<std::string> referenced_blob_uuids, |
| 51 std::vector<scoped_ptr<BlobDataHandle>>* referenced_blob_handles) |
| 52 : data_builder(uuid), |
| 53 referenced_blob_uuids(referenced_blob_uuids), |
| 54 referenced_blob_handles(std::move(*referenced_blob_handles)) {} |
23 | 55 |
24 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} | 56 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} |
25 | 57 |
26 BlobAsyncBuilderHost::BlobAsyncBuilderHost() {} | 58 BlobAsyncBuilderHost::BlobAsyncBuilderHost() : ptr_factory_(this) {} |
27 | 59 |
28 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} | 60 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} |
29 | 61 |
30 bool BlobAsyncBuilderHost::StartBuildingBlob( | 62 BlobTransportResult BlobAsyncBuilderHost::RegisterBlobUUID( |
31 const std::string& uuid, | 63 const std::string& uuid, |
32 const std::string& type, | 64 const std::string& content_type, |
33 const std::vector<DataElement>& descriptions, | 65 const std::string& content_disposition, |
| 66 const std::set<std::string>& referenced_blob_uuids, |
| 67 BlobStorageContext* context) { |
| 68 if (async_blob_map_.find(uuid) != async_blob_map_.end()) |
| 69 return BlobTransportResult::BAD_IPC; |
| 70 if (referenced_blob_uuids.find(uuid) != referenced_blob_uuids.end()) |
| 71 return BlobTransportResult::BAD_IPC; |
| 72 context->CreatePendingBlob(uuid, content_type, content_disposition); |
| 73 std::vector<scoped_ptr<BlobDataHandle>> handles; |
| 74 for (const std::string& referenced_uuid : referenced_blob_uuids) { |
| 75 scoped_ptr<BlobDataHandle> handle = |
| 76 context->GetBlobDataFromUUID(referenced_uuid); |
| 77 if (!handle || handle->IsBroken()) { |
| 78 // We cancel the blob right away, and don't bother storing our state. |
| 79 context->CancelPendingBlob( |
| 80 uuid, IPCBlobCreationCancelCode::REFERENCED_BLOB_BROKEN); |
| 81 return BlobTransportResult::CANCEL_REFERENCED_BLOB_BROKEN; |
| 82 } |
| 83 handles.emplace_back(std::move(handle)); |
| 84 } |
| 85 async_blob_map_[uuid] = make_scoped_ptr( |
| 86 new BlobBuildingState(uuid, referenced_blob_uuids, &handles)); |
| 87 return BlobTransportResult::DONE; |
| 88 } |
| 89 |
| 90 BlobTransportResult BlobAsyncBuilderHost::StartBuildingBlob( |
| 91 const std::string& uuid, |
| 92 const std::vector<DataElement>& elements, |
34 size_t memory_available, | 93 size_t memory_available, |
35 const base::Callback<void(const std::vector<storage::BlobItemBytesRequest>&, | 94 BlobStorageContext* context, |
36 const std::vector<base::SharedMemoryHandle>&, | 95 const RequestMemoryCallback& request_memory) { |
37 const std::vector<uint64_t>&)>& request_memory, | 96 DCHECK(context); |
38 const base::Callback<void(const BlobDataBuilder&)>& done, | 97 if (async_blob_map_.find(uuid) == async_blob_map_.end()) { |
39 const base::Callback<void(IPCBlobCreationCancelCode)>& cancel) { | 98 return BlobTransportResult::BAD_IPC; |
40 if (async_blob_map_.find(uuid) != async_blob_map_.end()) | 99 } |
41 return false; | 100 |
42 if (BlobAsyncTransportStrategy::ShouldBeShortcut(descriptions, | 101 // Step 1: Get the sizes. |
43 memory_available)) { | 102 size_t shortcut_memory_size_bytes = 0; |
44 // We have enough memory, and all the data is here, so we use the shortcut | 103 uint64_t total_memory_size_bytes = 0; |
45 // method and populate the old way. | 104 if (!CalculateBlobMemorySize(elements, &shortcut_memory_size_bytes, |
46 BlobDataBuilder builder(uuid); | 105 &total_memory_size_bytes)) { |
47 builder.set_content_type(type); | 106 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
48 for (const DataElement& element : descriptions) { | 107 return BlobTransportResult::BAD_IPC; |
49 builder.AppendIPCDataElement(element); | 108 } |
50 } | 109 |
51 done.Run(builder); | 110 // Step 2: Check if we have enough memory to store the blob. |
52 return true; | 111 if (total_memory_size_bytes > memory_available) { |
53 } | 112 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, context); |
54 | 113 return BlobTransportResult::CANCEL_MEMORY_FULL; |
55 scoped_ptr<BlobBuildingState> state(new BlobBuildingState()); | 114 } |
56 BlobBuildingState* state_ptr = state.get(); | 115 |
57 async_blob_map_[uuid] = std::move(state); | 116 // From here on, we know we can fit the blob in memory. |
58 state_ptr->type = type; | 117 BlobBuildingState* state_ptr = async_blob_map_[uuid].get(); |
| 118 if (!state_ptr->request_builder.requests().empty()) { |
| 119 // Check that we're not a duplicate call. |
| 120 return BlobTransportResult::BAD_IPC; |
| 121 } |
59 state_ptr->request_memory_callback = request_memory; | 122 state_ptr->request_memory_callback = request_memory; |
60 state_ptr->done_callback = done; | 123 |
61 state_ptr->cancel_callback = cancel; | 124 // Step 3: Check to make sure the referenced blob information we received |
62 | 125 // earlier is correct: |
63 // We are currently only operating in 'no disk' mode. This will change in | 126 std::set<std::string> extracted_blob_uuids; |
64 // future patches to enable disk storage. | 127 for (const DataElement& e : elements) { |
65 // Since we don't have a disk yet, we put 0 for disk_space_left. | 128 if (e.type() == DataElement::TYPE_BLOB) { |
66 state_ptr->transport_strategy.Initialize( | 129 extracted_blob_uuids.insert(e.blob_uuid()); |
67 max_ipc_memory_size_, max_shared_memory_size_, max_file_size_, | 130 // We can't depend on ourselves. |
68 0 /* disk_space_left */, memory_available, uuid, descriptions); | 131 if (e.blob_uuid() == uuid) { |
69 | 132 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
70 switch (state_ptr->transport_strategy.error()) { | 133 return BlobTransportResult::BAD_IPC; |
71 case BlobAsyncTransportStrategy::ERROR_TOO_LARGE: | 134 } |
72 // Cancel cleanly, we're out of memory. | 135 } |
73 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | 136 } |
74 return true; | 137 if (extracted_blob_uuids != state_ptr->referenced_blob_uuids) { |
75 case BlobAsyncTransportStrategy::ERROR_INVALID_PARAMS: | 138 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
76 // Bad IPC, so we ignore and clean up. | 139 return BlobTransportResult::BAD_IPC; |
77 VLOG(1) << "Error initializing transport strategy: " | 140 } |
78 << state_ptr->transport_strategy.error(); | 141 |
79 async_blob_map_.erase(async_blob_map_.find(uuid)); | 142 // Step 4: Decide if we're using the shortcut method. This will also catch |
80 return false; | 143 // the case where we don't have any memory items. |
81 case BlobAsyncTransportStrategy::ERROR_NONE: | 144 if (shortcut_memory_size_bytes == total_memory_size_bytes && |
82 ContinueBlobMemoryRequests(uuid); | 145 shortcut_memory_size_bytes <= memory_available) { |
83 return true; | 146 for (const DataElement& e : elements) { |
84 } | 147 state_ptr->data_builder.AppendIPCDataElement(e); |
85 return false; | 148 } |
| 149 FinishBuildingBlob(state_ptr, context); |
| 150 return BlobTransportResult::DONE; |
| 151 } |
| 152 |
| 153 // From here on, we know the blob's size is less than |memory_available|, |
| 154 // so we know we're < max(size_t). |
| 155 // Step 5: Decide if we're using shared memory. |
| 156 if (total_memory_size_bytes > max_ipc_memory_size_) { |
| 157 state_ptr->request_builder.InitializeForSharedMemoryRequests( |
| 158 max_shared_memory_size_, total_memory_size_bytes, elements, |
| 159 &(state_ptr->data_builder)); |
| 160 } else { |
| 161 // Step 6: We can fit in IPC. |
| 162 state_ptr->request_builder.InitializeForIPCRequests( |
| 163 max_ipc_memory_size_, total_memory_size_bytes, elements, |
| 164 &(state_ptr->data_builder)); |
| 165 } |
| 166 // We initialize our requests received state now that they are populated. |
| 167 state_ptr->request_received.resize( |
| 168 state_ptr->request_builder.requests().size(), false); |
| 169 return ContinueBlobMemoryRequests(uuid, context); |
86 } | 170 } |
87 | 171 |
88 bool BlobAsyncBuilderHost::OnMemoryResponses( | 172 BlobTransportResult BlobAsyncBuilderHost::OnMemoryResponses( |
89 const std::string& uuid, | 173 const std::string& uuid, |
90 const std::vector<BlobItemBytesResponse>& responses) { | 174 const std::vector<BlobItemBytesResponse>& responses, |
91 if (responses.empty()) { | 175 BlobStorageContext* context) { |
92 return false; | |
93 } | |
94 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | 176 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
95 if (state_it == async_blob_map_.end()) { | 177 if (state_it == async_blob_map_.end()) { |
96 // There's a possibility that we had a shared memory error, and there were | |
97 // still responses in flight. So we don't fail here, we just ignore. | |
98 DVLOG(1) << "Could not find blob " << uuid; | 178 DVLOG(1) << "Could not find blob " << uuid; |
99 return true; | 179 return BlobTransportResult::BAD_IPC; |
| 180 } |
| 181 if (responses.empty()) { |
| 182 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
| 183 return BlobTransportResult::BAD_IPC; |
100 } | 184 } |
101 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | 185 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
102 BlobAsyncTransportStrategy& strategy = state->transport_strategy; | 186 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
103 bool invalid_ipc = false; | 187 const auto& requests = request_builder.requests(); |
104 bool memory_error = false; | |
105 const auto& requests = strategy.requests(); | |
106 for (const BlobItemBytesResponse& response : responses) { | 188 for (const BlobItemBytesResponse& response : responses) { |
107 if (response.request_number >= requests.size()) { | 189 if (response.request_number >= requests.size()) { |
108 // Bad IPC, so we delete our record and ignore. | 190 // Bad IPC, so we delete our record and ignore. |
109 DVLOG(1) << "Invalid request number " << response.request_number; | 191 DVLOG(1) << "Invalid request number " << response.request_number; |
110 async_blob_map_.erase(state_it); | 192 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
111 return false; | 193 return BlobTransportResult::BAD_IPC; |
112 } | 194 } |
| 195 DCHECK_LT(response.request_number, state->request_received.size()); |
113 const MemoryItemRequest& request = requests[response.request_number]; | 196 const MemoryItemRequest& request = requests[response.request_number]; |
114 if (request.received) { | 197 if (state->request_received[response.request_number]) { |
115 // Bad IPC, so we delete our record. | 198 // Bad IPC, so we delete our record. |
116 DVLOG(1) << "Already received response for that request."; | 199 DVLOG(1) << "Already received response for that request."; |
117 async_blob_map_.erase(state_it); | 200 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
118 return false; | 201 return BlobTransportResult::BAD_IPC; |
119 } | 202 } |
120 strategy.MarkRequestAsReceived(response.request_number); | 203 state->request_received[response.request_number] = true; |
| 204 bool invalid_ipc = false; |
| 205 bool memory_error = false; |
121 switch (request.message.transport_strategy) { | 206 switch (request.message.transport_strategy) { |
122 case IPCBlobItemRequestStrategy::IPC: | 207 case IPCBlobItemRequestStrategy::IPC: |
123 if (response.inline_data.size() < request.message.size) { | 208 if (response.inline_data.size() < request.message.size) { |
124 DVLOG(1) << "Invalid data size " << response.inline_data.size() | 209 DVLOG(1) << "Invalid data size " << response.inline_data.size() |
125 << " vs requested size of " << request.message.size; | 210 << " vs requested size of " << request.message.size; |
126 invalid_ipc = true; | 211 invalid_ipc = true; |
127 break; | 212 break; |
128 } | 213 } |
129 invalid_ipc = !strategy.blob_builder()->PopulateFutureData( | 214 invalid_ipc = !state->data_builder.PopulateFutureData( |
130 request.browser_item_index, &response.inline_data[0], | 215 request.browser_item_index, &response.inline_data[0], |
131 request.browser_item_offset, request.message.size); | 216 request.browser_item_offset, request.message.size); |
132 break; | 217 break; |
133 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | 218 case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
134 if (state->num_shared_memory_requests == 0) { | 219 if (state->num_shared_memory_requests == 0) { |
135 DVLOG(1) << "Received too many responses for shared memory."; | 220 DVLOG(1) << "Received too many responses for shared memory."; |
136 invalid_ipc = true; | 221 invalid_ipc = true; |
137 break; | 222 break; |
138 } | 223 } |
139 state->num_shared_memory_requests--; | 224 state->num_shared_memory_requests--; |
140 if (!state->shared_memory_block->memory()) { | 225 if (!state->shared_memory_block->memory()) { |
141 // We just map the whole block, as we'll probably be accessing the | 226 // We just map the whole block, as we'll probably be accessing the |
142 // whole thing in this group of responses. Another option is to use | 227 // whole thing in this group of responses. Another option is to use |
143 // MapAt, remove the mapped boolean, and then exclude the | 228 // MapAt, remove the mapped boolean, and then exclude the |
144 // handle_offset below. | 229 // handle_offset below. |
145 size_t handle_size = strategy.handle_sizes().at( | 230 size_t handle_size = request_builder.shared_memory_sizes() |
146 state->current_shared_memory_handle_index); | 231 [state->current_shared_memory_handle_index]; |
147 if (!state->shared_memory_block->Map(handle_size)) { | 232 if (!state->shared_memory_block->Map(handle_size)) { |
148 DVLOG(1) << "Unable to map memory to size " << handle_size; | 233 DVLOG(1) << "Unable to map memory to size " << handle_size; |
149 memory_error = true; | 234 memory_error = true; |
150 break; | 235 break; |
151 } | 236 } |
152 } | 237 } |
153 | 238 |
154 invalid_ipc = !strategy.blob_builder()->PopulateFutureData( | 239 invalid_ipc = !state->data_builder.PopulateFutureData( |
155 request.browser_item_index, | 240 request.browser_item_index, |
156 static_cast<const char*>(state->shared_memory_block->memory()) + | 241 static_cast<const char*>(state->shared_memory_block->memory()) + |
157 request.message.handle_offset, | 242 request.message.handle_offset, |
158 request.browser_item_offset, request.message.size); | 243 request.browser_item_offset, request.message.size); |
159 break; | 244 break; |
160 case IPCBlobItemRequestStrategy::FILE: | 245 case IPCBlobItemRequestStrategy::FILE: |
161 case IPCBlobItemRequestStrategy::UNKNOWN: | 246 case IPCBlobItemRequestStrategy::UNKNOWN: |
162 DVLOG(1) << "Not implemented."; | 247 DVLOG(1) << "Not implemented."; |
163 invalid_ipc = true; | 248 invalid_ipc = true; |
164 break; | 249 break; |
165 } | 250 } |
166 if (invalid_ipc) { | 251 if (invalid_ipc) { |
167 // Bad IPC, so we delete our record and return false. | 252 // Bad IPC, so we delete our record and return false. |
168 async_blob_map_.erase(state_it); | 253 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::UNKNOWN, context); |
169 return false; | 254 return BlobTransportResult::BAD_IPC; |
170 } | 255 } |
171 if (memory_error) { | 256 if (memory_error) { |
172 DVLOG(1) << "Shared memory error."; | 257 DVLOG(1) << "Shared memory error."; |
173 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | 258 CancelBuildingBlob(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY, |
174 return true; | 259 context); |
| 260 return BlobTransportResult::CANCEL_MEMORY_FULL; |
175 } | 261 } |
176 state->num_fulfilled_requests++; | 262 state->num_fulfilled_requests++; |
177 } | 263 } |
178 ContinueBlobMemoryRequests(uuid); | 264 return ContinueBlobMemoryRequests(uuid, context); |
179 return true; | |
180 } | 265 } |
181 | 266 |
182 void BlobAsyncBuilderHost::StopBuildingBlob(const std::string& uuid) { | 267 void BlobAsyncBuilderHost::CancelBuildingBlob(const std::string& uuid, |
183 async_blob_map_.erase(async_blob_map_.find(uuid)); | 268 IPCBlobCreationCancelCode code, |
| 269 BlobStorageContext* context) { |
| 270 DCHECK(context); |
| 271 auto state_it = async_blob_map_.find(uuid); |
| 272 if (state_it == async_blob_map_.end()) { |
| 273 return; |
| 274 } |
| 275 // We can have the blob dereferenced by the renderer, but have it still being |
| 276 // 'built'. In this case, it's destructed in the context, but we still have |
| 277 // it in our map. Hence we make sure the context has the entry before |
| 278 // calling cancel. |
| 279 if (context->registry().HasEntry(uuid)) |
| 280 context->CancelPendingBlob(uuid, code); |
| 281 async_blob_map_.erase(state_it); |
184 } | 282 } |
185 | 283 |
186 void BlobAsyncBuilderHost::ContinueBlobMemoryRequests(const std::string& uuid) { | 284 void BlobAsyncBuilderHost::CancelAll(BlobStorageContext* context) { |
| 285 DCHECK(context); |
| 286 // Some of our pending blobs may still be referenced elsewhere. |
| 287 std::vector<scoped_ptr<BlobDataHandle>> referenced_pending_blobs; |
| 288 for (const auto& uuid_state_pair : async_blob_map_) { |
| 289 if (context->IsBeingBuilt(uuid_state_pair.first)) { |
| 290 referenced_pending_blobs.emplace_back( |
| 291 context->GetBlobDataFromUUID(uuid_state_pair.first)); |
| 292 } |
| 293 } |
| 294 // We clear the map before canceling them to prevent any strange reentry into |
| 295 // our class (see ReferencedBlobFinished) if any blobs were waiting for others |
| 296 // to construct. |
| 297 async_blob_map_.clear(); |
| 298 for (const scoped_ptr<BlobDataHandle>& handle : referenced_pending_blobs) { |
| 299 context->CancelPendingBlob( |
| 300 handle->uuid(), IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT); |
| 301 } |
| 302 } |
| 303 |
| 304 BlobTransportResult BlobAsyncBuilderHost::ContinueBlobMemoryRequests( |
| 305 const std::string& uuid, |
| 306 BlobStorageContext* context) { |
187 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | 307 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); |
188 DCHECK(state_it != async_blob_map_.end()); | 308 DCHECK(state_it != async_blob_map_.end()); |
189 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | 309 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); |
190 | 310 |
191 const std::vector<MemoryItemRequest>& requests = | 311 BlobAsyncTransportRequestBuilder& request_builder = state->request_builder; |
192 state->transport_strategy.requests(); | 312 const std::vector<MemoryItemRequest>& requests = request_builder.requests(); |
193 BlobAsyncTransportStrategy& strategy = state->transport_strategy; | |
194 size_t num_requests = requests.size(); | 313 size_t num_requests = requests.size(); |
195 if (state->num_fulfilled_requests == num_requests) { | 314 if (state->num_fulfilled_requests == num_requests) { |
196 DoneAndCleanup(uuid); | 315 FinishBuildingBlob(state, context); |
197 return; | 316 return BlobTransportResult::DONE; |
198 } | 317 } |
199 DCHECK_LT(state->num_fulfilled_requests, num_requests); | 318 DCHECK_LT(state->num_fulfilled_requests, num_requests); |
200 if (state->next_request == num_requests) { | 319 if (state->next_request == num_requests) { |
201 // We are still waiting on other requests to come back. | 320 // We are still waiting on other requests to come back. |
202 return; | 321 return BlobTransportResult::PENDING_RESPONSES; |
203 } | 322 } |
204 | 323 |
205 std::vector<BlobItemBytesRequest> byte_requests; | 324 scoped_ptr<std::vector<BlobItemBytesRequest>> byte_requests( |
206 std::vector<base::SharedMemoryHandle> shared_memory; | 325 new std::vector<BlobItemBytesRequest>()); |
207 std::vector<uint64_t> files; | 326 scoped_ptr<std::vector<base::SharedMemoryHandle>> shared_memory( |
| 327 new std::vector<base::SharedMemoryHandle>()); |
208 | 328 |
209 for (; state->next_request < num_requests; ++state->next_request) { | 329 for (; state->next_request < num_requests; ++state->next_request) { |
210 const MemoryItemRequest& request = requests[state->next_request]; | 330 const MemoryItemRequest& request = requests[state->next_request]; |
211 | 331 |
212 bool stop_accumulating = false; | 332 bool stop_accumulating = false; |
213 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; | 333 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; |
214 switch (request.message.transport_strategy) { | 334 switch (request.message.transport_strategy) { |
215 case IPCBlobItemRequestStrategy::IPC: | 335 case IPCBlobItemRequestStrategy::IPC: |
216 byte_requests.push_back(request.message); | 336 byte_requests->push_back(request.message); |
217 break; | 337 break; |
218 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | 338 case IPCBlobItemRequestStrategy::SHARED_MEMORY: |
219 if (using_shared_memory_handle && | 339 if (using_shared_memory_handle && |
220 state->current_shared_memory_handle_index != | 340 state->current_shared_memory_handle_index != |
221 request.message.handle_index) { | 341 request.message.handle_index) { |
222 // We only want one shared memory per requesting blob. | 342 // We only want one shared memory per requesting blob. |
223 stop_accumulating = true; | 343 stop_accumulating = true; |
224 break; | 344 break; |
225 } | 345 } |
226 using_shared_memory_handle = true; | 346 using_shared_memory_handle = true; |
227 state->current_shared_memory_handle_index = | 347 state->current_shared_memory_handle_index = |
228 request.message.handle_index; | 348 request.message.handle_index; |
229 state->num_shared_memory_requests++; | 349 state->num_shared_memory_requests++; |
230 | 350 |
231 if (!state->shared_memory_block) { | 351 if (!state->shared_memory_block) { |
232 state->shared_memory_block.reset(new base::SharedMemory()); | 352 state->shared_memory_block.reset(new base::SharedMemory()); |
233 size_t size = strategy.handle_sizes()[request.message.handle_index]; | 353 size_t size = |
| 354 request_builder |
| 355 .shared_memory_sizes()[request.message.handle_index]; |
234 if (!state->shared_memory_block->CreateAnonymous(size)) { | 356 if (!state->shared_memory_block->CreateAnonymous(size)) { |
235 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; | 357 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; |
236 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | 358 return BlobTransportResult::CANCEL_MEMORY_FULL; |
237 return; | |
238 } | 359 } |
239 } | 360 } |
240 shared_memory.push_back(state->shared_memory_block->handle()); | 361 shared_memory->push_back(state->shared_memory_block->handle()); |
241 byte_requests.push_back(request.message); | 362 byte_requests->push_back(request.message); |
242 // Since we are only using one handle at a time, transform our handle | 363 // Since we are only using one handle at a time, transform our handle |
243 // index correctly back to 0. | 364 // index correctly back to 0. |
244 byte_requests.back().handle_index = 0; | 365 byte_requests->back().handle_index = 0; |
245 break; | 366 break; |
246 case IPCBlobItemRequestStrategy::FILE: | 367 case IPCBlobItemRequestStrategy::FILE: |
247 case IPCBlobItemRequestStrategy::UNKNOWN: | 368 case IPCBlobItemRequestStrategy::UNKNOWN: |
248 NOTREACHED() << "Not implemented yet."; | 369 NOTREACHED() << "Not implemented yet."; |
249 break; | 370 break; |
250 } | 371 } |
251 if (stop_accumulating) { | 372 if (stop_accumulating) { |
252 break; | 373 break; |
253 } | 374 } |
254 } | 375 } |
255 | |
256 DCHECK(!requests.empty()); | 376 DCHECK(!requests.empty()); |
257 | 377 |
258 state->request_memory_callback.Run(byte_requests, shared_memory, files); | 378 state->request_memory_callback.Run( |
| 379 std::move(byte_requests), std::move(shared_memory), |
| 380 make_scoped_ptr(new std::vector<base::File>())); |
| 381 return BlobTransportResult::PENDING_RESPONSES; |
259 } | 382 } |
260 | 383 |
261 void BlobAsyncBuilderHost::CancelAndCleanup(const std::string& uuid, | 384 void BlobAsyncBuilderHost::ReferencedBlobFinished( |
262 IPCBlobCreationCancelCode code) { | 385 const std::string& owning_blob_uuid, |
263 scoped_ptr<BlobBuildingState> state = std::move(async_blob_map_[uuid]); | 386 base::WeakPtr<BlobStorageContext> context, |
264 async_blob_map_.erase(uuid); | 387 bool construction_success) { |
265 state->cancel_callback.Run(code); | 388 if (!context) { |
| 389 return; |
| 390 } |
| 391 auto state_it = async_blob_map_.find(owning_blob_uuid); |
| 392 if (state_it == async_blob_map_.end()) { |
| 393 return; |
| 394 } |
| 395 if (!construction_success) { |
| 396 CancelBuildingBlob(owning_blob_uuid, |
| 397 IPCBlobCreationCancelCode::SOURCE_DIED_IN_TRANSIT, |
| 398 context.get()); |
| 399 return; |
| 400 } |
| 401 BlobBuildingState* state = state_it->second.get(); |
| 402 DCHECK_GT(state->num_referenced_blobs_building, 0u); |
| 403 if (--state->num_referenced_blobs_building == 0) { |
| 404 context->CompletePendingBlob(state->data_builder); |
| 405 async_blob_map_.erase(state->data_builder.uuid()); |
| 406 } |
266 } | 407 } |
267 | 408 |
268 void BlobAsyncBuilderHost::DoneAndCleanup(const std::string& uuid) { | 409 void BlobAsyncBuilderHost::FinishBuildingBlob(BlobBuildingState* state, |
269 scoped_ptr<BlobBuildingState> state = std::move(async_blob_map_[uuid]); | 410 BlobStorageContext* context) { |
270 async_blob_map_.erase(uuid); | 411 if (!state->referenced_blob_uuids.empty()) { |
271 BlobDataBuilder* builder = state->transport_strategy.blob_builder(); | 412 DCHECK_EQ(0u, state->num_referenced_blobs_building); |
272 builder->set_content_type(state->type); | 413 state->num_referenced_blobs_building = 0; |
273 state->done_callback.Run(*builder); | 414 // We assume re-entry is not possible, as RunOnConstructionComplete |
| 415 // will schedule a task when the blob is being built. Thus we can't have the |
| 416 // case where |num_referenced_blobs_building| reaches 0 in the |
| 417 // ReferencedBlobFinished method before we're finished looping. |
| 418 for (const std::string& referenced_uuid : state->referenced_blob_uuids) { |
| 419 if (context->IsBeingBuilt(referenced_uuid)) { |
| 420 state->num_referenced_blobs_building++; |
| 421 context->RunOnConstructionComplete( |
| 422 referenced_uuid, |
| 423 base::Bind(&BlobAsyncBuilderHost::ReferencedBlobFinished, |
| 424 ptr_factory_.GetWeakPtr(), state->data_builder.uuid(), |
| 425 context->AsWeakPtr())); |
| 426 } |
| 427 } |
| 428 if (state->num_referenced_blobs_building > 0) { |
| 429 // We wait until referenced blobs are done. |
| 430 return; |
| 431 } |
| 432 } |
| 433 context->CompletePendingBlob(state->data_builder); |
| 434 async_blob_map_.erase(state->data_builder.uuid()); |
274 } | 435 } |
275 | 436 |
276 } // namespace storage | 437 } // namespace storage |
OLD | NEW |