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 "mojo/services/files/public/cpp/input_stream_file.h" | |
6 | |
7 #include <utility> | |
8 | |
9 #include "mojo/public/cpp/environment/logging.h" | |
10 | |
11 namespace files_impl { | |
12 | |
13 InputStreamFile::PendingRead::PendingRead(uint32_t num_bytes, | |
14 const ReadCallback& callback) | |
15 : num_bytes(num_bytes), callback(callback) {} | |
16 | |
17 InputStreamFile::PendingRead::~PendingRead() {} | |
18 | |
19 // static | |
20 std::unique_ptr<InputStreamFile> InputStreamFile::Create( | |
21 Client* client, | |
22 mojo::InterfaceRequest<mojo::files::File> request) { | |
23 // TODO(vtl): Use make_unique when we have C++14. | |
24 return std::unique_ptr<InputStreamFile>( | |
25 new InputStreamFile(client, request.Pass())); | |
26 } | |
27 | |
28 InputStreamFile::~InputStreamFile() { | |
29 if (was_destroyed_) | |
30 *was_destroyed_ = true; | |
31 } | |
32 | |
33 InputStreamFile::InputStreamFile( | |
34 Client* client, | |
35 mojo::InterfaceRequest<mojo::files::File> request) | |
36 : client_(client), | |
37 is_closed_(false), | |
38 was_destroyed_(nullptr), | |
39 binding_(this, request.Pass()) { | |
40 binding_.set_connection_error_handler([this]() { | |
41 if (client_) | |
42 client_->OnClosed(); | |
43 }); | |
44 } | |
45 | |
46 void InputStreamFile::Close(const CloseCallback& callback) { | |
47 if (is_closed_) { | |
48 callback.Run(mojo::files::Error::CLOSED); | |
49 return; | |
50 } | |
51 | |
52 // TODO(vtl): Call pending read callbacks? | |
53 | |
54 is_closed_ = true; | |
55 callback.Run(mojo::files::Error::OK); | |
56 | |
57 if (client_) | |
58 client_->OnClosed(); | |
59 } | |
60 | |
61 void InputStreamFile::Read(uint32_t num_bytes_to_read, | |
62 int64_t offset, | |
63 mojo::files::Whence whence, | |
64 const ReadCallback& callback) { | |
65 if (is_closed_) { | |
66 callback.Run(mojo::files::Error::CLOSED, mojo::Array<uint8_t>()); | |
67 return; | |
68 } | |
69 | |
70 if (offset != 0 || whence != mojo::files::Whence::FROM_CURRENT) { | |
71 // TODO(vtl): Is this the "right" behavior? | |
72 callback.Run(mojo::files::Error::INVALID_ARGUMENT, mojo::Array<uint8_t>()); | |
73 return; | |
74 } | |
75 | |
76 bool should_start_read = pending_read_queue_.empty(); | |
77 pending_read_queue_.push_back(PendingRead(num_bytes_to_read, callback)); | |
78 if (should_start_read) | |
79 StartRead(); | |
80 } | |
81 | |
82 void InputStreamFile::Write(mojo::Array<uint8_t> bytes_to_write, | |
83 int64_t offset, | |
84 mojo::files::Whence whence, | |
85 const WriteCallback& callback) { | |
86 MOJO_DCHECK(!bytes_to_write.is_null()); | |
87 | |
88 if (is_closed_) { | |
89 callback.Run(mojo::files::Error::CLOSED, 0); | |
90 return; | |
91 } | |
92 | |
93 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
94 // unsupported/EINVAL is better.) | |
95 callback.Run(mojo::files::Error::UNAVAILABLE, 0); | |
96 } | |
97 | |
98 void InputStreamFile::ReadToStream(mojo::ScopedDataPipeProducerHandle source, | |
99 int64_t offset, | |
100 mojo::files::Whence whence, | |
101 int64_t num_bytes_to_read, | |
102 const ReadToStreamCallback& callback) { | |
103 if (is_closed_) { | |
104 callback.Run(mojo::files::Error::CLOSED); | |
105 return; | |
106 } | |
107 | |
108 // TODO(vtl) | |
109 MOJO_DLOG(ERROR) << "Not implemented"; | |
110 callback.Run(mojo::files::Error::UNIMPLEMENTED); | |
111 } | |
112 | |
113 void InputStreamFile::WriteFromStream(mojo::ScopedDataPipeConsumerHandle sink, | |
114 int64_t offset, | |
115 mojo::files::Whence whence, | |
116 const WriteFromStreamCallback& callback) { | |
117 if (is_closed_) { | |
118 callback.Run(mojo::files::Error::CLOSED); | |
119 return; | |
120 } | |
121 | |
122 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
123 // unsupported/EINVAL is better.) | |
124 callback.Run(mojo::files::Error::UNAVAILABLE); | |
125 } | |
126 | |
127 void InputStreamFile::Tell(const TellCallback& callback) { | |
128 if (is_closed_) { | |
129 callback.Run(mojo::files::Error::CLOSED, 0); | |
130 return; | |
131 } | |
132 | |
133 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
134 // unsupported/EINVAL is better.) | |
135 callback.Run(mojo::files::Error::UNAVAILABLE, 0); | |
136 } | |
137 | |
138 void InputStreamFile::Seek(int64_t offset, | |
139 mojo::files::Whence whence, | |
140 const SeekCallback& callback) { | |
141 if (is_closed_) { | |
142 callback.Run(mojo::files::Error::CLOSED, 0); | |
143 return; | |
144 } | |
145 | |
146 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
147 // unsupported/EINVAL is better.) | |
148 callback.Run(mojo::files::Error::UNAVAILABLE, 0); | |
149 } | |
150 | |
151 void InputStreamFile::Stat(const StatCallback& callback) { | |
152 if (is_closed_) { | |
153 callback.Run(mojo::files::Error::CLOSED, nullptr); | |
154 return; | |
155 } | |
156 | |
157 // TODO(vtl) | |
158 MOJO_DLOG(ERROR) << "Not implemented"; | |
159 callback.Run(mojo::files::Error::UNIMPLEMENTED, nullptr); | |
160 } | |
161 | |
162 void InputStreamFile::Truncate(int64_t size, const TruncateCallback& callback) { | |
163 if (is_closed_) { | |
164 callback.Run(mojo::files::Error::CLOSED); | |
165 return; | |
166 } | |
167 | |
168 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
169 // unsupported/EINVAL is better.) | |
170 callback.Run(mojo::files::Error::UNAVAILABLE); | |
171 } | |
172 | |
173 void InputStreamFile::Touch(mojo::files::TimespecOrNowPtr atime, | |
174 mojo::files::TimespecOrNowPtr mtime, | |
175 const TouchCallback& callback) { | |
176 if (is_closed_) { | |
177 callback.Run(mojo::files::Error::CLOSED); | |
178 return; | |
179 } | |
180 | |
181 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
182 // unsupported/EINVAL is better.) | |
183 callback.Run(mojo::files::Error::UNAVAILABLE); | |
184 } | |
185 | |
186 void InputStreamFile::Dup(mojo::InterfaceRequest<mojo::files::File> file, | |
187 const DupCallback& callback) { | |
188 if (is_closed_) { | |
189 callback.Run(mojo::files::Error::CLOSED); | |
190 return; | |
191 } | |
192 | |
193 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
194 // unsupported/EINVAL is better.) | |
195 callback.Run(mojo::files::Error::UNAVAILABLE); | |
196 } | |
197 | |
198 void InputStreamFile::Reopen(mojo::InterfaceRequest<mojo::files::File> file, | |
199 uint32_t open_flags, | |
200 const ReopenCallback& callback) { | |
201 if (is_closed_) { | |
202 callback.Run(mojo::files::Error::CLOSED); | |
203 return; | |
204 } | |
205 | |
206 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
207 // unsupported/EINVAL is better.) | |
208 callback.Run(mojo::files::Error::UNAVAILABLE); | |
209 } | |
210 | |
211 void InputStreamFile::AsBuffer(const AsBufferCallback& callback) { | |
212 if (is_closed_) { | |
213 callback.Run(mojo::files::Error::CLOSED, mojo::ScopedSharedBufferHandle()); | |
214 return; | |
215 } | |
216 | |
217 // TODO(vtl): Is this what we want? (Also is "unavailable" right? Maybe | |
218 // unsupported/EINVAL is better.) | |
219 callback.Run(mojo::files::Error::UNAVAILABLE, | |
220 mojo::ScopedSharedBufferHandle()); | |
221 } | |
222 | |
223 void InputStreamFile::Ioctl(uint32_t request, | |
224 mojo::Array<uint32_t> in_values, | |
225 const IoctlCallback& callback) { | |
226 if (is_closed_) { | |
227 callback.Run(mojo::files::Error::CLOSED, mojo::Array<uint32_t>()); | |
228 return; | |
229 } | |
230 | |
231 callback.Run(mojo::files::Error::UNIMPLEMENTED, mojo::Array<uint32_t>()); | |
232 } | |
233 | |
234 void InputStreamFile::StartRead() { | |
235 MOJO_DCHECK(!pending_read_queue_.empty()); | |
236 | |
237 // If we don't have a client, just drain all the reads. | |
238 if (!client_) { | |
239 while (!pending_read_queue_.empty()) { | |
240 // TODO(vtl): Is this what we want? | |
241 pending_read_queue_.front().callback.Run(mojo::files::Error::UNAVAILABLE, | |
242 mojo::Array<uint8_t>()); | |
243 pending_read_queue_.pop_front(); | |
244 } | |
245 return; | |
246 } | |
247 | |
248 do { | |
249 // Find a non-zero-byte read, completing any zero-byte reads at the front of | |
250 // the queue. Note that we do this in FIFO order (thus couldn't have | |
251 // completed them earlier). | |
252 while (!pending_read_queue_.front().num_bytes) { | |
253 pending_read_queue_.front().callback.Run(mojo::files::Error::OK, | |
254 mojo::Array<uint8_t>()); | |
255 pending_read_queue_.pop_front(); | |
256 | |
257 if (pending_read_queue_.empty()) | |
258 return; | |
259 } | |
260 | |
261 // Binding |this| is OK, since the client must not call the callback if we | |
262 // are destroyed. | |
263 mojo::files::Error error = mojo::files::Error::INTERNAL; | |
264 mojo::Array<uint8_t> data; | |
265 // Detect if we were destroyed inside |RequestData()|. | |
266 bool was_destroyed = false; | |
267 MOJO_CHECK(!was_destroyed_); | |
268 was_destroyed_ = &was_destroyed; | |
269 bool synchronous_completion = client_->RequestData( | |
270 pending_read_queue_.front().num_bytes, &error, &data, | |
271 [this](mojo::files::Error e, mojo::Array<uint8_t> d) { | |
272 CompleteRead(e, d.Pass()); | |
273 if (!pending_read_queue_.empty()) | |
274 StartRead(); | |
275 }); | |
276 if (was_destroyed) | |
277 return; | |
278 was_destroyed_ = nullptr; | |
279 if (synchronous_completion) | |
280 CompleteRead(error, data.Pass()); | |
281 else | |
282 return; // Asynchronous completion. | |
283 } while (!pending_read_queue_.empty()); | |
284 } | |
285 | |
286 void InputStreamFile::CompleteRead(mojo::files::Error error, | |
287 mojo::Array<uint8_t> data) { | |
288 MOJO_CHECK(!pending_read_queue_.empty()); | |
289 | |
290 if (error != mojo::files::Error::OK) { | |
291 pending_read_queue_.front().callback.Run(error, mojo::Array<uint8_t>()); | |
292 pending_read_queue_.pop_front(); | |
293 return; | |
294 } | |
295 | |
296 MOJO_CHECK(!data.is_null()); | |
297 MOJO_CHECK(data.size() <= pending_read_queue_.front().num_bytes); | |
298 pending_read_queue_.front().callback.Run(mojo::files::Error::OK, data.Pass()); | |
299 pending_read_queue_.pop_front(); | |
300 } | |
301 | |
302 } // namespace files_impl | |
OLD | NEW |