Chromium Code Reviews| 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 |