Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(104)

Side by Side Diff: extensions/renderer/resources/data_sender.js

Issue 488003002: Add the JS data pipe client to be used to implement serial send. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@js-receive-pipe
Patch Set: address comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 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 define('data_sender', [
6 'async_waiter',
7 'device/serial/data_stream.mojom',
8 'mojo/public/js/bindings/core',
9 'mojo/public/js/bindings/router',
10 ], function(asyncWaiter, dataStreamMojom, core, routerModule) {
11 /**
12 * @module data_sender
13 */
14
15 /**
16 * A pending send operation.
17 * @param {ArrayBuffer} data The data to be sent.
18 * @constructor
19 * @alias module:data_sender~PendingSend
20 * @private
21 */
22 function PendingSend(data) {
23 /**
24 * The remaining data to be sent.
25 * @type {ArrayBuffer}
26 * @private
27 */
28 this.data_ = data;
29 /**
30 * The total length of data to be sent.
31 * @type {number}
32 * @private
33 */
34 this.length_ = data.byteLength;
35 /**
36 * The number of bytes that have been received by the DataSink.
37 * @type {number}
38 * @private
39 */
40 this.bytesSent_ = 0;
benwells 2014/08/20 06:13:47 This name is confusing. It isn't sent bytes, it is
Sam McNally 2014/08/20 06:30:56 Done.
41 /**
42 * The promise that will be resolved or rejected when this send completes
43 * or fails, respectively.
44 * @type {Promise.<number>}
45 * @private
46 */
47 this.promise_ = new Promise(function(resolve, reject) {
48 /**
49 * The callback to call on success.
50 * @type {Function}
51 * @private
52 */
53 this.successCallback_ = resolve;
54 /**
55 * The callback to call with the error on failure.
56 * @type {Function}
57 * @private
58 */
59 this.errorCallback_ = reject;
60 }.bind(this));
61 }
62
63 /**
64 * Returns the promise that will be resolved when this operation completes or
65 * rejected if an error occurs.
66 * @return {Promise.<number>} A promise to the number of bytes sent.
67 */
68 PendingSend.prototype.getPromise = function() {
69 return this.promise_;
70 };
71
72 /**
73 * @typedef module:data_sender~PendingSend.ReportBytesResult
74 * @property {number} bytesLeft The number of bytes reported that were not
75 * sent as part of the send.
76 * @property {boolean?} done Whether this send has completed.
77 * @property {number?} bytesToFlush The number of bytes to flush in the event
78 * of an error.
79 */
80
81 /**
82 * Invoked when the DataSink reports that bytes have been sent. Resolves the
83 * promise returned by
84 * [getPromise()]{@link module:data_sender~PendingSend#getPromise} once all
85 * bytes have been reported as sent.
86 * @param {number} numBytes The number of bytes sent.
87 * @return {module:data_sender~PendingSend.ReportBytesResult}
88 */
89 PendingSend.prototype.reportBytesSent = function(numBytes) {
90 var result = this.reportBytesSentInternal_(numBytes);
91 if (this.bytesSent_ == this.length_) {
92 result.done = true;
93 this.successCallback_(this.bytesSent_);
94 }
95 return result;
96 };
97
98 /**
99 * Invoked when the DataSink reports an error. Rejects the promise returned by
100 * [getPromise()]{@link module:data_sender~PendingSend#getPromise} unless the
101 * error occurred after this send, that is, unless numBytes is greater than
102 * the nubmer of outstanding bytes.
103 * @param {number} numBytes The number of bytes sent.
104 * @param {number} error The error reported by the DataSink.
105 * @return {module:data_sender~PendingSend.ReportBytesResult}
106 */
107 PendingSend.prototype.reportBytesSentAndError = function(numBytes, error) {
108 var result = this.reportBytesSentInternal_(numBytes);
109 // If there are remaining bytes to report, the error occurred after this
110 // PendingSend so we should report success.
111 if (result.bytesLeft > 0) {
112 this.successCallback_(this.bytesSent_);
113 return result;
114 }
115
116 var e = new Error();
117 e.error = error;
118 e.bytesSent = this.bytesSent_;
119 this.errorCallback_(e);
120 this.done = true;
121 result.bytesToFlush =
122 this.length_ - this.data_.byteLength - this.bytesSent_;
123 return result;
124 };
125
126 /**
127 * Updates the internal state in response to a report from the DataSink.
128 * @param {number} numBytes The number of bytes sent.
129 * @return {module:data_sender~PendingSend.ReportBytesResult}
130 * @private
131 */
132 PendingSend.prototype.reportBytesSentInternal_ = function(numBytes) {
133 this.bytesSent_ += numBytes;
134 var result = {bytesLeft: 0, bytesToFlush: 0};
benwells 2014/08/20 06:13:47 Nit: bytesToFlush isn't used in the normal (non-er
Sam McNally 2014/08/20 06:30:56 Done.
135 if (this.bytesSent_ > this.length_) {
136 result.bytesLeft = this.bytesSent_ - this.length_;
137 this.bytesSent_ = this.length_;
138 }
139 result.done = false;
140 return result;
141 };
142
143 /**
144 * Writes pending data into the data pipe.
145 * @param {MojoHandle} handle The handle to the data pipe.
146 * @return {number} The Mojo result corresponding to the outcome:
147 * <ul>
148 * <li>RESULT_OK if the write completes successfully;
149 * <li>RESULT_SHOULD_WAIT if some, but not all data was written; or
150 * <li>the data pipe error if the write failed.
151 * </ul>
152 */
153 PendingSend.prototype.sendData = function(handle) {
154 var result = core.writeData(
155 handle, new Int8Array(this.data_), core.WRITE_DATA_FLAG_NONE);
156 if (result.result != core.RESULT_OK)
157 return result.result;
158 this.data_ = this.data_.slice(result.numBytes);
159 return this.data_.byteLength ? core.RESULT_SHOULD_WAIT : core.RESULT_OK;
160 };
161
162 /**
163 * A DataSender that sends data to a DataSink.
164 * @param {MojoHandle} handle The handle to the DataSink.
165 * @param {number} bufferSize How large a buffer the data pipe should use.
166 * @param {number} fatalErrorValue The send error value to report in the
167 * event of a fatal error.
168 * @constructor
169 * @alias module:data_sender.DataSender
170 */
171 function DataSender(handle, bufferSize, fatalErrorValue) {
172 /**
173 * The [Router]{@link module:mojo/public/js/bindings/router.Router} for the
174 * connection to the DataSink.
175 * @private
176 */
177 this.router_ = new routerModule.Router(handle);
178 /**
179 * The connection to the DataSink.
180 * @private
181 */
182 this.sink_ = new dataStreamMojom.DataSinkProxy(this.router_);
183 this.router_.setIncomingReceiver(this);
184 var dataPipeOptions = {
185 flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
186 elementNumBytes: 1,
187 capacityNumBytes: bufferSize,
188 };
189 var sendPipe = core.createDataPipe(dataPipeOptions);
190 this.sink_.init(sendPipe.consumerHandle);
191 /**
192 * The handle to the data pipe to use for sending data.
193 * @private
194 */
195 this.sendPipe_ = sendPipe.producerHandle;
196 /**
197 * The error to be dispatched in the event of a fatal error.
198 * @type {number}
199 * @private
200 */
201 this.fatalErrorValue_ = fatalErrorValue;
202 /**
203 * The async waiter used to wait for
204 * {@link module:data_sender.DataSender#sendPipe_} to be writable.
205 * @type module:async_waiter.AsyncWaiter
206 * @private
207 */
208 this.waiter_ = new asyncWaiter.AsyncWaiter(
209 this.sendPipe_, core.HANDLE_SIGNAL_WRITABLE,
210 this.onHandleReady_.bind(this));
211 /**
212 * A queue of sends that have not fully written their data to the data pipe.
213 * @type module:data_sender~PendingSend[]
214 * @private
215 */
216 this.pendingSends_ = [];
217 /**
218 * A queue of sends that have written their data to the data pipe, but have
219 * not been received by the DataSink.
220 * @type module:data_sender~PendingSend[]
221 * @private
222 */
223 this.sendsAwaitingAck_ = [];
224 /**
225 * The callback that will resolve a pending cancel if one is in progress.
226 * @type Function
227 * @private
228 */
229 this.pendingCancel_ = null;
230 /**
231 * Whether this DataReceiver has shut down.
232 * @type {boolean}
233 * @private
234 */
235 this.shutDown_ = false;
236 }
237
238 DataSender.prototype =
239 $Object.create(dataStreamMojom.DataSinkClientStub.prototype);
240
241 /**
242 * Closes this DataSender.
243 */
244 DataSender.prototype.close = function() {
245 if (this.shutDown_)
246 return;
247 this.shutDown_ = true;
248 this.waiter_.stop();
249 this.router_.close();
250 core.close(this.sendPipe_);
251 while (this.pendingSends_.length) {
252 this.pendingSends_.pop().reportBytesSentAndError(
253 0, this.fatalErrorValue_);
254 }
255 while (this.sendsAwaitingAck_.length) {
256 this.sendsAwaitingAck_.pop().reportBytesSentAndError(
257 0, this.fatalErrorValue_);
258 }
259 if (this.pendingCancel_) {
260 this.pendingCancel_();
261 this.pendingCancel_ = null;
262 }
263 };
264
265 /**
266 * Sends data to the DataSink.
267 * @return {Promise.<number>} A promise to the number of bytes sent. If an
268 * error occurs, the promise will reject with an Error object with a
269 * property error containing the error code.
270 * @throws Will throw if this has encountered a fatal error or a cancel is in
271 * progress.
272 */
273 DataSender.prototype.send = function(data) {
274 if (this.shutDown_)
275 throw new Error('DataSender has been closed');
276 if (this.pendingCancel_)
277 throw new Error('Cancel in progress');
278 var send = new PendingSend(data);
279 this.pendingSends_.push(send);
280 if (!this.waiter_.isWaiting())
281 this.waiter_.start();
282 return send.getPromise();
283 };
284
285 /**
286 * Requests the cancellation of any in-progress sends. Calls to
287 * [send()]{@link module:data_sender.DataSender#send} will fail until the
288 * cancel has completed.
289 * @param {number} error The error to report for cancelled sends.
290 * @return {Promise} A promise that will resolve when the cancel completes.
291 * @throws Will throw if this has encountered a fatal error or another cancel
292 * is in progress.
293 */
294 DataSender.prototype.cancel = function(error) {
295 if (this.shutDown_)
296 throw new Error('DataSender has been closed');
297 if (this.pendingCancel_)
298 throw new Error('Cancel already in progress');
299 if (this.pendingSends_.length + this.sendsAwaitingAck_.length == 0)
300 return Promise.resolve();
301
302 this.sink_.cancel(error);
303 return new Promise(function(resolve) {
304 this.pendingCancel_ = resolve;
305 }.bind(this));
306 };
307
308 /**
309 * Invoked when |handle_| is ready to write. Writes to the data pipe if the
310 * wait is successful.
311 * @param {number} waitResult The result of the asynchronous wait.
312 * @private
313 */
314 DataSender.prototype.onHandleReady_ = function(result) {
315 if (result != core.RESULT_OK) {
316 this.close();
317 return;
318 }
319 while (this.pendingSends_.length) {
320 var result = this.pendingSends_[0].sendData(this.sendPipe_);
321 if (result == core.RESULT_OK) {
322 this.sendsAwaitingAck_.push(this.pendingSends_.shift());
323 } else if (result == core.RESULT_SHOULD_WAIT) {
324 this.waiter_.start();
325 return;
326 } else {
327 this.close();
328 return;
329 }
330 }
331 };
332
333 /**
334 * Calls and clears the pending cancel callback if one is pending.
335 * @private
336 */
337 DataSender.prototype.callCancelCallback_ = function() {
338 if (this.pendingCancel_) {
339 this.pendingCancel_();
340 this.pendingCancel_ = null;
341 }
342 };
343
344 /**
345 * Invoked by the DataSink to report that data has been successfully sent.
346 * @param {number} numBytes The number of bytes sent.
347 * @private
348 */
349 DataSender.prototype.reportBytesSent = function(numBytes) {
350 while (numBytes > 0 && this.sendsAwaitingAck_.length) {
351 var result = this.sendsAwaitingAck_[0].reportBytesSent(numBytes);
352 numBytes = result.bytesLeft;
benwells 2014/08/20 06:13:47 *Now* I understand bytesLeft. In the context of Pe
Sam McNally 2014/08/20 06:30:56 Done.
353 if (result.done)
354 this.sendsAwaitingAck_.shift();
355 }
356 if (numBytes > 0 && this.pendingSends_.length) {
357 var result = this.pendingSends_[0].reportBytesSent(numBytes);
358 numBytes = result.bytesLeft;
359 }
360 // A cancel is completed when all of the sends that were in progress have
361 // completed or failed. This is the case where all sends complete
362 // successfully.
363 if (this.pendingSends_.length + this.sendsAwaitingAck_.length == 0)
364 this.callCancelCallback_();
365 };
366
367 /**
368 * Invoked by the DataSink to report an error in sending data.
369 * @param {number} numBytes The number of bytes sent.
370 * @param {number} error The error reported by the DataSink.
371 * @private
372 */
373 DataSender.prototype.reportBytesSentAndError = function(numBytes, error) {
374 var bytesToFlush = 0;
375 while (this.sendsAwaitingAck_.length) {
376 var result = this.sendsAwaitingAck_[0].reportBytesSentAndError(
377 numBytes, error);
378 numBytes = result.bytesLeft;
379 this.sendsAwaitingAck_.shift();
380 bytesToFlush += result.bytesToFlush;
381 }
382 while (this.pendingSends_.length) {
383 var result = this.pendingSends_[0].reportBytesSentAndError(
384 numBytes, error);
385 numBytes = result.bytesLeft;
386 this.pendingSends_.shift();
387 // Only the first PendingSend in |pendingSends_| will have data to flush
benwells 2014/08/20 06:13:47 Nit: this comment is informative but doesn't seem
Sam McNally 2014/08/20 06:30:56 Done.
388 // as only the first can have written data to the data pipe.
389 bytesToFlush += result.bytesToFlush;
390 }
391 this.callCancelCallback_();
392 return Promise.resolve({bytes_to_flush: bytesToFlush});
393 };
394
395 return {DataSender: DataSender};
396 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698