Home | History | Annotate | Download | only in image_loader
      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  */
     11 function 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  */
     57 Worker.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  */
     66 Worker.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  */
     84 Worker.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  */
    105 Worker.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  */
    121 Worker.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  */
    133 Worker.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  */
    156 Worker.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