Files
@ 8bb2f526d457
Branch filter:
Location: light9/light9/web/AutoDependencies.ts
8bb2f526d457
4.4 KiB
video/MP2T
logging
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | import debug from "debug";
import { Quad_Graph, Quad_Object, Quad_Predicate, Quad_Subject } from "n3";
import { filter } from "underscore";
import { allPatchSubjs, Patch } from "./patch";
const log = debug("autodep");
interface QuadPattern {
subject: Quad_Subject | null;
predicate: Quad_Predicate | null;
object: Quad_Object | null;
graph: Quad_Graph | null;
}
// use patch as an optional optimization, but you can't count on it
export type HandlerFunc = (p?: Patch) => void;
class Handler {
patterns: QuadPattern[];
innerHandlers: Handler[];
// a function and the quad patterns it cared about
constructor(public func: HandlerFunc | null, public label: string) {
this.patterns = []; // s,p,o,g quads that should trigger the next run
this.innerHandlers = []; // Handlers requested while this one was running
}
}
export class AutoDependencies {
handlers: Handler;
handlerStack: Handler[];
constructor() {
// tree of all known Handlers (at least those with non-empty
// patterns). Top node is not a handler.
this.handlers = new Handler(null, "root");
this.handlerStack = [this.handlers]; // currently running
}
runHandler(func: HandlerFunc, label: string) {
// what if we have this func already? duplicate is safe?
if (label == null) {
throw new Error("missing label");
}
const h = new Handler(func, label);
const tailChildren = this.handlerStack[this.handlerStack.length - 1].innerHandlers;
const matchingLabel = filter(tailChildren, (c: Handler) => c.label === label).length;
// ohno, something depends on some handlers getting run twice :(
if (matchingLabel < 2) {
tailChildren.push(h);
}
//console.time("handler #{label}")
// todo: this may fire 1-2 times before the
// graph is initially loaded, which is a waste. Try deferring it if we
// haven't gotten the graph yet.
this._rerunHandler(h, undefined);
}
//console.timeEnd("handler #{label}")
//@_logHandlerTree()
_rerunHandler(handler: Handler, patch?: Patch) {
handler.patterns = [];
this.handlerStack.push(handler);
try {
if (handler.func === null) {
throw new Error("tried to rerun root");
}
handler.func(patch);
} catch (e) {
log("error running handler: ", e);
} finally {
// assuming here it didn't get to do all its queries, we could
// add a *,*,*,* handler to call for sure the next time?
// log('done. got: ', handler.patterns)
this.handlerStack.pop();
}
}
// handler might have no watches, in which case we could forget about it
_logHandlerTree() {
log("handler tree:");
var prn = function (h: Handler, depth: number) {
let indent = "";
for (let i = 0; i < depth; i++) {
indent += " ";
}
log(`${indent} \"${h.label}\" ${h.patterns.length} pats`);
Array.from(h.innerHandlers).map((c: any) => prn(c, depth + 1));
};
prn(this.handlers, 0);
}
_handlerIsAffected(child: Handler, patchSubjs: Set<string>) {
if (patchSubjs === null) {
return true;
}
if (!child.patterns.length) {
return false;
}
for (let stmt of Array.from(child.patterns)) {
if (stmt.subject === null) {
// wildcard on subject
return true;
}
if (patchSubjs.has(stmt.subject.value)) {
return true;
}
}
return false;
}
graphChanged(patch: Patch) {
// SyncedGraph is telling us this patch just got applied to the graph.
const subjs = allPatchSubjs(patch);
var rerunInners = (cur: Handler) => {
const toRun = cur.innerHandlers.slice();
for (let child of Array.from(toRun)) {
//match = @_handlerIsAffected(child, subjs)
//continue if not match
//log('match', child.label, match)
//child.innerHandlers = [] # let all children get called again
this._rerunHandler(child, patch);
rerunInners(child);
}
};
rerunInners(this.handlers);
}
askedFor(s: Quad_Subject | null, p: Quad_Predicate | null, o: Quad_Object | null, g: Quad_Graph | null) {
// SyncedGraph is telling us someone did a query that depended on
// quads in the given pattern.
const current = this.handlerStack[this.handlerStack.length - 1];
if (current != null && current !== this.handlers) {
current.patterns.push({ subject: s, predicate: p, object: o, graph: g } as QuadPattern);
}
}
}
|