# lib/goog/async/workqueue.js

1 | // Copyright 2015 The Closure Library Authors. All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS-IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | goog.provide('goog.async.WorkItem');
16 | goog.provide('goog.async.WorkQueue');
17 |
18 | goog.require('goog.asserts');
19 | goog.require('goog.async.FreeList');
20 |
21 |
22 | // TODO(johnlenz): generalize the WorkQueue if this is used by more
23 | // than goog.async.run.
24 |
25 |
26 |
27 | /**
28 | * A low GC workqueue. The key elements of this design:
29 | * - avoids the need for goog.bind or equivalent by carrying scope
30 | * - avoids the need for array reallocation by using a linked list
31 | * - minimizes work entry objects allocation by recycling objects
32 | * @constructor
33 | * @final
34 | * @struct
35 | */
36 | goog.async.WorkQueue = function() {
37 | this.workHead_ = null;
38 | this.workTail_ = null;
39 | };
40 |
41 |
42 | /** @define {number} The maximum number of entries to keep for recycling. */
43 | goog.define('goog.async.WorkQueue.DEFAULT_MAX_UNUSED', 100);
44 |
45 |
46 | /** @const @private {goog.async.FreeList<goog.async.WorkItem>} */
47 | goog.async.WorkQueue.freelist_ = new goog.async.FreeList(
48 | function() {return new goog.async.WorkItem(); },
49 | function(item) {item.reset()},
50 | goog.async.WorkQueue.DEFAULT_MAX_UNUSED);
51 |
52 |
53 | /**
54 | * @param {function()} fn
55 | * @param {Object|null|undefined} scope
56 | */
57 | goog.async.WorkQueue.prototype.add = function(fn, scope) {
58 | var item = this.getUnusedItem_();
59 | item.set(fn, scope);
60 |
61 | if (this.workTail_) {
62 | this.workTail_.next = item;
63 | this.workTail_ = item;
64 | } else {
65 | goog.asserts.assert(!this.workHead_);
66 | this.workHead_ = item;
67 | this.workTail_ = item;
68 | }
69 | };
70 |
71 |
72 | /**
73 | * @return {goog.async.WorkItem}
74 | */
75 | goog.async.WorkQueue.prototype.remove = function() {
76 | var item = null;
77 |
78 | if (this.workHead_) {
79 | item = this.workHead_;
80 | this.workHead_ = this.workHead_.next;
81 | if (!this.workHead_) {
82 | this.workTail_ = null;
83 | }
84 | item.next = null;
85 | }
86 | return item;
87 | };
88 |
89 |
90 | /**
91 | * @param {goog.async.WorkItem} item
92 | */
93 | goog.async.WorkQueue.prototype.returnUnused = function(item) {
94 | goog.async.WorkQueue.freelist_.put(item);
95 | };
96 |
97 |
98 | /**
99 | * @return {goog.async.WorkItem}
100 | * @private
101 | */
102 | goog.async.WorkQueue.prototype.getUnusedItem_ = function() {
103 | return goog.async.WorkQueue.freelist_.get();
104 | };
105 |
106 |
107 |
108 | /**
109 | * @constructor
110 | * @final
111 | * @struct
112 | */
113 | goog.async.WorkItem = function() {
114 | /** @type {?function()} */
115 | this.fn = null;
116 | /** @type {Object|null|undefined} */
117 | this.scope = null;
118 | /** @type {?goog.async.WorkItem} */
119 | this.next = null;
120 | };
121 |
122 |
123 | /**
124 | * @param {function()} fn
125 | * @param {Object|null|undefined} scope
126 | */
127 | goog.async.WorkItem.prototype.set = function(fn, scope) {
128 | this.fn = fn;
129 | this.scope = scope;
130 | this.next = null;
131 | };
132 |
133 |
134 | /** Reset the work item so they don't prevent GC before reuse */
135 | goog.async.WorkItem.prototype.reset = function() {
136 | this.fn = null;
137 | this.scope = null;
138 | this.next = null;
139 | };