package water;
import java.util.HashSet;
import java.util.Stack;
// A "scope" for tracking Key lifetimes.
//
// A Scope defines a *SINGLE THREADED* local lifetime management context,
// stored in Thread Local Storage. Scopes can be explicitly entered or exited.
// User keys created by this thread are tracked, and deleted when the scope is
// exited. Since enter & exit are explicit, failure to exit means the Keys
// leak (there is no reliable thread-on-exit cleanup action). You must call
// Scope.exit() at some point. Only user keys & Vec keys are tracked.
//
// Scopes support nesting. Scopes support partial cleanup: you can list Keys
// you'd like to keep in the exit() call. These will be "bumped up" to the
// higher nested scope - or escaped and become untracked at the top-level.
public class Scope {
// Thread-based Key lifetime tracking
static private final ThreadLocal<Scope> _scope = new ThreadLocal<Scope>() {
@Override protected Scope initialValue() { return new Scope(); }
};
private final Stack<HashSet<Key>> _keys = new Stack<HashSet<Key>>();
static public void enter() { _scope.get()._keys.push(new HashSet<Key>()); }
static public void exit () {
Stack<HashSet<Key>> keys = _scope.get()._keys;
if( keys.size()==0 ) return;
for( Key key : keys.pop() )
Lockable.delete(key);
}
static public Key exit(Key key) { throw H2O.unimpl(); }
static public Key[] exit(Key... key) { throw H2O.unimpl(); }
static public void track( Key k ) {
if( !k.user_allowed() && !k.isVec() ) return; // Not tracked
Scope scope = _scope.get(); // Pay the price of T.L.S. lookup
if( scope == null ) return; // Not tracking this thread
if( scope._keys.size() == 0 ) return; // Tracked in the past, but no scope now
scope._keys.peek().add(k); // Track key
}
}