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