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.
6 * @fileoverview Queue of pending requests from an origin.
12 * Represents a queued request. Once given a token, call complete() once the
13 * request is processed (or dropped.)
16 function QueuedRequestToken() {}
18 /** Completes (or cancels) this queued request. */
19 QueuedRequestToken.prototype.complete = function() {};
22 * @param {!RequestQueue} queue The queue for this request.
23 * @param {number} id An id for this request.
24 * @param {function(QueuedRequestToken)} beginCb Called when work may begin on
26 * @param {RequestToken} opt_prev Previous request in the same queue.
27 * @param {RequestToken} opt_next Next request in the same queue.
29 * @implements {QueuedRequestToken}
31 function RequestToken(queue, id, beginCb, opt_prev, opt_next) {
32 /** @private {!RequestQueue} */
34 /** @private {number} */
36 /** @type {function(QueuedRequestToken)} */
37 this.beginCb = beginCb;
38 /** @type {RequestToken} */
40 /** @type {RequestToken} */
42 /** @private {boolean} */
43 this.completed_ = false;
46 /** Completes (or cancels) this queued request. */
47 RequestToken.prototype.complete = function() {
48 if (this.completed_) {
49 // Either the caller called us more than once, or the timer is firing.
50 // Either way, nothing more to do here.
53 this.completed_ = true;
54 this.queue_.complete(this);
57 /** @return {boolean} Whether this token has already completed. */
58 RequestToken.prototype.completed = function() {
59 return this.completed_;
63 * @param {!SystemTimer} sysTimer A system timer implementation.
66 function RequestQueue(sysTimer) {
67 /** @private {!SystemTimer} */
68 this.sysTimer_ = sysTimer;
69 /** @private {RequestToken} */
71 /** @private {RequestToken} */
73 /** @private {number} */
78 * Inserts this token into the queue.
79 * @param {RequestToken} token Queue token
82 RequestQueue.prototype.insertToken_ = function(token) {
83 console.log(UTIL_fmt('token ' + this.id_ + ' inserted'));
84 if (this.head_ === null) {
88 if (!this.tail_) throw 'Non-empty list missing tail';
89 this.tail_.next = token;
90 token.prev = this.tail_;
96 * Removes this token from the queue.
97 * @param {RequestToken} token Queue token
100 RequestQueue.prototype.removeToken_ = function(token) {
102 token.next.prev = token.prev;
105 token.prev.next = token.next;
107 if (this.head_ === token && this.tail_ === token) {
108 this.head_ = this.tail_ = null;
110 if (this.head_ === token) {
111 this.head_ = token.next;
112 this.head_.prev = null;
114 if (this.tail_ === token) {
115 this.tail_ = token.prev;
116 this.tail_.next = null;
119 token.prev = token.next = null;
123 * Completes this token's request, and begins the next queued request, if one
125 * @param {RequestToken} token Queue token
127 RequestQueue.prototype.complete = function(token) {
128 console.log(UTIL_fmt('token ' + this.id_ + ' completed'));
129 var next = token.next;
130 this.removeToken_(token);
136 /** @return {boolean} Whether this queue is empty. */
137 RequestQueue.prototype.empty = function() {
138 return this.head_ === null;
142 * Queues this request, and, if it's the first request, begins work on it.
143 * @param {function(QueuedRequestToken)} beginCb Called when work begins on this
145 * @param {Countdown} timer Countdown timer
146 * @return {QueuedRequestToken} A token for the request.
148 RequestQueue.prototype.queueRequest = function(beginCb, timer) {
149 var startNow = this.empty();
150 var token = new RequestToken(this, ++this.id_, beginCb);
151 // Clone the timer to set a callback on it, which will ensure complete() is
152 // eventually called, even if the caller never gets around to it.
153 timer.clone(token.complete.bind(token));
154 this.insertToken_(token);
156 this.sysTimer_.setTimeout(function() {
157 if (!token.completed()) {
158 token.beginCb(token);
166 * @param {!SystemTimer} sysTimer A system timer implementation.
169 function OriginKeyedRequestQueue(sysTimer) {
170 /** @private {!SystemTimer} */
171 this.sysTimer_ = sysTimer;
172 /** @private {Object<string, !RequestQueue>} */
177 * Queues this request, and, if it's the first request, begins work on it.
178 * @param {string} appId Application Id
179 * @param {string} origin Request origin
180 * @param {function(QueuedRequestToken)} beginCb Called when work begins on this
182 * @param {Countdown} timer Countdown timer
183 * @return {QueuedRequestToken} A token for the request.
185 OriginKeyedRequestQueue.prototype.queueRequest =
186 function(appId, origin, beginCb, timer) {
187 var key = appId + ' ' + origin;
188 if (!this.requests_.hasOwnProperty(key)) {
189 this.requests_[key] = new RequestQueue(this.sysTimer_);
191 var queue = this.requests_[key];
192 return queue.queueRequest(beginCb, timer);