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