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 <utility> | |
8 | |
9 #include "base/memory/shared_memory.h" | |
10 | |
11 namespace storage { | |
12 | |
13 using MemoryItemRequest = BlobAsyncTransportStrategy::RendererMemoryItemRequest; | |
14 | |
15 BlobAsyncBuilderHost::BlobBuildingState::BlobBuildingState() | |
16 : next_request(0), | |
17 num_fulfilled_requests(0), | |
18 num_shared_memory_requests(0), | |
19 current_shared_memory_handle_index(0) {} | |
20 | |
21 BlobAsyncBuilderHost::BlobBuildingState::~BlobBuildingState() {} | |
22 | |
23 BlobAsyncBuilderHost::BlobAsyncBuilderHost() {} | |
24 | |
25 BlobAsyncBuilderHost::~BlobAsyncBuilderHost() {} | |
26 | |
27 bool BlobAsyncBuilderHost::StartBuildingBlob( | |
28 const std::string& uuid, | |
29 const std::string& type, | |
30 const std::vector<DataElement>& descriptions, | |
31 size_t memory_available, | |
32 const base::Callback<void(const std::vector<storage::BlobItemBytesRequest>&, | |
33 const std::vector<base::SharedMemoryHandle>&, | |
34 const std::vector<uint64_t>&)>& request_memory, | |
35 const base::Callback<void(const BlobDataBuilder&)>& done, | |
36 const base::Callback<void(IPCBlobCreationCancelCode)>& cancel) { | |
37 if (BlobAsyncTransportStrategy::ShouldBeShortcut(descriptions, | |
38 memory_available)) { | |
39 // We have enough memory, and all the data is here, so we use the shortcut | |
40 // method and populate the old way. | |
41 BlobDataBuilder builder(uuid); | |
42 builder.set_content_type(type); | |
43 for (const DataElement& element : descriptions) { | |
44 builder.AppendIPCDataElement(element); | |
45 } | |
46 done.Run(builder); | |
47 return true; | |
48 } | |
49 | |
50 if (async_blob_map_.find(uuid) != async_blob_map_.end()) | |
51 return false; | |
kinuko
2015/11/25 16:08:17
nit: is it not necessary to do this check before l
dmurph
2015/11/25 21:16:30
Good catch.
| |
52 scoped_ptr<BlobBuildingState> state(new BlobBuildingState()); | |
53 BlobBuildingState* state_ptr = state.get(); | |
54 async_blob_map_[uuid] = std::move(state); | |
michaeln
2015/11/24 23:19:40
fancy :)
dmurph
2015/11/25 21:16:30
:)
| |
55 state_ptr->type = type; | |
56 state_ptr->request_memory_callback = request_memory; | |
57 state_ptr->done_callback = done; | |
58 state_ptr->cancel_callback = cancel; | |
59 | |
60 // We are currently only operating in 'no disk' mode. This will change in | |
61 // future patches to enable disk storage. | |
62 // Since we don't have a disk yet, we put 0 for disk_space_left. | |
63 state_ptr->transport_strategy.Initialize( | |
64 max_ipc_memory_size_, max_shared_memory_size_, max_file_size_, | |
65 0 /* disk_space_left */, memory_available, uuid, descriptions); | |
66 | |
67 switch (state_ptr->transport_strategy.error()) { | |
68 case BlobAsyncTransportStrategy::ERROR_NONE: | |
69 ContinueBlobMemoryRequests(uuid); | |
70 return true; | |
71 case BlobAsyncTransportStrategy::ERROR_TOO_LARGE: | |
72 // Cancel cleanly, we're out of memory. | |
73 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
74 return true; | |
75 case BlobAsyncTransportStrategy::ERROR_INVALID_PARAMS: | |
76 // Bad IPC, so we ignore and clean up. | |
77 async_blob_map_.erase(async_blob_map_.find(uuid)); | |
78 VLOG(1) << "Error initializing transport strategy: " | |
79 << state_ptr->transport_strategy.error(); | |
80 return false; | |
81 } | |
82 } | |
83 | |
84 bool BlobAsyncBuilderHost::OnMemoryResponses( | |
85 const std::string& uuid, | |
86 const std::vector<BlobItemBytesResponse>& responses) { | |
87 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | |
88 if (state_it == async_blob_map_.end()) | |
89 return false; | |
michaeln
2015/11/24 23:19:40
I'm not sure this one is always indicative of a ba
dmurph
2015/11/25 21:16:30
Done.
| |
90 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | |
91 BlobAsyncTransportStrategy& strategy = state->transport_strategy; | |
92 bool invalid_ipc = false; | |
93 bool memory_error = false; | |
94 const auto& requests = strategy.requests(); | |
95 for (const BlobItemBytesResponse& response : responses) { | |
96 if (response.request_number >= requests.size()) { | |
97 // Bad IPC, so we delete our record and ignore. | |
98 DVLOG(1) << "Invalid request number " << response.request_number; | |
99 async_blob_map_.erase(state_it); | |
100 return false; | |
101 } | |
102 const MemoryItemRequest& request = requests[response.request_number]; | |
103 if (request.received) { | |
104 // Bad IPC, so we delete our record. | |
105 DVLOG(1) << "Already received response for that request."; | |
106 async_blob_map_.erase(state_it); | |
107 return false; | |
108 } | |
109 switch (request.message.transport_strategy) { | |
110 case IPCBlobItemRequestStrategy::IPC: | |
111 if (response.inline_data.size() < request.message.size) { | |
112 DVLOG(1) << "Invalid data size " << response.inline_data.size() | |
113 << " vs requested size of " << request.message.size; | |
114 invalid_ipc = true; | |
115 break; | |
116 } | |
117 invalid_ipc = !strategy.blob_builder()->PopulateFutureData( | |
118 request.browser_item_index, &response.inline_data[0], | |
119 request.browser_item_offset, request.message.size); | |
120 break; | |
121 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
122 if (state->num_shared_memory_requests == 0) { | |
123 DVLOG(1) << "Received too many responses for shared memory."; | |
124 invalid_ipc = true; | |
125 break; | |
126 } | |
127 state->num_shared_memory_requests--; | |
128 if (!state->shared_memory_block->memory()) { | |
129 // We just map the whole block, as we'll probably be accessing the | |
130 // whole thing in this group of responses. Another option is to use | |
131 // MapAt, remove the mapped boolean, and then exclude the | |
132 // handle_offset below. | |
133 size_t handle_size = strategy.shared_memory_handle_sizes().at( | |
134 state->current_shared_memory_handle_index); | |
135 if (!state->shared_memory_block->Map(handle_size)) { | |
136 DVLOG(1) << "Unable to map memory to size " << handle_size; | |
137 memory_error = true; | |
138 break; | |
139 } | |
140 } | |
141 | |
142 invalid_ipc = !strategy.blob_builder()->PopulateFutureData( | |
143 request.browser_item_index, | |
144 static_cast<const char*>(state->shared_memory_block->memory()) + | |
145 request.message.handle_offset, | |
146 request.browser_item_offset, request.message.size); | |
147 break; | |
148 case IPCBlobItemRequestStrategy::FILE: | |
149 case IPCBlobItemRequestStrategy::UNKNOWN: | |
150 DVLOG(1) << "Not implemented."; | |
151 invalid_ipc = true; | |
152 break; | |
153 } | |
154 if (invalid_ipc) { | |
155 // Bad IPC, so we delete our record and return false. | |
156 async_blob_map_.erase(state_it); | |
157 return false; | |
158 } | |
159 if (memory_error) { | |
160 DVLOG(1) << "Shared memory error."; | |
161 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
162 return true; | |
163 } | |
164 state->num_fulfilled_requests++; | |
165 } | |
166 ContinueBlobMemoryRequests(uuid); | |
167 return true; | |
168 } | |
169 | |
170 void BlobAsyncBuilderHost::StopBuildingBlob(const std::string& uuid) { | |
171 async_blob_map_.erase(async_blob_map_.find(uuid)); | |
172 } | |
173 | |
174 void BlobAsyncBuilderHost::ContinueBlobMemoryRequests(const std::string& uuid) { | |
175 AsyncBlobMap::const_iterator state_it = async_blob_map_.find(uuid); | |
176 DCHECK(state_it != async_blob_map_.end()); | |
177 BlobAsyncBuilderHost::BlobBuildingState* state = state_it->second.get(); | |
178 | |
179 const std::vector<MemoryItemRequest>& requests = | |
180 state->transport_strategy.requests(); | |
181 BlobAsyncTransportStrategy& strategy = state->transport_strategy; | |
182 size_t num_requests = requests.size(); | |
183 if (state->num_fulfilled_requests == num_requests) { | |
184 DoneAndCleanup(uuid); | |
185 return; | |
186 } | |
187 DCHECK_LT(state->num_fulfilled_requests, num_requests); | |
188 if (state->next_request == num_requests) { | |
189 // We are still waiting on other requests to come back. | |
190 return; | |
191 } | |
192 | |
193 std::vector<BlobItemBytesRequest> byte_requests; | |
194 std::vector<base::SharedMemoryHandle> shared_memory; | |
195 std::vector<uint64_t> files; | |
196 | |
197 for (; state->next_request < num_requests; ++state->next_request) { | |
198 const MemoryItemRequest& request = requests[state->next_request]; | |
199 | |
200 bool stop_accumulating = false; | |
201 bool using_shared_memory_handle = state->num_shared_memory_requests > 0; | |
202 switch (request.message.transport_strategy) { | |
203 case IPCBlobItemRequestStrategy::IPC: | |
204 byte_requests.push_back(request.message); | |
205 break; | |
206 case IPCBlobItemRequestStrategy::SHARED_MEMORY: | |
207 if (using_shared_memory_handle && | |
208 state->current_shared_memory_handle_index != | |
209 request.message.handle_index) { | |
210 // We only want one shared memory per requesting blob. | |
211 stop_accumulating = true; | |
212 break; | |
213 } | |
214 using_shared_memory_handle = true; | |
215 state->current_shared_memory_handle_index = | |
216 request.message.handle_index; | |
217 state->num_shared_memory_requests++; | |
218 | |
219 if (!state->shared_memory_block) { | |
220 state->shared_memory_block.reset(new base::SharedMemory()); | |
221 size_t size = | |
222 strategy | |
223 .shared_memory_handle_sizes()[request.message.handle_index]; | |
224 if (!state->shared_memory_block->CreateAnonymous(size)) { | |
225 DVLOG(1) << "Unable to allocate shared memory for blob transfer."; | |
226 CancelAndCleanup(uuid, IPCBlobCreationCancelCode::OUT_OF_MEMORY); | |
227 return; | |
228 } | |
229 } | |
230 shared_memory.push_back(state->shared_memory_block->handle()); | |
231 byte_requests.push_back(request.message); | |
232 // Since we are only using one handle at a time, transform our handle | |
233 // index correctly back to 0. | |
234 byte_requests.back().handle_index = 0; | |
235 break; | |
236 case IPCBlobItemRequestStrategy::FILE: | |
237 case IPCBlobItemRequestStrategy::UNKNOWN: | |
238 NOTREACHED() << "Not implemented yet."; | |
239 break; | |
240 } | |
241 if (stop_accumulating) { | |
242 break; | |
243 } | |
244 } | |
245 | |
246 DCHECK(!requests.empty()); | |
247 | |
248 state->request_memory_callback.Run(byte_requests, shared_memory, files); | |
249 } | |
250 | |
251 void BlobAsyncBuilderHost::CancelAndCleanup(const std::string& uuid, | |
252 IPCBlobCreationCancelCode code) { | |
253 scoped_ptr<BlobBuildingState> state = std::move(async_blob_map_[uuid]); | |
254 async_blob_map_.erase(uuid); | |
255 DCHECK(state.get()); | |
kinuko
2015/11/25 16:08:17
nit: this DCHECK and similar ones on line 262, 264
dmurph
2015/11/25 21:16:30
makes sense. Done.
| |
256 state->cancel_callback.Run(code); | |
257 } | |
258 | |
259 void BlobAsyncBuilderHost::DoneAndCleanup(const std::string& uuid) { | |
260 scoped_ptr<BlobBuildingState> state = std::move(async_blob_map_[uuid]); | |
261 async_blob_map_.erase(uuid); | |
262 DCHECK(state.get()); | |
263 BlobDataBuilder* builder = state->transport_strategy.blob_builder(); | |
264 DCHECK(builder); | |
265 builder->set_content_type(state->type); | |
266 state->done_callback.Run(*builder); | |
267 } | |
268 | |
269 } // namespace storage | |
OLD | NEW |