• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2013 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'use strict';
6
7/**
8 * Worker for requests. Fetches requests from a queue and processes them
9 * synchronously, taking into account priorities. The highest priority is 0.
10 */
11function Worker() {
12  /**
13   * List of requests waiting to be checked. If these items are available in
14   * cache, then they are processed immediately after starting the worker.
15   * However, if they have to be downloaded, then these requests are moved
16   * to pendingRequests_.
17   *
18   * @type {Array.<Request>}
19   * @private
20   */
21  this.newRequests_ = [];
22
23  /**
24   * List of pending requests for images to be downloaded.
25   * @type {Array.<Request>}
26   * @private
27   */
28  this.pendingRequests_ = [];
29
30  /**
31   * List of requests being processed.
32   * @type {Array.<Request>}
33   * @private
34   */
35  this.activeRequests_ = [];
36
37  /**
38   * Hash array of requests being added to the queue, but not finalized yet.
39   * @type {Object}
40   * @private
41   */
42  this.requests_ = {};
43
44  /**
45   * If the worker has been started.
46   * @type {boolean}
47   * @private
48   */
49  this.started_ = false;
50}
51
52/**
53 * Maximum download requests to be run in parallel.
54 * @type {number}
55 * @const
56 */
57Worker.MAXIMUM_IN_PARALLEL = 5;
58
59/**
60 * Adds a request to the internal priority queue and executes it when requests
61 * with higher priorities are finished. If the result is cached, then it is
62 * processed immediately once the worker is started.
63 *
64 * @param {Request} request Request object.
65 */
66Worker.prototype.add = function(request) {
67  if (!this.started_) {
68    this.newRequests_.push(request);
69    this.requests_[request.getId()] = request;
70    return;
71  }
72
73  // Enqueue the request, since already started.
74  this.pendingRequests_.push(request);
75  this.sortPendingRequests_();
76
77  this.continue_();
78};
79
80/**
81 * Removes a request from the worker (if exists).
82 * @param {string} requestId Unique ID of the request.
83 */
84Worker.prototype.remove = function(requestId) {
85  var request = this.requests_[requestId];
86  if (!request)
87    return;
88
89  // Remove from the internal queues with pending tasks.
90  var newIndex = this.pendingRequests_.indexOf(request);
91  if (newIndex != -1)
92    this.newRequests_.splice(newIndex, 1);
93  var pendingIndex = this.pendingRequests_.indexOf(request);
94  if (pendingIndex != -1)
95    this.pendingRequests_.splice(pendingIndex, 1);
96
97  // Cancel the request.
98  request.cancel();
99  delete this.requests_[requestId];
100};
101
102/**
103 * Starts handling requests.
104 */
105Worker.prototype.start = function() {
106  this.started_ = true;
107
108  // Process tasks added before worker has been started.
109  this.pendingRequests_ = this.newRequests_;
110  this.sortPendingRequests_();
111  this.newRequests_ = [];
112
113  // Start serving enqueued requests.
114  this.continue_();
115};
116
117/**
118 * Sorts pending requests by priorities.
119 * @private
120 */
121Worker.prototype.sortPendingRequests_ = function() {
122  this.pendingRequests_.sort(function(a, b) {
123    return a.getPriority() - b.getPriority();
124  });
125};
126
127/**
128 * Processes pending requests from the queue. There is no guarantee that
129 * all of the tasks will be processed at once.
130 *
131 * @private
132 */
133Worker.prototype.continue_ = function() {
134  // Run only up to MAXIMUM_IN_PARALLEL in the same time.
135  while (this.pendingRequests_.length &&
136         this.activeRequests_.length < Worker.MAXIMUM_IN_PARALLEL) {
137    var request = this.pendingRequests_.shift();
138    this.activeRequests_.push(request);
139
140    // Try to load from cache. If doesn't exist, then download.
141    request.loadFromCacheAndProcess(
142        this.finish_.bind(this, request),
143        function(currentRequest) {
144          currentRequest.downloadAndProcess(
145              this.finish_.bind(this, currentRequest));
146        }.bind(this, request));
147  }
148};
149
150/**
151 * Handles finished requests.
152 *
153 * @param {Request} request Finished request.
154 * @private
155 */
156Worker.prototype.finish_ = function(request) {
157  var index = this.activeRequests_.indexOf(request);
158  if (index < 0)
159    console.warn('Request not found.');
160  this.activeRequests_.splice(index, 1);
161  delete this.requests_[request.getId()];
162
163  // Continue handling the most important requests (if started).
164  if (this.started_)
165    this.continue_();
166};
167