sclass WeakValueMap is AutoCloseable, IntSize {
// A value that has been stored with this key has been garbage
// collected.
event valueForKeyReleased(A key);
// internal
Map> map = syncMap();
RunnablesReferenceQueue queue;
bool myQueue = true;
*() {}
*(RunnablesReferenceQueue *queue) { myQueue = false; }
class MyRef extends WeakRef is Runnable {
A key;
*(A *key, B value) { super(value, queue()!); }
run {
bool current = syncMapRemoveKeyAndValuePair(map, key, this);
// Note that the value may be stale (replaed by another value in
// the meantime). If you want to distinguish that kind of event
// from a non-stale value being released, check the variable
// "current".
valueForKeyReleased(key);
}
}
// public methods follow
B get(A key) {
ret getWeakRef(map.get(key));
}
B put(A key, B value) {
synchronized(mutex()) {
if (value == null) {
B old = getWeakRef(map.get(key));
map.remove(key);
ret old;
} else
ret getWeakRef(map.put(key, new MyRef(key, value)));
}
}
O mutex() { ret collectionMutex(map); }
RunnablesReferenceQueue queue() {
synchronized(mutex()) {
if (queue == null) queue = new RunnablesReferenceQueue;
ret queue;
}
}
close {
if (myQueue)
synchronized(mutex()) {
dispose queue;
}
}
public int size() { ret map.size(); }
Map snapshot() {
synchronized(mutex()) {
new Map snapshot;
for (key, ref : map)
mapPut(snapshot, key, getWeakRef(ref));
ret snapshot;
}
}
void setQueue aka queue(RunnablesReferenceQueue queue) {
synchronized(mutex()) {
if (queue == null || this.queue == queue) ret;
if (this.queue != null)
fail("Can't change queue once we're started");
this.queue = queue;
myQueue = false;
}
}
}