- forEach was returning the ExpiringElements, not the data.
- We shouldn't need to run checkTimer() on add(), generally. If adding the sole element, we can just schedule the expiration directly. - Simplified and functionalized some other bits. git-svn-id: svn+ssh://src.earth.threerings.net/narya/trunk@5778 542714f4-19e9-0310-aa3c-eee0fc999fb1
This commit is contained in:
@@ -35,6 +35,8 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
public function ExpiringSet (ttl :Number)
|
public function ExpiringSet (ttl :Number)
|
||||||
{
|
{
|
||||||
_ttl = Math.round(ttl * 1000);
|
_ttl = Math.round(ttl * 1000);
|
||||||
|
_timer = new Timer(_ttl, 1);
|
||||||
|
_timer.addEventListener(TimerEvent.TIMER, checkTimer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,12 +48,6 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
return _ttl / 1000;
|
return _ttl / 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
// from Set
|
|
||||||
public function isEmpty () :Boolean
|
|
||||||
{
|
|
||||||
return size() == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calling this function will not expire the elements, it simply removes them. No
|
* Calling this function will not expire the elements, it simply removes them. No
|
||||||
* ValueEvent will be dispatched.
|
* ValueEvent will be dispatched.
|
||||||
@@ -66,7 +62,7 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
public function forEach (fn :Function) :void
|
public function forEach (fn :Function) :void
|
||||||
{
|
{
|
||||||
for each (var e :ExpiringElement in _data) {
|
for each (var e :ExpiringElement in _data) {
|
||||||
fn(e);
|
fn(e.element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,15 +72,18 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
return _data.length;
|
return _data.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// from Set
|
||||||
|
public function isEmpty () :Boolean
|
||||||
|
{
|
||||||
|
return size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
// from Set
|
// from Set
|
||||||
public function contains (o :Object) :Boolean
|
public function contains (o :Object) :Boolean
|
||||||
{
|
{
|
||||||
for each (var e :ExpiringElement in _data) {
|
return _data.some(function (e :ExpiringElement, ... ignored) :Boolean {
|
||||||
if (e.objectEquals(o)) {
|
return e.objectEquals(o);
|
||||||
return true;
|
});
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -95,13 +94,9 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
public function add (o :Object) :Boolean
|
public function add (o :Object) :Boolean
|
||||||
{
|
{
|
||||||
var added :Boolean = true;
|
var added :Boolean = true;
|
||||||
var element :ExpiringElement;
|
|
||||||
var expire :int = getTimer() + _ttl;
|
|
||||||
for (var ii :int = 0; ii < _data.length; ii++) {
|
for (var ii :int = 0; ii < _data.length; ii++) {
|
||||||
if ((_data[ii] as ExpiringElement).objectEquals(o)) {
|
if (ExpiringElement(_data[ii]).objectEquals(o)) {
|
||||||
// already contained - update expire time and remove from current position.
|
// already contained, remove this one and re-add at end
|
||||||
element = _data[ii] as ExpiringElement;
|
|
||||||
element.expirationTime = expire;
|
|
||||||
_data.splice(ii, 1);
|
_data.splice(ii, 1);
|
||||||
added = false;
|
added = false;
|
||||||
break;
|
break;
|
||||||
@@ -110,8 +105,13 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
|
|
||||||
// push the item onto the queue. since each element has the same TTL, elements end up
|
// push the item onto the queue. since each element has the same TTL, elements end up
|
||||||
// being ordered by their expiration time.
|
// being ordered by their expiration time.
|
||||||
_data.push(element || new ExpiringElement(o, expire));
|
_data.push(new ExpiringElement(o, getTimer() + _ttl));
|
||||||
checkTimer();
|
if (_data.length == 1) {
|
||||||
|
// set up the timer to remove this one element, otherwise it was already running
|
||||||
|
_timer.reset();
|
||||||
|
_timer.delay = _ttl;
|
||||||
|
_timer.start();
|
||||||
|
}
|
||||||
return added;
|
return added;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,13 +121,11 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
// pull the item from anywhere in the queue. If we remove the first element, the timer
|
// pull the item from anywhere in the queue. If we remove the first element, the timer
|
||||||
// will harmlessly NOOP when it wakes up
|
// will harmlessly NOOP when it wakes up
|
||||||
for (var ii :int = 0; ii < _data.length; ii++) {
|
for (var ii :int = 0; ii < _data.length; ii++) {
|
||||||
var e :ExpiringElement = _data[ii] as ExpiringElement;
|
if (ExpiringElement(_data[ii]).objectEquals(o)) {
|
||||||
if (e.objectEquals(o)) {
|
|
||||||
_data.splice(ii, 1);
|
_data.splice(ii, 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -137,56 +135,33 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
*/
|
*/
|
||||||
public function toArray () :Array
|
public function toArray () :Array
|
||||||
{
|
{
|
||||||
var elements :Array = [];
|
return _data.map(function (e :ExpiringElement, ... ignored) :Object {
|
||||||
for each (var element :ExpiringElement in _data) {
|
return e.element;
|
||||||
elements.push(element.element);
|
});
|
||||||
}
|
|
||||||
return elements;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function checkTimer (...ignored) :void
|
/**
|
||||||
|
* Remove probably just one element from the front of the expiration queue, and
|
||||||
|
* schedule a wakeup for the time to the new head's expiration time.
|
||||||
|
*/
|
||||||
|
protected function checkTimer (... ignored) :void
|
||||||
{
|
{
|
||||||
// expiration check
|
|
||||||
var now :int = getTimer();
|
var now :int = getTimer();
|
||||||
while (headIsExpired(now)) {
|
while (_data.length > 0) {
|
||||||
// pop the head off the queue and dispatch an event
|
var e :ExpiringElement = ExpiringElement(_data[0]);
|
||||||
var head :ExpiringElement = _data.shift() as ExpiringElement;
|
var timeToExpire :int = e.expirationTime - now;
|
||||||
dispatchEvent(new ValueEvent(ELEMENT_EXPIRED, head.element));
|
if (timeToExpire <= 0) {
|
||||||
}
|
_data.shift(); // remove it
|
||||||
|
dispatchEvent(new ValueEvent(ELEMENT_EXPIRED, e.element)); // notify
|
||||||
|
|
||||||
// empty check
|
} else {
|
||||||
if (isEmpty()) {
|
// the head element is not yet expired
|
||||||
return;
|
_timer.reset();
|
||||||
|
_timer.delay = timeToExpire;
|
||||||
|
_timer.start();
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the timer is already running, and we're not empty, we want to just let it finish
|
|
||||||
// its current delay, and set up a new one then
|
|
||||||
if (_timer != null && _timer.running) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// sanity check
|
|
||||||
var delay :int = (_data[0] as ExpiringElement).expirationTime - now;
|
|
||||||
if (delay <= 0) {
|
|
||||||
// blow up
|
|
||||||
log.warning("calculated delay after expiring elements is invalid! [" + delay + "]");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// set up next delay
|
|
||||||
if (_timer == null) {
|
|
||||||
_timer = new Timer(delay, 1);
|
|
||||||
_timer.addEventListener(TimerEvent.TIMER, checkTimer);
|
|
||||||
} else {
|
|
||||||
_timer.delay = delay;
|
|
||||||
_timer.reset();
|
|
||||||
}
|
|
||||||
_timer.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function headIsExpired (now :int) :Boolean
|
|
||||||
{
|
|
||||||
return isEmpty() ? false : (_data[0] as ExpiringElement).isExpired(now);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static const log :Log = Log.getLog(ExpiringSet);
|
protected static const log :Log = Log.getLog(ExpiringSet);
|
||||||
@@ -195,7 +170,7 @@ public class ExpiringSet extends EventDispatcher
|
|||||||
protected /* final */ var _ttl :int;
|
protected /* final */ var _ttl :int;
|
||||||
|
|
||||||
/** Array of ExpiringElement instances, sorted by expiration time. */
|
/** Array of ExpiringElement instances, sorted by expiration time. */
|
||||||
protected var _data :Array = new Array();
|
protected var _data :Array = [];
|
||||||
|
|
||||||
protected var _timer :Timer;
|
protected var _timer :Timer;
|
||||||
}
|
}
|
||||||
@@ -214,11 +189,6 @@ class ExpiringElement
|
|||||||
this.expirationTime = expiration;
|
this.expirationTime = expiration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function isExpired (now :int) :Boolean
|
|
||||||
{
|
|
||||||
return expirationTime <= now;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function objectEquals (element :Object) :Boolean
|
public function objectEquals (element :Object) :Boolean
|
||||||
{
|
{
|
||||||
return Util.equals(element, this.element);
|
return Util.equals(element, this.element);
|
||||||
|
|||||||
Reference in New Issue
Block a user