// Copyright 2015 The Closure Library Authors. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. goog.provide('goog.async.WorkItem'); goog.provide('goog.async.WorkQueue'); goog.require('goog.asserts'); goog.require('goog.async.FreeList'); // TODO(johnlenz): generalize the WorkQueue if this is used by more // than goog.async.run. /** * A low GC workqueue. The key elements of this design: * - avoids the need for goog.bind or equivalent by carrying scope * - avoids the need for array reallocation by using a linked list * - minimizes work entry objects allocation by recycling objects * @constructor * @final * @struct */ goog.async.WorkQueue = function() { this.workHead_ = null; this.workTail_ = null; }; /** @define {number} The maximum number of entries to keep for recycling. */ goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100); /** @const @private {goog.async.FreeList} */ goog.async.WorkQueue.freelist_ = new goog.async.FreeList( function() { return new goog.async.WorkItem(); }, function(item) { item.reset(); }, goog.async.WorkQueue.DEFAULT_MAX_UNUSED); /** * @param {function()} fn * @param {Object|null|undefined} scope */ goog.async.WorkQueue.prototype.add = function(fn, scope) { var item = this.getUnusedItem_(); item.set(fn, scope); if (this.workTail_) { this.workTail_.next = item; this.workTail_ = item; } else { goog.asserts.assert(!this.workHead_); this.workHead_ = item; this.workTail_ = item; } }; /** * @return {goog.async.WorkItem} */ goog.async.WorkQueue.prototype.remove = function() { var item = null; if (this.workHead_) { item = this.workHead_; this.workHead_ = this.workHead_.next; if (!this.workHead_) { this.workTail_ = null; } item.next = null; } return item; }; /** * @param {goog.async.WorkItem} item */ goog.async.WorkQueue.prototype.returnUnused = function(item) { goog.async.WorkQueue.freelist_.put(item); }; /** * @return {goog.async.WorkItem} * @private */ goog.async.WorkQueue.prototype.getUnusedItem_ = function() { return goog.async.WorkQueue.freelist_.get(); }; /** * @constructor * @final * @struct */ goog.async.WorkItem = function() { /** @type {?function()} */ this.fn = null; /** @type {Object|null|undefined} */ this.scope = null; /** @type {?goog.async.WorkItem} */ this.next = null; }; /** * @param {function()} fn * @param {Object|null|undefined} scope */ goog.async.WorkItem.prototype.set = function(fn, scope) { this.fn = fn; this.scope = scope; this.next = null; }; /** Reset the work item so they don't prevent GC before reuse */ goog.async.WorkItem.prototype.reset = function() { this.fn = null; this.scope = null; this.next = null; };