OLD | NEW |
---|---|
1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 library pool; | 5 library pool; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 | 9 |
10 import 'package:async/async.dart'; | 10 import 'package:async/async.dart'; |
(...skipping 28 matching lines...) Expand all Loading... | |
39 final _onReleaseCompleters = new Queue<Completer<PoolResource>>(); | 39 final _onReleaseCompleters = new Queue<Completer<PoolResource>>(); |
40 | 40 |
41 /// The maximum number of resources that may be allocated at once. | 41 /// The maximum number of resources that may be allocated at once. |
42 final int _maxAllocatedResources; | 42 final int _maxAllocatedResources; |
43 | 43 |
44 /// The number of resources that are currently allocated. | 44 /// The number of resources that are currently allocated. |
45 int _allocatedResources = 0; | 45 int _allocatedResources = 0; |
46 | 46 |
47 /// The timeout timer. | 47 /// The timeout timer. |
48 /// | 48 /// |
49 /// If [_timeout] isn't null, this timer is set as soon as the resource limit | 49 /// This timer is canceled as long as the pool is below the resource limit. |
50 /// is reached and is reset every time an resource is released or a new | 50 /// It's reset once the resource limit is reached and again every time an |
51 /// resource is requested. If it fires, that indicates that the caller became | 51 /// resource is released or a new resource is requested. If it fires, that |
52 /// deadlocked, likely due to files waiting for additional files to be read | 52 /// indicates that the caller became deadlocked, likely due to files waiting |
53 /// before they could be closed. | 53 /// for additional files to be read before they could be closed. |
54 Timer _timer; | 54 /// |
55 /// This is `null` if this pool shouldn't time out. | |
56 RestartableTimer _timer; | |
55 | 57 |
56 /// The amount of time to wait before timing out the pending resources. | 58 /// The amount of time to wait before timing out the pending resources. |
57 final Duration _timeout; | 59 final Duration _timeout; |
58 | 60 |
59 /// A [FutureGroup] that tracks all the `onRelease` callbacks for resources | 61 /// A [FutureGroup] that tracks all the `onRelease` callbacks for resources |
60 /// that have been marked releasable. | 62 /// that have been marked releasable. |
61 /// | 63 /// |
62 /// This is `null` until [close] is called. | 64 /// This is `null` until [close] is called. |
63 FutureGroup _closeGroup; | 65 FutureGroup _closeGroup; |
64 | 66 |
65 /// Whether [close] has been called. | 67 /// Whether [close] has been called. |
66 bool get isClosed => _closeGroup != null; | 68 bool get isClosed => _closeGroup != null; |
67 | 69 |
68 /// Creates a new pool with the given limit on how many resources may be | 70 /// Creates a new pool with the given limit on how many resources may be |
69 /// allocated at once. | 71 /// allocated at once. |
70 /// | 72 /// |
71 /// If [timeout] is passed, then if that much time passes without any activity | 73 /// If [timeout] is passed, then if that much time passes without any activity |
72 /// all pending [request] futures will throw a [TimeoutException]. This is | 74 /// all pending [request] futures will throw a [TimeoutException]. This is |
73 /// intended to avoid deadlocks. | 75 /// intended to avoid deadlocks. |
74 Pool(this._maxAllocatedResources, {Duration timeout}) | 76 Pool(this._maxAllocatedResources, {Duration timeout}) |
75 : _timeout = timeout; | 77 : _timeout = timeout { |
78 if (timeout != null) { | |
79 // Start the timer canceled since we only want to start counting down once | |
80 // we've run out of available resources. | |
81 _timer = new RestartableTimer(timeout, _onTimeout)..cancel(); | |
Bob Nystrom
2015/10/28 21:40:27
"cancel" is a weird name for this. To me, it impli
nweiz
2015/10/28 21:47:36
It's an inherited name from Timer. I wanted to kee
| |
82 } | |
83 } | |
76 | 84 |
77 /// Request a [PoolResource]. | 85 /// Request a [PoolResource]. |
78 /// | 86 /// |
79 /// If the maximum number of resources is already allocated, this will delay | 87 /// If the maximum number of resources is already allocated, this will delay |
80 /// until one of them is released. | 88 /// until one of them is released. |
81 Future<PoolResource> request() { | 89 Future<PoolResource> request() { |
82 if (isClosed) { | 90 if (isClosed) { |
83 throw new StateError("request() may not be called on a closed Pool."); | 91 throw new StateError("request() may not be called on a closed Pool."); |
84 } | 92 } |
85 | 93 |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
183 _onReleaseCompleters.removeFirst().completeError(error, stackTrace); | 191 _onReleaseCompleters.removeFirst().completeError(error, stackTrace); |
184 }); | 192 }); |
185 | 193 |
186 var completer = new Completer.sync(); | 194 var completer = new Completer.sync(); |
187 _onReleaseCompleters.add(completer); | 195 _onReleaseCompleters.add(completer); |
188 return completer.future; | 196 return completer.future; |
189 } | 197 } |
190 | 198 |
191 /// A resource has been requested, allocated, or released. | 199 /// A resource has been requested, allocated, or released. |
192 void _resetTimer() { | 200 void _resetTimer() { |
193 if (_timer != null) _timer.cancel(); | 201 if (_timer == null) return; |
194 if (_timeout == null || _requestedResources.isEmpty) { | 202 |
195 _timer = null; | 203 if (_requestedResources.isEmpty) { |
204 _timer.cancel(); | |
196 } else { | 205 } else { |
197 _timer = new Timer(_timeout, _onTimeout); | 206 _timer.reset(); |
198 } | 207 } |
199 } | 208 } |
200 | 209 |
201 /// Handles [_timer] timing out by causing all pending resource completers to | 210 /// Handles [_timer] timing out by causing all pending resource completers to |
202 /// emit exceptions. | 211 /// emit exceptions. |
203 void _onTimeout() { | 212 void _onTimeout() { |
204 for (var completer in _requestedResources) { | 213 for (var completer in _requestedResources) { |
205 completer.completeError( | 214 completer.completeError( |
206 new TimeoutException("Pool deadlock: all resources have been " | 215 new TimeoutException("Pool deadlock: all resources have been " |
207 "allocated for too long.", | 216 "allocated for too long.", |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
248 /// produce additional information later on. For example, an isolate's task | 257 /// produce additional information later on. For example, an isolate's task |
249 /// may be complete, but it could still emit asynchronous errors. | 258 /// may be complete, but it could still emit asynchronous errors. |
250 void allowRelease(onRelease()) { | 259 void allowRelease(onRelease()) { |
251 if (_released) { | 260 if (_released) { |
252 throw new StateError("A PoolResource may only be released once."); | 261 throw new StateError("A PoolResource may only be released once."); |
253 } | 262 } |
254 _released = true; | 263 _released = true; |
255 _pool._onResourceReleaseAllowed(onRelease); | 264 _pool._onResourceReleaseAllowed(onRelease); |
256 } | 265 } |
257 } | 266 } |
OLD | NEW |