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 "storage/browser/blob/blob_async_builder_host.h" | |
6 | |
7 #include "base/memory/shared_memory.h" | |
8 #include "base/stl_util.h" | |
9 | |
10 namespace storage { | |
11 | |
12 using MemoryItemRequest = BlobAsyncTransportStrategy::MemoryItemRequest; | |
13 | |
14 BlobAsyncBuilderHost::BlobAsyncBuilderHost() | |
15 : max_ipc_memory_size_(kBlobStorageIPCThresholdBytes), | |
16 max_shared_memory_size_(kBlobStorageMaxSharedMemoryBytes), | |
17 max_file_size_(kBlobStorageMaxFileSizeBytes), | |
18 max_blob_in_memory_size_(kBlobStorageMaxBlobMemorySize) {} | |
19 | |
20 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() { | |
21 STLDeleteContainerPairSecondPointers(async_blob_map_.begin(), | |
22 async_blob_map_.end()); | |
23 } | |
24 | |
25 void BlobAsyncBuilderHost::BuildBlobAsync( | |
26 const std::string& uuid, | |
27 const std::string& type, | |
28 const std::vector<DataElement>& descriptions, | |
29 size_t memory_available, | |
30 base::Callback<void(const std::vector<storage::BlobItemBytesRequest>&, | |
31 const std::vector<base::SharedMemoryHandle>&, | |
32 const std::vector<IPC::PlatformFileForTransit>&)> | |
33 request_memory, | |
34 base::Callback<void(BlobDataBuilder*)> done, | |
35 base::Callback<void(IPCBlobCreationCancelCode)> cancel) { | |
36 if (BlobAsyncTransportStrategy::ShouldBeShortcut(descriptions, | |
37 memory_available)) { | |
38 // We have enough memory, and all the data is here, so we use the shortcut | |
39 // method and populate the old way. | |
40 BlobDataBuilder builder(uuid); | |
41 builder.set_content_type(type); | |
42 for (const DataElement& element : descriptions) { | |
43 builder.AppendDataElement(element); | |
44 } | |
45 done.Run(&builder); | |
kinuko
2015/11/17 14:51:41
Calling this synchronously while the method name t
dmurph
2015/11/17 20:40:19
Renamed, and clarified behavior in comment.
| |
46 return; | |
47 } | |
48 | |
49 DCHECK(async_blob_map_.find(uuid) == async_blob_map_.end()); | |
50 BlobAsyncBuilderHost::BlobBuildingState* state = | |
51 new BlobAsyncBuilderHost::BlobBuildingState(); | |
52 async_blob_map_[uuid] = state; | |
53 state->type = type; | |
54 state->request_memory = request_memory; | |
55 state->done = done; | |
56 state->cancel = cancel; | |
michaeln
2015/11/17 21:55:28
naming nit: consider appending _callback to reques
dmurph
2015/11/19 02:06:17
Done.
| |
57 | |
58 state->transport_strategy.Initialize( | |
59 BlobAsyncTransportStrategy::MODE_NO_DISK, max_ipc_memory_size_, | |
60 max_shared_memory_size_, max_file_size_, max_blob_in_memory_size_, 0, | |
61 memory_available, uuid, descriptions); | |
kinuko
2015/11/17 14:51:41
Can you add a comment for '0'? (disk_space_left?)
dmurph
2015/11/17 20:40:19
Done.
| |
62 if (state->transport_strategy.error() != | |
63 BlobAsyncTransportStrategy::ERROR_NONE) { | |
64 LOG(ERROR) << "Error initializing transport strategy: " | |
kinuko
2015/11/17 14:51:41
DVLOG ?
dmurph
2015/11/17 20:40:19
Done.
michaeln
2015/11/17 21:55:28
is the logging needed in a release build, here and
dmurph
2015/11/19 02:06:17
Can I log to debug? There's no way of telling what
| |
65 << state->transport_strategy.error(); | |
66 CancelAndCleanup(uuid, state->transport_strategy.error() == | |
67 BlobAsyncTransportStrategy::ERROR_TOO_LARGE | |
68 ? IPCBlobCreationCancelCode::OUT_OF_MEMORY | |
69 : IPCBlobCreationCancelCode::UNKNOWN); | |
70 return; | |
71 } | |
72 | |
73 ContinueBlobMemoryRequests(uuid); | |
74 } | |
75 | |
76 void BlobAsyncBuilderHost::OnMemoryResponses( | |
77 const std::string& uuid, | |
78 const std::vector<BlobItemBytesResponse> responses) { | |
79 AsyncBlobMap::iterator state_it = async_blob_map_.find(uuid); | |
80 if (state_it == async_blob_map_.end()) { | |
michaeln
2015/11/17 21:55:27
is this indicative of a bad ipc message or could t
dmurph
2015/11/19 02:06:17
Bad IPC message. I could have the check happen ear
| |
81 return; | |
82 } | |
83 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second; | |
84 BlobAsyncTransportStrategy& strategy = state->transport_strategy; | |
85 bool populate_success = false; | |
86 const auto& requests = strategy.requests(); | |
87 for (const BlobItemBytesResponse& response : responses) { | |
88 if (response.request_number >= requests.size()) { | |
89 LOG(ERROR) << "Invalid request number " << response.request_number; | |
michaeln
2015/11/17 21:55:28
we might want to treat this as a badipc?
dmurph
2015/11/19 02:06:17
See above resolution.
| |
90 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::UNKNOWN); | |
91 return; | |
92 } | |
93 const MemoryItemRequest& request = requests[response.request_number]; | |
94 if (request.received) { | |
95 LOG(ERROR) << "Already received response for that request."; | |
michaeln
2015/11/17 21:55:28
and this one too, when would this be expected?
dmurph
2015/11/19 02:06:17
Done.
| |
96 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::UNKNOWN); | |
97 return; | |
98 } | |
99 switch (request.message.transport_strategy) { | |
100 case IPCBlobItemRequestStrategy::IPC: | |
101 if (response.inline_data.size() < request.message.size) { | |
michaeln
2015/11/17 21:55:28
ditto
dmurph
2015/11/19 02:06:17
Done.
| |
102 LOG(ERROR) << "Invalid data size " << response.inline_data.size() | |
103 << " vs requested size of " << request.message.size; | |
104 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::UNKNOWN); | |
105 return; | |
106 } | |
107 populate_success = strategy.blob_builder()->PopulateFutureData( | |
108 request.browser_item_index, &response.inline_data[0], | |
109 request.browser_item_offset, request.message.size); | |
110 break; | |
111 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
112 if (state->num_shared_memory_requests == 0) { | |
michaeln
2015/11/17 21:55:27
ditto
| |
113 LOG(ERROR) << "Received too many responses for shared memory."; | |
114 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::UNKNOWN); | |
115 return; | |
116 } | |
117 state->num_shared_memory_requests--; | |
118 if (!state->shared_memory_block->memory()) { | |
119 // We just map the whole block, as we'll probably be accessing the | |
120 // whole thing in this group of responses. Another option is to use | |
121 // MapAt, remove the mapped boolean, and then exclude the | |
122 // handle_offset below. | |
123 size_t handle_size = strategy.shared_memory_handles().at( | |
124 state->current_shared_memory_handle_index); | |
125 if (!state->shared_memory_block->Map(handle_size)) { | |
126 LOG(ERROR) << "Unable to map memory to size " << handle_size; | |
127 populate_success = false; | |
128 break; | |
129 } | |
130 } | |
131 | |
132 populate_success = strategy.blob_builder()->PopulateFutureData( | |
133 request.browser_item_index, | |
134 static_cast<const char*>(state->shared_memory_block->memory()) + | |
135 request.message.handle_offset, | |
136 request.browser_item_offset, request.message.size); | |
137 break; | |
138 case IPCBlobItemRequestStrategy::FILE: | |
139 case IPCBlobItemRequestStrategy::UNKNOWN: | |
140 NOTREACHED() << "Not implemented yet."; | |
141 break; | |
142 } | |
143 if (!populate_success) { | |
144 LOG(ERROR) << "Unable to populate blob data."; | |
145 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::UNKNOWN); | |
146 return; | |
147 } | |
148 state->num_fulfilled_requests++; | |
149 } | |
150 ContinueBlobMemoryRequests(uuid); | |
151 } | |
152 | |
153 void BlobAsyncBuilderHost::StopBuildingBlob(const std::string& uuid) { | |
154 AsyncBlobMap::iterator state_it = async_blob_map_.find(uuid); | |
155 if (state_it == async_blob_map_.end()) { | |
156 return; | |
157 } | |
158 delete state_it->second; | |
159 async_blob_map_.erase(state_it); | |
160 } | |
161 | |
162 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState() | |
kinuko
2015/11/17 14:51:41
nit: could these inner-class definitions be placed
dmurph
2015/11/17 20:40:19
Done.
| |
163 : next_request(0), | |
164 num_fulfilled_requests(0), | |
165 num_shared_memory_requests(0), | |
166 current_shared_memory_handle_index(0) {} | |
167 | |
168 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} | |
169 | |
170 void BlobAsyncBuilderHost::ContinueBlobMemoryRequests(const std::string& uuid) { | |
171 AsyncBlobMap::iterator state_it = async_blob_map_.find(uuid); | |
172 DCHECK(state_it != async_blob_map_.end()); | |
173 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second; | |
174 | |
175 const auto& requests = state->transport_strategy.requests(); | |
kinuko
2015/11/17 14:51:41
nit: prefer using non-auto class here
dmurph
2015/11/17 20:40:19
Done.
| |
176 BlobAsyncTransportStrategy& strategy = state->transport_strategy; | |
177 size_t num_requests = requests.size(); | |
178 if (state->num_fulfilled_requests == num_requests) { | |
179 DoneAndCleanup(uuid); | |
180 return; | |
181 } | |
182 DCHECK_LT(state->num_fulfilled_requests, num_requests); | |
183 | |
184 std::vector<BlobItemBytesRequest> byte_requests; | |
185 std::vector<base::SharedMemoryHandle> shared_memory; | |
186 std::vector<IPC::PlatformFileForTransit> files; | |
187 | |
188 if (state->next_request == num_requests) { | |
michaeln
2015/11/17 21:55:28
move this test/early exit up above the local vecto
dmurph
2015/11/19 02:06:17
Done.
| |
189 // We are still waiting on other requests to come back. | |
190 return; | |
191 } | |
192 | |
193 for (; state->next_request < num_requests; ++state->next_request) { | |
194 const MemoryItemRequest& request = requests[state->next_request]; | |
195 | |
196 bool stop_accumulating = false; | |
197 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; | |
198 switch (request.message.transport_strategy) { | |
199 case IPCBlobItemRequestStrategy::IPC: | |
200 byte_requests.push_back(request.message); | |
201 break; | |
202 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
203 if (using_shared_memory_handle && | |
204 state->current_shared_memory_handle_index != | |
205 request.message.handle_index) { | |
206 // We only want one shared memory per requesting blob. | |
207 stop_accumulating = true; | |
208 break; | |
209 } | |
210 using_shared_memory_handle = true; | |
211 state->current_shared_memory_handle_index = | |
212 request.message.handle_index; | |
213 state->num_shared_memory_requests++; | |
214 | |
215 if (!state->shared_memory_block) { | |
216 state->shared_memory_block.reset(new base::SharedMemory()); | |
217 size_t size = | |
218 strategy.shared_memory_handles()[request.message.handle_index]; | |
219 if (!state->shared_memory_block->CreateAnonymous(size)) { | |
220 LOG(ERROR) << "Unable to allocate shared memory for blob transfer."; | |
221 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
222 return; | |
223 } | |
224 } | |
225 shared_memory.push_back(state->shared_memory_block->handle()); | |
226 byte_requests.push_back(request.message); | |
227 // Since we are only using one handle at a time, transform our handle | |
228 // index correctly back to 0. | |
229 byte_requests.back().handle_index = 0; | |
230 break; | |
231 case IPCBlobItemRequestStrategy::FILE: | |
232 case IPCBlobItemRequestStrategy::UNKNOWN: | |
233 NOTREACHED() << "Not implemented yet."; | |
234 break; | |
235 } | |
236 if (stop_accumulating) { | |
237 break; | |
238 } | |
239 } | |
240 | |
241 DCHECK(!requests.empty()); | |
242 | |
243 state->request_memory.Run(byte_requests, shared_memory, files); | |
244 } | |
245 | |
246 void BlobAsyncBuilderHost::CancelAndCleanup(const std::string& uuid, | |
247 IPCBlobCreationCancelCode code) { | |
248 AsyncBlobMap::iterator state_it = async_blob_map_.find(uuid); | |
249 if (state_it == async_blob_map_.end()) { | |
kinuko
2015/11/17 14:51:41
I feel this and the similar line in DoneAndCleanup
dmurph
2015/11/17 20:40:19
Done.
michaeln
2015/11/17 21:55:28
looks like this could be dcheck'able instead, i th
| |
250 return; | |
251 } | |
252 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second; | |
253 | |
254 state->cancel.Run(code); | |
255 delete state; | |
kinuko
2015/11/17 14:51:41
could use ScopedPtrMap or assign state_it->second
dmurph
2015/11/17 20:40:19
Done.
| |
256 async_blob_map_.erase(state_it); | |
257 } | |
258 | |
259 void BlobAsyncBuilderHost::DoneAndCleanup(const std::string& uuid) { | |
260 AsyncBlobMap::iterator state_it = async_blob_map_.find(uuid); | |
261 if (state_it == async_blob_map_.end()) { | |
michaeln
2015/11/17 21:55:27
ditto
dmurph
2015/11/19 02:06:17
Changed to scoped ptr map, and cleaned this up a b
| |
262 return; | |
263 } | |
264 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second; | |
265 BlobDataBuilder* builder = state->transport_strategy.blob_builder(); | |
266 builder->set_content_type(state->type); | |
267 DCHECK(builder); | |
268 state->done.Run(builder); | |
269 delete state; | |
270 async_blob_map_.erase(state_it); | |
271 } | |
272 | |
273 } // namespace storage | |
OLD | NEW |