1 package org.simantics.db.impl.query;
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.concurrent.atomic.AtomicBoolean;
7 import org.simantics.db.AsyncReadGraph;
8 import org.simantics.db.RelationInfo;
9 import org.simantics.db.common.utils.Logger;
10 import org.simantics.db.exception.DatabaseException;
11 import org.simantics.db.impl.DebugPolicy;
12 import org.simantics.db.impl.graph.ReadGraphImpl;
13 import org.simantics.db.impl.procedure.InternalProcedure;
14 import org.simantics.db.procedure.AsyncMultiProcedure;
15 import org.simantics.db.procedure.AsyncProcedure;
16 import org.simantics.db.procedure.Listener;
17 import org.simantics.db.procedure.ListenerBase;
18 import org.simantics.db.procedure.Procedure;
19 import org.simantics.db.request.AsyncMultiRead;
20 import org.simantics.db.request.AsyncRead;
21 import org.simantics.db.request.ExternalRead;
22 import org.simantics.db.request.MultiRead;
23 import org.simantics.db.request.Read;
25 import gnu.trove.map.hash.THashMap;
27 public class QueryCache {
29 final public UnaryQueryHashMap<IntProcedure> directPredicatesMap;
30 final public UnaryQueryHashMap<IntProcedure> principalTypesMap;
31 final public THashMap<String, URIToResource> uriToResourceMap;
32 final public THashMap<String, NamespaceIndex> namespaceIndexMap22;
33 final public UnaryQueryHashMap<IntProcedure> projectsMap;
34 final public UnaryQueryHashMap<InternalProcedure<RelationInfo>> relationInfoMap;
35 final public UnaryQueryHashMap<InternalProcedure<IntSet>> superTypesMap;
36 final public UnaryQueryHashMap<InternalProcedure<IntSet>> typeHierarchyMap;
37 final public UnaryQueryHashMap<InternalProcedure<IntSet>> superRelationsMap;
38 final public UnaryQueryHashMap<InternalProcedure<IntSet>> typesMap;
39 final public UnaryQueryHashMap<InternalProcedure<byte[]>> valueMap;
40 final public DoubleKeyQueryHashMap<IntProcedure> directObjectsMap;
41 final public DoubleKeyQueryHashMap<IntProcedure> objectsMap;
42 final public UnaryQueryHashMap<IntProcedure> orderedSetMap;
43 final public UnaryQueryHashMap<IntProcedure> predicatesMap;
44 final public DoubleKeyQueryHashMap<TripleIntProcedure> statementsMap;
45 final public UnaryQueryHashMap<IntProcedure> assertedPredicatesMap;
46 final public BinaryQueryHashMap<TripleIntProcedure> assertedStatementsMap;
47 final public StableHashMap<ExternalRead, ExternalReadEntry> externalReadMap;
48 final public StableHashMap<AsyncRead, AsyncReadEntry> asyncReadMap;
49 final public StableHashMap<Read, ReadEntry> readMap;
50 final public StableHashMap<AsyncMultiRead, AsyncMultiReadEntry> asyncMultiReadMap;
51 final public StableHashMap<MultiRead, MultiReadEntry> multiReadMap;
53 final THashMap<CacheEntry, ArrayList<ListenerEntry>> listeners;
56 directPredicatesMap = new UnaryQueryHashMap();
57 valueMap = new UnaryQueryHashMap();
58 principalTypesMap = new UnaryQueryHashMap();
59 uriToResourceMap = new THashMap<String, URIToResource>();
60 namespaceIndexMap22 = new THashMap<String, NamespaceIndex>();
61 projectsMap = new UnaryQueryHashMap();
62 relationInfoMap = new UnaryQueryHashMap();
63 typeHierarchyMap = new UnaryQueryHashMap();
64 superTypesMap = new UnaryQueryHashMap();
65 superRelationsMap = new UnaryQueryHashMap();
66 typesMap = new UnaryQueryHashMap();
67 objectsMap = new DoubleKeyQueryHashMap();
68 orderedSetMap = new UnaryQueryHashMap();
69 predicatesMap = new UnaryQueryHashMap();
70 statementsMap = new DoubleKeyQueryHashMap();
71 directObjectsMap = new DoubleKeyQueryHashMap();
72 assertedPredicatesMap = new UnaryQueryHashMap();
73 assertedStatementsMap = new BinaryQueryHashMap();
74 asyncReadMap = new StableHashMap<AsyncRead, AsyncReadEntry>();
75 readMap = new StableHashMap<Read, ReadEntry>();
76 asyncMultiReadMap = new StableHashMap<AsyncMultiRead, AsyncMultiReadEntry>();
77 multiReadMap = new StableHashMap<MultiRead, MultiReadEntry>();
78 externalReadMap = new StableHashMap<ExternalRead, ExternalReadEntry>();
79 listeners = new THashMap<CacheEntry, ArrayList<ListenerEntry>>(10, 0.75f);
82 public int requestHash(Object object) {
84 return object.hashCode();
85 } catch (Throwable t) {
86 Logger.defaultLogError(t);
91 private CacheEntryBase getCached(Object query, int hash) {
92 if (query instanceof AsyncRead)
93 return asyncReadMap.get(query, hash);
94 else if (query instanceof Read)
95 return readMap.get(query, hash);
96 else if (query instanceof ExternalRead)
97 return externalReadMap.get(query, hash);
98 else if (query instanceof AsyncMultiRead)
99 return asyncMultiReadMap.get(query, hash);
100 throw new IllegalStateException();
103 private CacheEntryBase createEntry(QuerySupport support, Object query, int hash) {
105 CacheEntryBase result;
106 if (query instanceof AsyncRead) {
107 AsyncReadEntry entry = new AsyncReadEntry((AsyncRead)query);
108 asyncReadMap.put((AsyncRead)query, entry, hash);
110 } else if (query instanceof Read) {
111 ReadEntry entry = new ReadEntry((Read)query);
112 readMap.put((Read)query, entry, hash);
114 } else if (query instanceof ExternalRead) {
115 ExternalReadEntry entry = new ExternalReadEntry((ExternalRead)query);
116 externalReadMap.put((ExternalRead)query, entry, hash);
118 } else if (query instanceof AsyncMultiRead) {
119 AsyncMultiReadEntry entry = new AsyncMultiReadEntry((AsyncMultiRead)query);
120 asyncMultiReadMap.put((AsyncMultiRead)query, entry, hash);
123 throw new IllegalStateException();
127 result.clearResult(support);
133 public final <T> Object runQuery(final ReadGraphImpl graph, final Object query, final CacheEntry parent, final ListenerBase listener, final Object procedure) throws DatabaseException {
135 int hash = requestHash(query);
137 CacheEntryBase entry = getCached(query, hash);
139 if(parent == null && listener == null) {
140 if(entry != null && (entry.isReady() || entry.isExcepted())) {
141 return entry.performFromCache(graph, this, procedure);
143 return performQuery(graph, query, entry, null, procedure);
148 entry = createEntry(graph.processor.querySupport, query, hash);
149 return performForEach(graph, query, entry, parent, listener, procedure, false);
153 if(entry.isPending()) {
154 synchronized(entry) {
155 if(entry.isPending()) {
156 throw new IllegalStateException();
157 // final AsyncBarrierImpl parentBarrier = graph.state.barrier;
158 // if(entry.procs == null) entry.procs = new ArrayList<AsyncProcedure<T>>();
159 // entry.procs.add(new AsyncProcedure<T>() {
162 // public void execute(AsyncReadGraph graph, T result) {
163 // procedure.execute(graph, result);
164 // parentBarrier.dec(query);
168 // public void exception(AsyncReadGraph graph, Throwable throwable) {
169 // procedure.exception(graph, throwable);
170 // parentBarrier.dec(query);
174 // if(graph.parent != null || listener != null) {
175 // registerDependencies(graph, entry, parent, listener, procedure, false);
178 // query.perform(graph, procedure);
186 if(entry.isReady()) {
187 Object result = entry.performFromCache(graph, this, procedure);
188 registerDependencies(graph, entry, parent, listener, procedure, false);
191 return performForEach(graph, query, entry, parent, listener, procedure, false);
198 public <T> Object performQuery(ReadGraphImpl parentGraph, final Object query_, final CacheEntryBase entry_, ListenerEntry listenerEntry, Object procedure_) throws DatabaseException {
200 ReadGraphImpl queryGraph = parentGraph.withParent(entry_);
202 if(query_ instanceof AsyncRead) {
204 AsyncRead<T> query = (AsyncRead<T>)query_;
205 AsyncReadEntry<T> entry = (AsyncReadEntry<T>)entry_;
206 AsyncProcedure<T> procedure = (AsyncProcedure<T>)procedure_;
210 query.perform(queryGraph, new AsyncProcedure<T>() {
213 public void execute(AsyncReadGraph returnGraph, T result) {
214 ReadGraphImpl impl = (ReadGraphImpl)returnGraph;
215 entry.addOrSet(parentGraph, result);
216 if(listenerEntry != null) {
217 primeListenerEntry(listenerEntry, result);
220 procedure.execute(parentGraph, result);
221 } catch (Throwable t) {
224 // parentBarrier.dec(query);
228 public void exception(AsyncReadGraph returnGraph, Throwable t) {
229 ReadGraphImpl impl = (ReadGraphImpl)returnGraph;
230 // AsyncReadGraph resumeGraph = finalParentGraph.newAsync();
231 entry.except(parentGraph, t);
233 procedure.exception(parentGraph, t);
234 } catch (Throwable t2) {
235 t2.printStackTrace();
237 // parentBarrier.dec(query);
241 public String toString() {
242 return procedure.toString();
247 } catch (Throwable t) {
251 procedure.exception(parentGraph, t);
252 } catch (Throwable t2) {
253 t2.printStackTrace();
255 // parentBarrier.dec(query);
261 } else if (query_ instanceof Read) {
263 Read query = (Read)query_;
264 ReadEntry entry = (ReadEntry)entry_;
270 T result = (T)query.perform(queryGraph);
271 entry.addOrSet(queryGraph, result);
273 if(listenerEntry != null) primeListenerEntry(listenerEntry, result);
275 return (T)entry.get(parentGraph, parentGraph.processor, procedure_);
277 } catch (Throwable t) {
280 return (T)entry.get(parentGraph, parentGraph.processor, procedure_);
284 } else if (query_ instanceof ExternalRead) {
286 ExternalRead query = (ExternalRead)query_;
287 ExternalReadEntry entry = (ExternalReadEntry)entry_;
288 Procedure<T> procedure = (Procedure<T>)procedure_;
292 query.register(parentGraph, new Listener<T>() {
294 AtomicBoolean used = new AtomicBoolean(false);
297 public void execute(T result) {
300 if(entry.isDiscarded()) return;
301 if(entry.isExcepted()) entry.setPending();
303 if(used.compareAndSet(false, true)) {
304 entry.addOrSet(parentGraph.processor, result);
305 procedure.execute(result);
308 parentGraph.processor.updatePrimitive(query);
314 public void exception(Throwable t) {
318 if(used.compareAndSet(false, true)) {
319 procedure.exception(t);
321 // entry.queue(result);
322 parentGraph.processor.updatePrimitive(query);
328 public String toString() {
329 return procedure.toString();
333 public boolean isDisposed() {
334 return entry.isDiscarded() || !parentGraph.processor.isBound(entry);
339 return entry.getResult();
341 } catch (Throwable t) {
344 procedure.exception(t);
345 return entry.getResult();
349 } else if (query_ instanceof AsyncMultiRead) {
351 AsyncMultiRead query = (AsyncMultiRead)query_;
352 AsyncMultiReadEntry entry = (AsyncMultiReadEntry)entry_;
353 AsyncMultiProcedure<T> procedure = (AsyncMultiProcedure<T>)procedure_;
357 query.perform(queryGraph, new AsyncMultiProcedure<T>() {
360 public void execute(AsyncReadGraph graph, T result) {
361 ReadGraphImpl impl = (ReadGraphImpl)graph;
362 entry.addOrSet(result);
364 procedure.execute(parentGraph, result);
365 } catch (Throwable t) {
371 public void finished(AsyncReadGraph graph) {
372 ReadGraphImpl impl = (ReadGraphImpl)graph;
373 entry.finish(parentGraph);
375 procedure.finished(parentGraph);
376 } catch (Throwable t) {
382 public void exception(AsyncReadGraph graph, Throwable t) {
383 ReadGraphImpl impl = (ReadGraphImpl)graph;
384 entry.except(parentGraph, t);
386 procedure.exception(parentGraph, t);
387 } catch (Throwable t2) {
388 t2.printStackTrace();
394 return entry.getResult();
396 } catch (Throwable t) {
400 procedure.exception(parentGraph, t);
401 } catch (Throwable t2) {
402 t2.printStackTrace();
405 return entry.getResult();
411 throw new IllegalStateException();
417 public <T> Object performFromCache(ReadGraphImpl parentGraph, Object query, CacheEntryBase entry_, ListenerEntry listenerEntry, Object procedure_) throws DatabaseException {
419 Object result = entry_.performFromCache(parentGraph, this, procedure_);
420 if(listenerEntry != null) {
421 primeListenerEntry(listenerEntry, result);
425 // if(query instanceof AsyncRead) {
427 // AsyncProcedure<T> procedure = (AsyncProcedure<T>)procedure_;
432 // } else if(query instanceof Read) {
434 // ReadEntry entry = (ReadEntry)entry_;
436 // T result = (T)entry.get(parentGraph, parentGraph.processor, procedure_);
438 // if(listenerEntry != null) primeListenerEntry(listenerEntry, result);
442 // } else if(query instanceof ExternalRead) {
444 // ExternalReadEntry entry = (ExternalReadEntry)entry_;
445 // Procedure<T> procedure = (Procedure<T>)procedure_;
447 // return entry.performFromCache(procedure);
449 // } else if (query instanceof AsyncMultiRead) {
451 // AsyncMultiReadEntry entry = (AsyncMultiReadEntry)entry_;
452 // AsyncMultiProcedure<T> procedure = (AsyncMultiProcedure<T>)procedure_;
454 // return entry.performFromCache(parentGraph, this, procedure);
458 // throw new IllegalStateException();
464 public <T> Object performForEach(ReadGraphImpl parentGraph, final Object query, final CacheEntryBase entry, final CacheEntry parent, final ListenerBase base, final Object procedure,
465 boolean inferredDependency) throws DatabaseException {
467 // if (DebugPolicy.PERFORM)
468 // System.out.println("PE[ " + (query.hashCode() & THREAD_MASK) + "] " + query);
471 // assert (!collecting);
473 assert(!entry.isDiscarded());
475 final ListenerEntry listenerEntry = registerDependencies(parentGraph, entry, parent, base, procedure, inferredDependency);
477 // FRESH, REFUTED, EXCEPTED go here
478 if (!entry.isReady()) {
484 return performQuery(parentGraph, query, entry, listenerEntry, procedure);
490 return performFromCache(parentGraph, query, entry, listenerEntry, procedure);
492 // parentBarrier.dec(query);
500 synchronized public ListenerEntry registerDependencies(ReadGraphImpl graph, CacheEntry child, CacheEntry parent, ListenerBase listener, Object procedure, boolean inferred) {
502 if (parent != null && !inferred) {
504 if(!child.isImmutable(graph))
505 child.addParent(parent);
506 } catch (DatabaseException e) {
507 Logger.defaultLogError(e);
509 if(DebugPolicy.DEPENDENCIES) System.out.println(child + " -> " + parent);
512 if (listener != null) {
513 return registerListener(child, listener, procedure);
520 public ListenerEntry registerListener(final CacheEntry entry, final ListenerBase base, final Object procedure) {
522 assert (entry != null);
524 if (base.isDisposed())
527 return addListener(entry, base, procedure);
531 private void primeListenerEntry(final ListenerEntry entry, final Object result) {
532 entry.setLastKnown(result);
535 private ListenerEntry addListener(CacheEntry entry, ListenerBase base, Object procedure) {
537 assert (entry != null);
538 assert (procedure != null);
540 ArrayList<ListenerEntry> list = listeners.get(entry);
542 list = new ArrayList<ListenerEntry>(1);
543 listeners.put(entry, list);
546 ListenerEntry result = new ListenerEntry(entry, base, procedure);
547 int currentIndex = list.indexOf(result);
548 // There was already a listener
549 if(currentIndex > -1) {
550 ListenerEntry current = list.get(currentIndex);
551 if(!current.base.isDisposed()) return null;
552 list.set(currentIndex, result);
557 if(DebugPolicy.LISTENER) {
558 new Exception().printStackTrace();
559 System.out.println("addListener -> " + list.size() + " " + entry + " " + base + " " + procedure);
567 public Collection<CacheEntry> getRootList() {
569 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>();
571 for (Object e : valueMap.values()) {
572 result.add((CacheEntry) e);
574 for (Object e : directPredicatesMap.values()) {
575 result.add((CacheEntry) e);
577 for (Object e : objectsMap.values()) {
578 result.add((CacheEntry) e);
580 for (Object e : directObjectsMap.values()) {
581 result.add((CacheEntry) e);
583 for (Object e : principalTypesMap.values()) {
584 result.add((CacheEntry) e);
586 for (Object e : superRelationsMap.values()) {
587 result.add((CacheEntry) e);
589 for (Object e : superTypesMap.values()) {
590 result.add((CacheEntry) e);
592 for (Object e : typesMap.values()) {
593 result.add((CacheEntry) e);
595 for (Object e : objectsMap.values()) {
596 result.add((CacheEntry) e);
598 for (Object e : assertedStatementsMap.values()) {
599 result.add((CacheEntry) e);
601 for (Object e : readMap.values()) {
602 if(e instanceof CacheEntry) {
603 result.add((CacheEntry) e);
605 System.err.println("e=" + e);
608 for (Object e : asyncReadMap.values()) {
609 if(e instanceof CacheEntry) {
610 result.add((CacheEntry) e);
612 System.err.println("e=" + e);
615 for (Object e : externalReadMap.values()) {
616 result.add((CacheEntry) e);
618 for (Object e : orderedSetMap.values()) {
619 result.add((CacheEntry) e);
626 public int calculateCurrentSize() {
630 realSize += directPredicatesMap.size();
631 realSize += principalTypesMap.size();
632 realSize += uriToResourceMap.size();
633 realSize += namespaceIndexMap22.size();
634 realSize += projectsMap.size();
636 realSize += relationInfoMap.size();
637 realSize += superTypesMap.size();
638 realSize += typeHierarchyMap.size();
639 realSize += superRelationsMap.size();
640 realSize += typesMap.size();
642 realSize += valueMap.size();
643 realSize += directObjectsMap.size();
644 realSize += objectsMap.size();
645 realSize += orderedSetMap.size();
646 realSize += predicatesMap.size();
648 realSize += statementsMap.size();
649 realSize += assertedPredicatesMap.size();
650 realSize += assertedStatementsMap.size();
651 realSize += externalReadMap.size();
652 realSize += asyncReadMap.size();
654 realSize += readMap.size();
655 realSize += asyncMultiReadMap.size();
656 realSize += multiReadMap.size();
662 CacheCollectionResult allCaches(CacheCollectionResult result) {
664 int level = Integer.MAX_VALUE;
665 directPredicatesMap.values(level, result);
666 principalTypesMap.values(level, result);
667 for(CacheEntryBase e : uriToResourceMap.values())
668 if(e.getLevel() <= level)
670 for(CacheEntryBase e : namespaceIndexMap22.values())
671 if(e.getLevel() <= level)
673 projectsMap.values(level, result);
675 relationInfoMap.values(level, result);
676 superTypesMap.values(level, result);
677 typeHierarchyMap.values(level, result);
678 superRelationsMap.values(level, result);
679 typesMap.values(level, result);
681 valueMap.values(level, result);
682 directObjectsMap.values(level, result);
683 objectsMap.values(level, result);
684 orderedSetMap.values(level, result);
685 predicatesMap.values(level, result);
687 statementsMap.values(level, result);
688 assertedPredicatesMap.values(level, result);
689 assertedStatementsMap.values(level, result);
690 externalReadMap.values(level, result);
691 asyncReadMap.values(level, result);
693 readMap.values(level, result);
694 asyncMultiReadMap.values(level, result);
695 multiReadMap.values(level, result);
701 public void scanPending() {
703 ArrayList<CacheEntry> entries = new ArrayList<CacheEntry>();
705 entries.addAll(directPredicatesMap.values());
706 entries.addAll(principalTypesMap.values());
707 entries.addAll(uriToResourceMap.values());
708 entries.addAll(namespaceIndexMap22.values());
709 entries.addAll(projectsMap.values());
710 entries.addAll(relationInfoMap.values());
711 entries.addAll(superTypesMap.values());
712 entries.addAll(superRelationsMap.values());
713 entries.addAll(typesMap.values());
714 entries.addAll(valueMap.values());
715 entries.addAll(directObjectsMap.values());
716 entries.addAll(objectsMap.values());
717 entries.addAll(orderedSetMap.values());
718 entries.addAll(predicatesMap.values());
719 entries.addAll(orderedSetMap.values());
720 entries.addAll(statementsMap.values());
721 // entries.addAll(assertedObjectsMap.values());
722 entries.addAll(assertedPredicatesMap.values());
723 entries.addAll(assertedStatementsMap.values());
724 entries.addAll(externalReadMap.values());
725 entries.addAll(asyncReadMap.values());
726 entries.addAll(externalReadMap.values());
727 entries.addAll(readMap.values());
728 entries.addAll(asyncMultiReadMap.values());
729 entries.addAll(multiReadMap.values());
730 entries.addAll(readMap.values());
731 System.out.println(entries.size() + " entries.");
732 for(Object e : entries) {
733 if(e instanceof CacheEntry) {
734 CacheEntry en = (CacheEntry)e;
735 if(en.isPending()) System.out.println("pending " + e);
736 if(en.isExcepted()) System.out.println("excepted " + e);
737 if(en.isDiscarded()) System.out.println("discarded " + e);
738 if(en.isRefuted()) System.out.println("refuted " + e);
739 if(en.isFresh()) System.out.println("fresh " + e);
741 //System.out.println("Unknown object " + e);