]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.db.impl/src/org/simantics/db/impl/query/QueryProcessor.java
Fixed multiple issues causing dangling references to discarded queries
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / query / QueryProcessor.java
1 /*******************************************************************************
2  * Copyright (c) 2007, 2010 Association for Decentralized Information Management
3  * in Industry THTH ry.
4  * All rights reserved. This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License v1.0
6  * which accompanies this distribution, and is available at
7  * http://www.eclipse.org/legal/epl-v10.html
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.impl.query;
13
14 import java.io.BufferedOutputStream;
15 import java.io.File;
16 import java.io.FileOutputStream;
17 import java.io.IOException;
18 import java.io.PrintStream;
19 import java.util.ArrayList;
20 import java.util.Arrays;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.IdentityHashMap;
26 import java.util.Iterator;
27 import java.util.LinkedList;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Set;
31 import java.util.concurrent.Semaphore;
32 import java.util.concurrent.atomic.AtomicBoolean;
33 import java.util.concurrent.atomic.AtomicInteger;
34
35 import org.eclipse.core.runtime.Platform;
36 import org.simantics.databoard.Bindings;
37 import org.simantics.db.AsyncReadGraph;
38 import org.simantics.db.DevelopmentKeys;
39 import org.simantics.db.DirectStatements;
40 import org.simantics.db.ReadGraph;
41 import org.simantics.db.RelationInfo;
42 import org.simantics.db.Resource;
43 import org.simantics.db.Session;
44 import org.simantics.db.Statement;
45 import org.simantics.db.VirtualGraph;
46 import org.simantics.db.common.ByteFileReader;
47 import org.simantics.db.common.procedure.adapter.AsyncMultiProcedureAdapter;
48 import org.simantics.db.common.utils.Logger;
49 import org.simantics.db.exception.DatabaseException;
50 import org.simantics.db.exception.ManyObjectsForFunctionalRelationException;
51 import org.simantics.db.exception.NoInverseException;
52 import org.simantics.db.exception.ResourceNotFoundException;
53 import org.simantics.db.impl.ResourceImpl;
54 import org.simantics.db.impl.graph.BarrierTracing;
55 import org.simantics.db.impl.graph.ReadGraphImpl;
56 import org.simantics.db.impl.graph.ReadGraphSupport;
57 import org.simantics.db.impl.procedure.IntProcedureAdapter;
58 import org.simantics.db.impl.procedure.InternalProcedure;
59 import org.simantics.db.impl.procedure.TripleIntProcedureAdapter;
60 import org.simantics.db.impl.support.ResourceSupport;
61 import org.simantics.db.procedure.AsyncMultiListener;
62 import org.simantics.db.procedure.AsyncMultiProcedure;
63 import org.simantics.db.procedure.AsyncProcedure;
64 import org.simantics.db.procedure.AsyncSetListener;
65 import org.simantics.db.procedure.ListenerBase;
66 import org.simantics.db.procedure.MultiProcedure;
67 import org.simantics.db.procedure.StatementProcedure;
68 import org.simantics.db.procedure.SyncMultiProcedure;
69 import org.simantics.db.request.AsyncMultiRead;
70 import org.simantics.db.request.ExternalRead;
71 import org.simantics.db.request.MultiRead;
72 import org.simantics.db.request.RequestFlags;
73 import org.simantics.db.service.Bytes;
74 import org.simantics.layer0.Layer0;
75 import org.simantics.utils.DataContainer;
76 import org.simantics.utils.Development;
77 import org.simantics.utils.FileUtils;
78 import org.simantics.utils.datastructures.Pair;
79 import org.simantics.utils.datastructures.collections.CollectionUtils;
80 import org.simantics.utils.datastructures.disposable.AbstractDisposable;
81
82 import gnu.trove.procedure.TIntProcedure;
83 import gnu.trove.procedure.TLongProcedure;
84 import gnu.trove.procedure.TObjectProcedure;
85 import gnu.trove.set.hash.THashSet;
86 import gnu.trove.set.hash.TIntHashSet;
87
88 @SuppressWarnings({"rawtypes", "unchecked"})
89 final public class QueryProcessor extends AbstractDisposable implements ReadGraphSupport {
90
91         public static int                                       indent                = 0;
92
93         
94         // Garbage collection
95         
96         public int                                              boundQueries          = 0;
97
98
99         final private int                                       functionalRelation;
100
101         final private int                                       superrelationOf;
102
103         final private int                                       instanceOf;
104
105         final private int                                       inverseOf;
106
107         final private int                                       asserts;
108
109         final private int                                       hasPredicate;
110
111         final private int                                       hasPredicateInverse;
112
113         final private int                                       hasObject;
114
115         final private int                                       inherits;
116
117         final private int                                       subrelationOf;
118
119         final private int                                       rootLibrary;
120
121         /**
122          * A cache for the root library resource. Initialized in
123          * {@link #getRootLibraryResource()}.
124          */
125         private volatile ResourceImpl                           rootLibraryResource;
126
127         final private int                                       library;
128
129         final private int                                       consistsOf;
130
131         final private int                                       hasName;
132
133         AtomicInteger                                       sleepers = new AtomicInteger(0);
134
135         boolean                                         updating              = false;
136
137
138         final public QueryCache                                 cache;
139         final public QuerySupport                               querySupport;
140         final public Session                                    session;
141         final public ResourceSupport                            resourceSupport;
142         
143         final public Semaphore                                  requests = new Semaphore(1);
144         
145         final public QueryListening                            listening = new QueryListening(this);
146
147         QueryThread[]                                   executors;
148
149         enum ThreadState {
150
151                 INIT, RUN, SLEEP, DISPOSED
152
153         }
154
155         final Scheduling scheduling;
156         
157         public ThreadState[]                                                                    threadStates;
158         
159         final Object querySupportLock;
160         
161         public Long modificationCounter = 0L;
162
163         public void close() {
164         }
165
166
167     /*
168      * We are running errands while waiting for requests to complete.
169      * We can only run work that is part of the current root request to avoid any deadlocks
170      */
171     public boolean performPending(ReadGraphImpl under) {
172         SessionTask task = scheduling.getSubTask(under);
173                 if(task != null) {
174                         task.run(thread.get());
175                         return true;
176                 }
177                 return false;
178         }
179     
180     final public void scheduleNow(SessionTask request) {
181         SessionTask toExecute = scheduleOrReturnForExecution(request);
182         if(toExecute != null)
183             toExecute.run(thread.get());
184     }
185
186     final public SessionTask scheduleOrReturnForExecution(SessionTask request) {
187         
188         return scheduling.scheduleOrReturnForExecution(request);
189
190     }
191
192
193         final int THREADS;
194         final public int  THREAD_MASK;
195
196         final public static ThreadGroup QueryThreadGroup = new ThreadGroup("Query Thread Group");
197
198         public static abstract class SessionTask {
199
200             final protected ReadGraphImpl rootGraph;
201                 private int counter = 0;
202                 protected int position = 1;
203                 private Exception trace;
204
205                 public SessionTask() {
206                     this(null);
207                 }
208                 
209         public SessionTask(ReadGraphImpl rootGraph) {
210             this.rootGraph = rootGraph;
211         }
212         
213         public boolean isSubtask(ReadGraphImpl graph) {
214             return graph.isParent(rootGraph);
215         }
216
217         public abstract void run0(int thread);
218
219                 public final void run(int thread) {
220                     if(counter++ > 0) {
221                         if(BarrierTracing.BOOKKEEPING) {
222                             trace.printStackTrace();
223                             new Exception().printStackTrace();
224                         }
225                         throw new IllegalStateException("Multiple invocations of SessionTask!");
226                     }
227                     if(BarrierTracing.BOOKKEEPING) {
228                         trace = new Exception();
229                     }
230                     run0(thread);
231                 }
232                 
233                 public boolean maybeReady() {
234                         return true;
235                 }
236
237                 @Override
238                 public String toString() {
239                         if(rootGraph == null)
240                                 return "SessionTask[no graph]";
241                         else
242                                 return "SessionTask[" + rootGraph.parent + "]";
243                 }
244
245         }
246
247         public static abstract class SessionRead extends SessionTask {
248
249                 final public Semaphore notify;
250                 final public DataContainer<Throwable> throwable; 
251
252                 public SessionRead(DataContainer<Throwable> throwable, Semaphore notify) {
253                         super(null);
254                         this.throwable = throwable;
255                         this.notify = notify;
256                 }
257
258         }
259
260         public boolean resume(ReadGraphImpl graph) {
261                 return executors[0].runSynchronized();
262         }
263
264         public QueryProcessor(final int threads, QuerySupport core, Set<Thread> threadSet)
265                         throws DatabaseException {
266
267                 THREADS = threads;
268                 THREAD_MASK = threads - 1;
269
270                 scheduling = new Scheduling(requests);
271                 
272                 querySupport = core;
273                 cache = new QueryCache(core, threads);
274                 session = querySupport.getSession();
275                 resourceSupport = querySupport.getSupport();
276                 querySupportLock = core.getLock();
277
278                 executors = new QueryThread[THREADS];
279                 threadStates = new ThreadState[THREADS];
280
281                 for (int i = 0; i < THREADS; i++) {
282                         threadStates[i] = ThreadState.INIT;
283                 }
284
285                 for (int i = 0; i < THREADS; i++) {
286
287                         final int index = i;
288
289                         executors[i] = new QueryThread(session, this, index, "Query Thread " + index);
290
291                         threadSet.add(executors[i]);
292
293                 }
294
295                 // Now start threads
296                 for (int i = 0; i < THREADS; i++) {
297                         executors[i].start();
298                 }
299
300                 // Make sure that query threads are up and running
301                 while(sleepers.get() != THREADS) {
302                         try {
303                                 Thread.sleep(5);
304                         } catch (InterruptedException e) {
305                                 e.printStackTrace();
306                         }
307                 }
308
309                 rootLibrary = core.getBuiltin("http:/");
310                 boolean builtinsInstalled = rootLibrary != 0;
311
312                 if (builtinsInstalled) {
313                         functionalRelation = core.getBuiltin(Layer0.URIs.FunctionalRelation);
314                         assert (functionalRelation != 0);
315                 } else
316                         functionalRelation = 0;
317
318                 if (builtinsInstalled) {
319                         instanceOf = core.getBuiltin(Layer0.URIs.InstanceOf);
320                         assert (instanceOf != 0);
321                 } else
322                         instanceOf = 0;
323
324                 if (builtinsInstalled) {
325                         inverseOf = core.getBuiltin(Layer0.URIs.InverseOf);
326                         assert (inverseOf != 0);
327                 } else
328                         inverseOf = 0;
329
330
331                 if (builtinsInstalled) {
332                         inherits = core.getBuiltin(Layer0.URIs.Inherits);
333                         assert (inherits != 0);
334                 } else
335                         inherits = 0;
336
337                 if (builtinsInstalled) {
338                         asserts = core.getBuiltin(Layer0.URIs.Asserts);
339                         assert (asserts != 0);
340                 } else
341                         asserts = 0;
342
343                 if (builtinsInstalled) {
344                         hasPredicate = core.getBuiltin(Layer0.URIs.HasPredicate);
345                         assert (hasPredicate != 0);
346                 } else
347                         hasPredicate = 0;
348
349                 if (builtinsInstalled) {
350                         hasPredicateInverse = core.getBuiltin(Layer0.URIs.HasPredicateInverse);
351                         assert (hasPredicateInverse != 0);
352                 } else
353                         hasPredicateInverse = 0;
354
355                 if (builtinsInstalled) {
356                         hasObject = core.getBuiltin(Layer0.URIs.HasObject);
357                         assert (hasObject != 0);
358                 } else
359                         hasObject = 0;
360
361                 if (builtinsInstalled) {
362                         subrelationOf = core.getBuiltin(Layer0.URIs.SubrelationOf);
363                         assert (subrelationOf != 0);
364                 } else
365                         subrelationOf = 0;
366
367                 if (builtinsInstalled) {
368                         superrelationOf = core.getBuiltin(Layer0.URIs.SuperrelationOf);
369                         assert (superrelationOf != 0);
370                 } else
371                         superrelationOf = 0;
372
373                 if (builtinsInstalled) {
374                         library = core.getBuiltin(Layer0.URIs.Library);
375                         assert (library != 0);
376                 } else
377                         library = 0;
378
379                 if (builtinsInstalled) {
380                         consistsOf = core.getBuiltin(Layer0.URIs.ConsistsOf);
381                         assert (consistsOf != 0);
382                 } else
383                         consistsOf = 0;
384
385                 if (builtinsInstalled) {
386                         hasName = core.getBuiltin(Layer0.URIs.HasName);
387                         assert (hasName != 0);
388                 } else
389                         hasName = 0;
390
391         }
392
393         final public void releaseWrite(ReadGraphImpl graph) {
394                 propagateChangesInQueryCache(graph);
395                 modificationCounter++;
396         }
397
398         final public int getId(final Resource r) {
399                 return querySupport.getId(r);
400         }
401
402         public QuerySupport getCore() {
403                 return querySupport;
404         }
405
406         public int getFunctionalRelation() {
407                 return functionalRelation;
408         }
409
410         public int getInherits() {
411                 return inherits;
412         }
413
414         public int getInstanceOf() {
415                 return instanceOf;
416         }
417
418         public int getInverseOf() {
419                 return inverseOf;
420         }
421
422         public int getSubrelationOf() {
423                 return subrelationOf;
424         }
425
426         public int getSuperrelationOf() {
427                 return superrelationOf;
428         }
429
430         public int getAsserts() {
431                 return asserts;
432         }
433
434         public int getHasPredicate() {
435                 return hasPredicate;
436         }
437
438         public int getHasPredicateInverse() {
439                 return hasPredicateInverse;
440         }
441
442         public int getHasObject() {
443                 return hasObject;
444         }
445
446         public int getRootLibrary() {
447                 return rootLibrary;
448         }
449
450         public Resource getRootLibraryResource() {
451                 if (rootLibraryResource == null) {
452                         // Synchronization is not needed here, it doesn't matter if multiple
453                         // threads simultaneously set rootLibraryResource once.
454                         int root = getRootLibrary();
455                         if (root == 0)
456                                 throw new UnsupportedOperationException("database is not initialized, cannot get root library resource");
457                         this.rootLibraryResource = new ResourceImpl(querySupport.getSupport(), root);
458                 }
459                 return rootLibraryResource;
460         }
461
462         public int getLibrary() {
463                 return library;
464         }
465
466         public int getConsistsOf() {
467                 return consistsOf;
468         }
469
470         public int getHasName() {
471                 return hasName;
472         }
473
474         public void forResource(ReadGraphImpl graph, final String id, CacheEntry parent, final InternalProcedure<Integer> procedure) {
475
476                 try {
477                         
478                         QueryCache.runnerURIToResource(graph, id, parent, null, new InternalProcedure<Integer>() {
479
480                                 @Override
481                                 public void execute(ReadGraphImpl graph, Integer result) throws DatabaseException {
482
483                                         if (result != null && result != 0) {
484                                                 procedure.execute(graph, result);
485                                                 return;
486                                         }
487
488                                         // Fall back to using the fixed builtins.
489 //                                      result = querySupport.getBuiltin(id);
490 //                                      if (result != 0) {
491 //                                              procedure.execute(graph, result);
492 //                                              return;
493 //                                      } 
494
495 //                                      try {
496 //                                              result = querySupport.getRandomAccessReference(id);
497 //                                      } catch (ResourceNotFoundException e) {
498 //                                              procedure.exception(graph, e);
499 //                                              return;
500 //                                      }
501
502                                         if (result != 0) {
503                                                 procedure.execute(graph, result);
504                                         } else {
505                                                 procedure.exception(graph, new ResourceNotFoundException(id));
506                                         }
507
508                                 }
509
510                                 @Override
511                                 public void exception(ReadGraphImpl graph, Throwable t) throws DatabaseException {
512                                         procedure.exception(graph, t);
513                                 }
514
515                         });
516                 } catch (DatabaseException e) {
517                     
518                     try {
519                         
520                 procedure.exception(graph, e);
521                 
522             } catch (DatabaseException e1) {
523                 
524                 Logger.defaultLogError(e1);
525                 
526             }
527                     
528                 }
529
530         }
531
532         public void forBuiltin(ReadGraphImpl graph, final String id, CacheEntry parent, final InternalProcedure<Integer> procedure) throws DatabaseException {
533
534                 Integer result = querySupport.getBuiltin(id);
535                 if (result != 0) {
536                         procedure.execute(graph, result);
537                 } else {
538                         procedure.exception(graph, new ResourceNotFoundException(id));
539                 }
540
541         }
542
543         final <T> void runMultiRead(final ReadGraphImpl graph, MultiReadEntry cached, final MultiRead<T> query, final CacheEntry parent, final QueryProcessor provider, final ListenerBase listener, final SyncMultiProcedure<T> procedure) {
544
545                 try {
546                         QueryCache.runnerMultiReadEntry(graph, query, parent, listener, procedure);
547                 } catch (DatabaseException e) {
548                         throw new IllegalStateException(e);
549                 }
550
551         }
552
553         public final <T> void runAsyncMultiRead(final ReadGraphImpl graph, final AsyncMultiRead<T> query, final CacheEntry parent, final ListenerBase listener, final AsyncMultiProcedure<T> procedure) {
554
555                 
556                 try {
557                         QueryCache.runnerAsyncMultiReadEntry(graph, query, parent, listener, procedure);
558                 } catch (DatabaseException e) {
559                         throw new IllegalStateException(e);
560                 }
561
562         }
563
564         final <T> void runPrimitiveRead(ReadGraphImpl graph, ExternalReadEntry cached, final ExternalRead<T> query, final CacheEntry parent, final QueryProcessor provider, final ListenerBase listener, final AsyncProcedure<T> procedure) throws DatabaseException {
565                 QueryCache.runnerExternalReadEntry(graph, query, parent, listener, procedure);
566         }
567
568 //    @Override
569 //      public <T> T query(final ReadGraphImpl graph, final Read<T> query, final CacheEntry parent, final AsyncProcedure<T> procedure, final ListenerBase listener) throws DatabaseException {
570 //      
571 //      return QueryCache.resultReadEntry(graph, query, parent, listener, procedure);
572 //
573 //      }
574
575         public <T> void queryMultiRead(final ReadGraphImpl graph, final MultiRead<T> query, final CacheEntry parent, final ListenerBase listener, final SyncMultiProcedure<T> procedure) throws DatabaseException {
576
577                 QueryCache.runnerMultiReadEntry(graph, query, parent, listener, procedure);
578
579         }
580
581         public <T> void queryPrimitiveRead(final ReadGraphImpl graph, final ExternalRead<T> query, final CacheEntry parent, final ListenerBase listener, final AsyncProcedure<T> procedure) throws DatabaseException {
582
583                 QueryCache.runnerExternalReadEntry(graph, query, parent, listener, procedure);
584
585         }
586
587         boolean isBound(ExternalReadEntry<?> entry) {
588                 if(entry.hasParents()) return true;
589                 else if(listening.hasListener(entry)) return true;
590                 else return false;
591         }
592
593         static class Dummy implements InternalProcedure<Object>, IntProcedure {
594
595                 @Override
596                 public void execute(ReadGraphImpl graph, int i) {
597                 }
598
599                 @Override
600                 public void finished(ReadGraphImpl graph) {
601                 }
602
603                 @Override
604                 public void execute(ReadGraphImpl graph, Object result) {
605                 }
606
607                 @Override
608                 public void exception(ReadGraphImpl graph, Throwable throwable) {
609                 }
610                 
611         }
612         
613         private static final Dummy dummy = new Dummy();
614
615         /*
616     public <Procedure> Object performForEach2(ReadGraphImpl graph, UnaryQuery<Procedure> query, CacheEntry parent, ListenerBase listener, Procedure procedure) throws Throwable {
617
618         if (DebugPolicy.PERFORM)
619             System.out.println("PE[ " + (query.hashCode() &  THREAD_MASK) + "] " + query);
620
621         assert (!dirty);
622         assert (!collecting);
623
624         assert(query.assertNotDiscarded());
625
626         registerDependencies(graph, query, parent, listener, procedure, false);
627
628         // FRESH, REFUTED, EXCEPTED go here 
629         if (!query.isReady()) {
630
631             size++;
632             misses++;
633
634             query.computeForEach(graph, this, (Procedure)dummy, true);
635             return query.get(graph, this, null);
636
637         } else {
638
639             hits++;
640
641             return query.get(graph, this, procedure);
642
643         }
644
645     }
646         */
647         
648
649         interface QueryCollectorSupport {
650                 public CacheCollectionResult allCaches();
651                 public Collection<CacheEntry> getRootList();
652                 public int getCurrentSize();
653                 public int calculateCurrentSize();
654                 public CacheEntryBase iterate(int level);
655                 public void remove();
656                 public void setLevel(CacheEntryBase entry, int level);
657                 public boolean start(boolean flush);
658         }
659
660         interface QueryCollector {
661
662                 public void collect(int youngTarget, int allowedTimeInMs);
663
664         }
665
666         class QueryCollectorSupportImpl implements QueryCollectorSupport {
667
668                 private static final boolean DEBUG = false;
669                 private static final double ITERATION_RATIO = 0.2;
670                 
671                 private CacheCollectionResult iteration = new CacheCollectionResult();
672                 private boolean fresh = true;
673                 private boolean needDataInStart = true;
674                 
675                 QueryCollectorSupportImpl() {
676                         iteration.restart();
677                 }
678
679                 public CacheCollectionResult allCaches() {
680                         CacheCollectionResult result = new CacheCollectionResult();
681                         QueryProcessor.this.allCaches(result);
682                         result.restart();
683                         return result;
684                 }
685                 
686                 public boolean start(boolean flush) {
687                         // We need new data from query maps
688                         fresh = true;
689                         if(needDataInStart || flush) {
690                                 // Last run ended after processing all queries => refresh data
691                                 restart(flush ? 0.0 : ITERATION_RATIO);
692                         } else {
693                                 // continue with previous big data
694                         }
695                         // Notify caller about iteration situation
696                         return iteration.isAtStart();
697                 }
698
699                 private void restart(double targetRatio) {
700                         
701                         needDataInStart = true;
702
703                         long start = System.nanoTime();
704                         if(fresh) {
705                                 
706                                 // We need new data from query maps
707                                 
708                                 int iterationSize = iteration.size()+1;
709                                 int diff = calculateCurrentSize()-iterationSize;
710                                 
711                                 double ratio = (double)diff / (double)iterationSize;
712                                 boolean dirty = Math.abs(ratio) >= targetRatio;
713                                 
714                                 if(dirty) {
715                                         iteration = allCaches();
716                                         if(DEBUG) {
717                                                 System.err.print("iterate: allCaches in " + 1e-9*(System.nanoTime()-start) + "s. (" + iteration.size() + ") ");
718                                                 for(int i=0;i<CacheCollectionResult.LEVELS;i++)
719                                                         System.err.print(" " + iteration.levels[i].size());
720                                                 System.err.println("");
721                                         }
722                                 } else {
723                                         iteration.restart();
724                                 }
725                                 
726                                 fresh = false;
727                                 needDataInStart = false;
728                         } else {
729                                 // We are returning here within the same GC round - reuse the cache table
730                                 iteration.restart();
731                         }
732                         
733                         return;
734                         
735                 }
736                 
737                 @Override
738                 public CacheEntryBase iterate(int level) {
739                         
740                         CacheEntryBase entry = iteration.next(level);
741                         if(entry == null) {
742                                 restart(ITERATION_RATIO);
743                                 return null;
744                         }
745                         
746                         while(entry != null && entry.isDiscarded()) {
747                                 entry = iteration.next(level);
748                         }
749                         
750                         return entry;
751                         
752                 }
753                 
754                 @Override
755                 public void remove() {
756                         iteration.remove();
757                 }
758                 
759                 @Override
760                 public void setLevel(CacheEntryBase entry, int level) {
761                         iteration.setLevel(entry, level);
762                 }
763
764                 public Collection<CacheEntry> getRootList() {
765                         return cache.getRootList();
766                 }
767
768                 @Override
769                 public int calculateCurrentSize() {
770                         return cache.calculateCurrentSize();
771                 }
772
773                 @Override
774                 public int getCurrentSize() {
775                         return cache.size;
776                 }
777
778         }
779         //    final private static int MINIMUM_SIZE = (int)(Runtime.getRuntime().maxMemory() / 600);
780
781         private QueryCollectorSupport collectorSupport = new QueryCollectorSupportImpl();
782         private QueryCollector collector = new QueryCollectorImpl(this, collectorSupport);
783
784     public int querySize() {
785         return cache.size;
786     }
787
788         public void gc(int youngTarget, int allowedTimeInMs) {
789
790                 collector.collect(youngTarget, allowedTimeInMs);
791
792         }
793
794
795         void processParentReport(CacheEntry entry, Map<CacheEntry, Set<CacheEntry>> workarea) {
796
797                 if(entry.isDiscarded()) return;
798                 if(workarea.containsKey(entry)) return;
799                 
800                 Iterable<CacheEntry> parents = entry.getParents(this);
801                 HashSet<CacheEntry> ps = new HashSet<CacheEntry>();
802                 for(CacheEntry e : parents) {
803                         if(e.isDiscarded()) continue;
804                         ps.add(e);
805                         processParentReport(e, workarea);
806                 }
807                 workarea.put(entry, ps);
808
809         }
810
811         public synchronized String reportQueryActivity(File file) throws IOException {
812                 
813                 System.err.println("reportQueries " + file.getAbsolutePath());
814
815                 if (!isAlive())
816                         return "Disposed!";
817
818                 PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
819
820                 List<Pair<String,Integer>> entries = CollectionUtils.valueSortedEntries(Development.histogram);
821                 Collections.reverse(entries);
822                 
823                 for(Pair<String,Integer> entry : entries) {
824                         b.println(entry.first + ": " + entry.second);
825                 }
826
827                 b.close();
828                 
829                 Development.histogram.clear();
830
831                 return "OK";
832
833         }
834         
835         public synchronized String reportQueries(File file) throws IOException {
836
837                 System.err.println("reportQueries " + file.getAbsolutePath());
838
839                 if (!isAlive())
840                         return "Disposed!";
841
842                 PrintStream b = new PrintStream(new BufferedOutputStream(new FileOutputStream(file)));
843
844                 long start = System.nanoTime();
845
846 //              ArrayList<CacheEntry> all = ;
847                 
848                 Map<CacheEntry, Set<CacheEntry>> workarea = new HashMap<CacheEntry, Set<CacheEntry>>();
849                 Collection<CacheEntryBase> caches = allCaches(new CacheCollectionResult()).toCollection();
850                 for(CacheEntryBase entry : caches) {
851                         processParentReport(entry, workarea);
852                 }
853                 
854                 //        for(CacheEntry e : all) System.err.println("entry: " + e);
855
856                 long duration = System.nanoTime() - start;
857                 System.err.println("Query root set in " + 1e-9*duration + "s.");
858
859                 start = System.nanoTime();
860
861                 HashMap<CacheEntry, Integer> flagMap = new HashMap<CacheEntry, Integer>(); 
862
863                 int listeners = 0;
864
865                 for(CacheEntry entry : workarea.keySet()) {
866                         boolean listener = listening.hasListenerAfterDisposing(entry);
867                         boolean hasParents = entry.getParents(this).iterator().hasNext();
868                         if(listener) {
869                                 // Bound
870                                 flagMap.put(entry, 0);
871                         } else if (!hasParents) {
872                                 // Unbound
873                                 flagMap.put(entry, 1);
874                         } else {
875                                 // Unknown
876                                 flagMap.put(entry, 2);
877                         }
878                         //              // Write leaf bit
879                         //              entry.flags |= 4;
880                 }
881
882                 boolean done = true;
883                 int loops = 0;
884
885                 do {
886
887                         done = true;
888
889                         long start2 = System.nanoTime();
890
891                         int boundCounter = 0;
892                         int unboundCounter = 0;
893                         int unknownCounter = 0;
894
895                         for(CacheEntry<?> entry : workarea.keySet()) {
896
897                                 //System.err.println("process " + entry);
898
899                                 int flags = flagMap.get(entry);
900                                 int bindStatus = flags & 3;
901
902                                 if(bindStatus == 0) boundCounter++;
903                                 else if(bindStatus == 1) unboundCounter++;
904                                 else if(bindStatus == 2) unknownCounter++;
905
906                                 if(bindStatus < 2) continue;
907
908                                 int newStatus = 1;
909                                 for(CacheEntry parent : entry.getParents(this)) {
910
911                                         if(parent.isDiscarded()) flagMap.put(parent, 1);
912
913                                         int flags2 = flagMap.get(parent);
914                                         int bindStatus2 = flags2 & 3;
915                                         // Parent is bound => child is bound
916                                         if(bindStatus2 == 0) {
917                                                 newStatus = 0;
918                                                 break;
919                                         }
920                                         // Parent is unknown => child is unknown
921                                         else if (bindStatus2 == 2) {
922                                                 newStatus = 2;
923                                                 done = false;
924                                                 break;
925                                         }
926                                 }
927
928                                 flagMap.put(entry, newStatus);
929
930                         }
931
932                         duration = System.nanoTime() - start2;
933                         System.err.println("Query analysis pass (" + boundCounter + "/" + unboundCounter + "/" + unknownCounter + ") in "+ 1e-9*duration + "s.");
934                         b.println("Query analysis pass (" + boundCounter + "/" + unboundCounter + "/" + unknownCounter + ") in "+ 1e-9*duration + "s.");
935
936                 } while(!done && loops++ < 20);
937
938                 if(loops >= 20) {
939
940                         for(CacheEntry entry : workarea.keySet()) {
941
942                                 int bindStatus = flagMap.get(entry);
943                                 if(bindStatus == 2) System.err.println("Undefined bind status for " + entry);
944
945                         }
946
947                 }
948
949                 duration = System.nanoTime() - start;
950                 System.err.println("Query analysis in " + 1e-9*duration + "s.");
951
952                 Map<Class<?>, Integer> counts = new HashMap<Class<?>, Integer>();
953
954                 for(CacheEntry entry : workarea.keySet()) {
955                         Class<?> clazz = entry.getClass();
956                         if(entry instanceof ReadEntry) clazz = ((ReadEntry)entry).id.getClass(); 
957                         else if(entry instanceof MultiReadEntry) clazz = ((MultiReadEntry)entry).id.getClass(); 
958                         else if(entry instanceof AsyncReadEntry) clazz = ((AsyncReadEntry)entry).id.getClass(); 
959                         else if(entry instanceof AsyncMultiReadEntry) clazz = ((AsyncMultiReadEntry)entry).id.getClass(); 
960                         else if(entry instanceof ExternalReadEntry) clazz = ((ExternalReadEntry)entry).id.getClass(); 
961                         Integer c = counts.get(clazz);
962                         if(c == null) counts.put(clazz, -1);
963                         else counts.put(clazz, c-1);
964                 }
965
966                 b.print("// Simantics DB client query report file\n");
967                 b.print("// This file contains the following information\n");
968                 b.print("// -The amount of cached query instances per query class\n");
969                 b.print("// -The sizes of retained child sets\n");
970                 b.print("// -List of parents for each query (search for 'P <query name>')\n");
971                 b.print("//  -Followed by status, where\n");
972                 b.print("//   -0=bound\n");
973                 b.print("//   -1=free\n");
974                 b.print("//   -2=unknown\n");
975                 b.print("//   -L=has listener\n");
976                 b.print("// -List of children for each query (search for 'C <query name>')\n");
977
978                 b.print("----------------------------------------\n");
979
980                 b.print("// Queries by class\n");
981                 for(Pair<Class<?>, Integer> p : CollectionUtils.valueSortedEntries(counts)) {
982                         b.print(-p.second + " " + p.first.getName() + "\n");
983                 }
984
985                 Map<CacheEntry, Integer> hist = new HashMap<CacheEntry, Integer>();
986                 for(CacheEntry e : workarea.keySet())
987                         hist.put(e, -1);
988                 
989                 boolean changed = true;
990                 int iter = 0;
991                 while(changed && iter++<50) {
992                         
993                         changed = false;
994                         
995                         Map<CacheEntry, Integer> newHist = new HashMap<CacheEntry, Integer>();
996                         for(CacheEntry e : workarea.keySet())
997                                 newHist.put(e, -1);
998
999                         for(Map.Entry<CacheEntry, Set<CacheEntry>> e : workarea.entrySet()) {
1000                                 Integer c = hist.get(e.getKey());
1001                                 for(CacheEntry p : e.getValue()) {
1002                                         Integer i = newHist.get(p);
1003                                         newHist.put(p, i+c);
1004                                 }
1005                         }
1006                         for(CacheEntry e : workarea.keySet()) {
1007                                 Integer value = newHist.get(e);
1008                                 Integer old = hist.get(e);
1009                                 if(!value.equals(old)) {
1010                                         hist.put(e, value);
1011 //                                      System.err.println("hist " + e + ": " + old + " => " + value);
1012                                         changed = true;
1013                                 }
1014                         }
1015                         
1016                         System.err.println("Retained set iteration " + iter);
1017
1018                 }
1019
1020                 b.print("// Queries by retained set\n");
1021                 for(Pair<CacheEntry, Integer> p : CollectionUtils.valueSortedEntries(hist)) {
1022                         b.print("" + -p.second + " " + p.first + "\n");
1023                 }
1024
1025                 HashMap<CacheEntry, Collection<CacheEntry>> inverse = new HashMap<CacheEntry, Collection<CacheEntry>>();
1026
1027                 b.print("// Entry parent listing\n");
1028                 for(CacheEntry entry : workarea.keySet()) {
1029                         int status = flagMap.get(entry);
1030                         boolean hasListener = listening.hasListenerAfterDisposing(entry);
1031                         b.print("Q " + entry.toString());
1032                         if(hasListener) {
1033                                 b.print(" (L" + status + ")");
1034                                 listeners++;
1035                         } else {
1036                                 b.print(" (" + status + ")");
1037                         }
1038                         b.print("\n");
1039                         for(CacheEntry parent : workarea.get(entry)) {
1040                                 Collection<CacheEntry> inv = inverse.get(parent);
1041                                 if(inv == null) {
1042                                         inv = new ArrayList<CacheEntry>();
1043                                         inverse.put(parent, inv);
1044                                 }
1045                                 inv.add(entry);
1046                                 b.print("  " + parent.toString());
1047                                 b.print("\n");
1048                         }
1049                 }
1050
1051                 b.print("// Entry child listing\n");
1052                 for(Map.Entry<CacheEntry, Collection<CacheEntry>> entry : inverse.entrySet()) {
1053                         b.print("C " + entry.getKey().toString());
1054                         b.print("\n");
1055                         for(CacheEntry child : entry.getValue()) {
1056                                 Integer h = hist.get(child);
1057                                 if(h != null) {
1058                                         b.print("  " + h);
1059                                 } else {
1060                                         b.print("  <no children>");
1061                                 }
1062                                 b.print("  " + child.toString());
1063                                 b.print("\n");
1064                         }
1065                 }
1066
1067                 b.print("#queries: " + workarea.keySet().size() + "\n");
1068                 b.print("#listeners: " + listeners + "\n");
1069
1070                 b.close();
1071
1072                 return "Dumped " + workarea.keySet().size() + " queries.";
1073
1074         }
1075
1076     public synchronized void save() throws IOException {
1077
1078         long start = System.nanoTime();
1079
1080         Collection<CacheEntryBase> caches = allCaches(new CacheCollectionResult()).toCollection();
1081         Map<Long,List<CacheEntryBase>> cachesByCluster = new HashMap<>();
1082         for(CacheEntryBase entry : caches) {
1083             String clazz = entry.classId();
1084             if(clazz == null)
1085                 continue;
1086             long cluster = entry.cluster(this);
1087             List<CacheEntryBase> queries = cachesByCluster.get(cluster);
1088             if(queries == null) {
1089                 queries = new ArrayList<>();
1090                 cachesByCluster.put(cluster, queries);
1091             }
1092             queries.add(entry);
1093         }
1094
1095         File workspace = Platform.getLocation().toFile();
1096         File dir = new File(workspace, "queryData");
1097         FileUtils.deleteAll(dir);
1098
1099         dir.mkdir();
1100
1101         for(Long cluster : cachesByCluster.keySet()) {
1102
1103             List<CacheEntryBase> queries = cachesByCluster.get(cluster);
1104             QuerySerializer serializer = new QuerySerializer(this);
1105             int count = 0;
1106             int pos = serializer.writeUnknownSize();
1107             for(CacheEntryBase entry : queries) {
1108                 String clazz = entry.classId();
1109                 if(clazz == null)
1110                     continue;
1111                 try {
1112                     entry.serialize(serializer);
1113                     count++;
1114                 } catch (IllegalStateException e) {
1115                     System.err.println(e.getMessage());
1116                 }
1117             }
1118             serializer.setUnknownSize(pos, count);
1119
1120             System.err.println(serializer.bytes().length + " bytes for cluster " + cluster);
1121             FileUtils.writeFile(new File(dir, "" + cluster + ".queryData"), serializer.bytes());
1122
1123         }
1124
1125         long end = System.nanoTime();
1126
1127         System.err.println("saved queries in " + 1e-6*(end-start) + "ms.");
1128
1129     }
1130
1131     public void restore() throws IOException {
1132
1133         long start = System.nanoTime();
1134
1135         File workspace = Platform.getLocation().toFile();
1136         File dir = new File(workspace, "queryData");
1137         dir.mkdir();
1138
1139         for(File f : FileUtils.listFilesByExtension(dir, "queryData")) {
1140             byte[] bytes = FileUtils.readFile(f);
1141             QueryDeserializer qd = new QueryDeserializer(this, bytes);
1142             qd.readHeaders();
1143             qd.readQueries();
1144         }
1145
1146         long end = System.nanoTime();
1147
1148         System.err.println("restored queries in " + 1e-6*(end-start) + "ms.");
1149
1150     }
1151
1152         boolean removeQuery(CacheEntry entry) {
1153
1154                 // This entry has been removed before. No need to do anything here.
1155                 if(entry.isDiscarded()) return false;
1156
1157                 assert (!entry.isDiscarded());
1158
1159                 Query query = entry.getQuery();
1160
1161                 query.removeEntry(this);
1162
1163                 cache.updates++;
1164                 cache.size--;
1165
1166                 if((entry.getGCStatus() & CacheEntry.HAS_BEEN_BOUND) != 0)
1167                         boundQueries--;
1168                 
1169                 entry.discard();
1170
1171                 return true;
1172
1173         }
1174
1175         /**
1176          * 
1177          * @return true if this entry is being listened
1178          */
1179         private boolean updateQuery(UpdateEntry e, LinkedList<UpdateEntry> todo, IdentityHashMap<CacheEntry, CacheEntry> immediates) throws DatabaseException {
1180
1181                 assert (e != null);
1182
1183                 CacheEntry entry = e.entry;
1184
1185                 /*
1186                  * If the dependency graph forms a DAG, some entries are inserted in the
1187                  * todo list many times. They only need to be processed once though.
1188                  */
1189                 if (entry.isDiscarded()) {
1190                         if (Development.DEVELOPMENT) {
1191                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1192                                         System.err.print("D");
1193                                         for (int i = 0; i < e.indent; i++)
1194                                                 System.err.print(" ");
1195                                         System.err.println(entry.getQuery());
1196                                 }
1197                         }
1198 //                      System.err.println(" => DISCARDED");
1199                         return false;
1200                 }
1201
1202 //              if (entry.isRefuted()) {
1203 //                      if (Development.DEVELOPMENT) {
1204 //                              if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1205 //                                      System.err.print("R");
1206 //                                      for (int i = 0; i < e.indent; i++)
1207 //                                              System.err.print(" ");
1208 //                                      System.err.println(entry.getQuery());
1209 //                              }
1210 //                      }
1211 //                      return false;
1212 //              }
1213
1214                 if (entry.isExcepted()) {
1215                         if (Development.DEVELOPMENT) {
1216                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1217                                         System.err.print("E");
1218                                 }
1219                         }
1220                 }
1221
1222                 if (entry.isPending()) {
1223                         if (Development.DEVELOPMENT) {
1224                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1225                                         System.err.print("P");
1226                                 }
1227                         }
1228                 }
1229
1230                 cache.updates++;
1231
1232                 if (Development.DEVELOPMENT) {
1233                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1234                                 System.err.print("U ");
1235                                 for (int i = 0; i < e.indent; i++)
1236                                         System.err.print(" ");
1237                                 System.err.print(entry.getQuery());
1238                         }
1239                 }
1240
1241                 Query query = entry.getQuery();
1242                 int type = query.type();
1243
1244                 boolean hasListener = listening.hasListener(entry); 
1245
1246                 if (Development.DEVELOPMENT) {
1247                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1248                                 if(listening.hasListener(entry)) {
1249                                         System.err.println(" (L)");
1250                                 } else {
1251                                         System.err.println("");
1252                                 }
1253                         }
1254                 }
1255
1256                 if(entry.isPending() || entry.isExcepted()) {
1257
1258                         // If updated
1259                         if ((type & RequestFlags.UPDATE_MASK) == RequestFlags.IMMEDIATE_UPDATE) {
1260
1261                                 immediates.put(entry, entry);
1262
1263                         } else {
1264
1265                                 if(hasListener) {
1266                                         entry.refute();
1267                                 } else {
1268                                         removeQuery(entry);
1269                                 }
1270
1271                         }
1272
1273                 } else {
1274
1275                         // If updated
1276                         if ((type & RequestFlags.UPDATE_MASK) == RequestFlags.IMMEDIATE_UPDATE) {
1277
1278                                 immediates.put(entry, entry);
1279
1280                         } else {
1281
1282                                 if(hasListener) {
1283                                         entry.refute();
1284                                 } else {
1285                                         removeQuery(entry);
1286                                 }
1287
1288                         }
1289
1290                 }
1291
1292 //              System.err.println(" => FOO " + type);
1293
1294                 if (hasListener) {
1295                         ArrayList<ListenerEntry> entries = listening.listeners.get(entry);
1296                         if(entries != null) {
1297                                 for (ListenerEntry le : entries) {
1298                                         listening.scheduleListener(le);
1299                                 }
1300                         }
1301                 }
1302
1303                 // If invalid, update parents
1304                 if (type == RequestFlags.INVALIDATE) {
1305                         listening.updateParents(e.indent, entry, todo);
1306                 }
1307
1308                 return hasListener;
1309
1310         }
1311
1312         /**
1313          * @param av1 an array (guaranteed)
1314          * @param av2 any object
1315          * @return <code>true</code> if the two arrays are equal
1316          */
1317         private final boolean arrayEquals(Object av1, Object av2) {
1318                 if (av2 == null)
1319                         return false;
1320                 Class<?> c1 = av1.getClass().getComponentType();
1321                 Class<?> c2 = av2.getClass().getComponentType();
1322                 if (c2 == null || !c1.equals(c2))
1323                         return false;
1324                 boolean p1 = c1.isPrimitive();
1325                 boolean p2 = c2.isPrimitive();
1326                 if (p1 != p2)
1327                         return false;
1328                 if (!p1)
1329                         return Arrays.equals((Object[]) av1, (Object[]) av2);
1330                 if (boolean.class.equals(c1))
1331                         return Arrays.equals((boolean[]) av1, (boolean[]) av2);
1332                 else if (byte.class.equals(c1))
1333                         return Arrays.equals((byte[]) av1, (byte[]) av2);
1334                 else if (int.class.equals(c1))
1335                         return Arrays.equals((int[]) av1, (int[]) av2);
1336                 else if (long.class.equals(c1))
1337                         return Arrays.equals((long[]) av1, (long[]) av2);
1338                 else if (float.class.equals(c1))
1339                         return Arrays.equals((float[]) av1, (float[]) av2);
1340                 else if (double.class.equals(c1))
1341                         return Arrays.equals((double[]) av1, (double[]) av2);
1342                 throw new RuntimeException("??? Contact application querySupport.");
1343         }
1344
1345
1346
1347         final Object compareTo(ReadGraphImpl graph, final CacheEntry entry, final Object oldValue) {
1348
1349                 try {
1350
1351                         Query query = entry.getQuery();
1352
1353                         if (Development.DEVELOPMENT) {
1354                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_RECOMPUTE, Bindings.BOOLEAN)) {
1355                                         System.err.println("R " + query);
1356                                 }
1357                         }
1358
1359                         entry.prepareRecompute(querySupport);
1360                         
1361                         ReadGraphImpl parentGraph = graph.forRecompute(entry);
1362                         parentGraph.asyncBarrier.inc();
1363                         query.recompute(parentGraph);
1364                         parentGraph.asyncBarrier.dec();
1365
1366                         if(entry.isExcepted()) return ListenerEntry.NO_VALUE;
1367
1368                         Object newValue = entry.getResult();
1369
1370                         if (ListenerEntry.NO_VALUE == oldValue) {
1371                                 if (Development.DEVELOPMENT) {
1372                                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_CHANGES, Bindings.BOOLEAN)) {
1373                                                 System.out.println("C " + query);
1374                                                 System.out.println("- " + oldValue);
1375                                                 System.out.println("- " + newValue);
1376                                         }
1377                                 }
1378                                 return newValue;
1379                         }
1380
1381                         boolean changed = false;
1382
1383                         if (newValue != null) {
1384                                 if (newValue.getClass().isArray()) {
1385                                         changed = !arrayEquals(newValue, oldValue);
1386                                 } else {
1387                                         changed = !newValue.equals(oldValue);
1388                                 }
1389                         } else
1390                                 changed = (oldValue != null);
1391
1392                         if (Development.DEVELOPMENT) {
1393                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_CHANGES, Bindings.BOOLEAN)) {
1394                                         System.err.println("C " + query);
1395                                         System.err.println("- " + oldValue);
1396                                         System.err.println("- " + newValue);
1397                                 }
1398                         }
1399
1400                         return changed ? newValue : ListenerEntry.NOT_CHANGED;
1401
1402                 } catch (Throwable t) {
1403
1404                         Logger.defaultLogError(t);
1405                         entry.except(t);
1406                         return ListenerEntry.NO_VALUE;
1407
1408                 }
1409
1410         }
1411
1412
1413         /**
1414          * 
1415          * @return true if this entry still has listeners
1416          */
1417         public boolean update(final ReadGraphImpl graph, final CacheEntry entry) {
1418
1419                 assert (!cache.collecting);
1420                 assert (!updating);
1421                 updating = true;
1422
1423                 boolean hadListeners = false;
1424                 boolean listenersUnknown = false;
1425
1426                 try {
1427
1428                         assert(entry != null);
1429                         LinkedList<UpdateEntry> todo = new LinkedList<UpdateEntry>();
1430                         IdentityHashMap<CacheEntry, CacheEntry> immediates = new IdentityHashMap<CacheEntry, CacheEntry>();
1431                         todo.add(new UpdateEntry(null, entry, 0));
1432
1433                         while(true) {
1434
1435                                 // Walk the tree and collect immediate updates
1436                                 while (!todo.isEmpty()) {
1437                                         UpdateEntry e = todo.pop();
1438                                         hadListeners |= updateQuery(e, todo, immediates);
1439                                 }
1440
1441                                 if(immediates.isEmpty()) break;
1442
1443                                 // Evaluate all immediate updates and collect parents to update
1444                                 for(CacheEntry immediate : immediates.values()) {
1445
1446                                         if(immediate.isDiscarded()) {
1447                                                 continue;
1448                                         }
1449
1450                                         if(immediate.isExcepted()) {
1451
1452                                                 Object newValue = compareTo(graph, immediate, ListenerEntry.NO_VALUE);
1453                                                 if (newValue != ListenerEntry.NOT_CHANGED)
1454                                                         listening.updateParents(0, immediate, todo);
1455
1456                                         } else {
1457
1458                                                 Object oldValue = immediate.getResult();
1459                                                 Object newValue = compareTo(graph, immediate, oldValue);
1460
1461                                                 if (newValue != ListenerEntry.NOT_CHANGED) {
1462                                                         listening.updateParents(0, immediate, todo);
1463                                                 } else {
1464                                                         // If not changed, keep the old value
1465                                                         immediate.setResult(oldValue);
1466                                                         immediate.setReady();
1467                                                         listenersUnknown = true;
1468                                                 }
1469
1470                                         }
1471
1472                                 }
1473                                 immediates.clear();
1474
1475                         }
1476
1477                 } catch (Throwable t) {
1478                         Logger.defaultLogError(t);
1479                 }
1480
1481                 assert (updating);
1482                 updating = false;
1483
1484                 return hadListeners | listenersUnknown;
1485
1486         }
1487
1488         private ObjectUpdateSet scheduledObjectUpdates = new ObjectUpdateSet();
1489         private ValueUpdateSet scheduledValueUpdates = new ValueUpdateSet();
1490         private ValueUpdateSet scheduledInvalidates = new ValueUpdateSet();
1491         // Maybe use a mutex from util.concurrent?
1492         private Object primitiveUpdateLock = new Object();
1493         private THashSet scheduledPrimitiveUpdates = new THashSet();
1494
1495         private ArrayList<CacheEntry> refutations = new ArrayList<>();
1496         
1497         private void markForUpdate(ReadGraphImpl graph, CacheEntry e) {
1498                 e.refute();
1499                 refutations.add(e);
1500         }
1501
1502         private void updateRefutations(ReadGraphImpl graph) {
1503                 
1504                 for(CacheEntry e : refutations)
1505                         update(graph, e);
1506                 
1507                 refutations.clear();
1508                 
1509         }
1510         
1511         public void propagateChangesInQueryCache(final ReadGraphImpl graph) {
1512                 
1513                 // Make sure that listening has performed its work
1514                 listening.sync();
1515
1516                 cache.dirty = false;
1517                 lastInvalidate = 0;
1518
1519                 if (Development.DEVELOPMENT) {
1520                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1521                                 System.err.println("== Query update ==");
1522                         }
1523                 }
1524
1525                 // Special case - one statement
1526                 if(scheduledObjectUpdates.size() == 1 && scheduledValueUpdates.size() == 0 && scheduledPrimitiveUpdates.size() == 0 && scheduledInvalidates.size() == 0) {
1527
1528                         long arg0 = scheduledObjectUpdates.getFirst();
1529
1530                         final int subject = (int)(arg0 >>> 32);
1531                         final int predicate = (int)(arg0 & 0xffffffff);
1532
1533                         for(Objects o : QueryCache.entriesObjects(QueryProcessor.this, subject)) markForUpdate(graph, o);
1534                         for(DirectObjects o : QueryCache.entriesDirectObjects(QueryProcessor.this, subject)) markForUpdate(graph, o);
1535                         for(Statements o : QueryCache.entriesStatements(QueryProcessor.this, subject)) markForUpdate(graph, o);
1536
1537                         if(predicate == instanceOf || predicate == inherits || predicate == subrelationOf) {
1538                                 PrincipalTypes principalTypes = QueryCache.entryPrincipalTypes(QueryProcessor.this, subject);
1539                                 if(principalTypes != null) markForUpdate(graph, principalTypes);
1540                                 Types types = QueryCache.entryTypes(QueryProcessor.this, subject);
1541                                 if(types != null) markForUpdate(graph, types);
1542                         }
1543
1544                         if(predicate == subrelationOf) {
1545                                 SuperRelations superRelations = SuperRelations.entry(QueryProcessor.this, subject);
1546                                 if(superRelations != null) markForUpdate(graph, superRelations);
1547                         }
1548
1549                         DirectPredicates dp = QueryCache.entryDirectPredicates(QueryProcessor.this, subject);
1550                         if(dp != null) markForUpdate(graph, dp);
1551                         OrderedSet os = QueryCache.entryOrderedSet(QueryProcessor.this, predicate);
1552                         if(os != null) markForUpdate(graph, os);
1553
1554                         updateRefutations(graph);
1555                         
1556                         scheduledObjectUpdates.clear();
1557
1558                         if (Development.DEVELOPMENT) {
1559                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1560                                         System.err.println("== Query update ends ==");
1561                                 }
1562                         }
1563
1564                         return;
1565
1566                 }
1567
1568                 // Special case - one value
1569                 if(scheduledObjectUpdates.size() == 0 && scheduledValueUpdates.size() == 1 && scheduledPrimitiveUpdates.size() == 0 && scheduledInvalidates.size() == 0) {
1570
1571                         int arg0 = scheduledValueUpdates.getFirst();
1572
1573                         ValueQuery valueQuery = QueryCache.entryValueQuery(QueryProcessor.this, arg0);
1574                         if(valueQuery != null) markForUpdate(graph, valueQuery);
1575
1576                         updateRefutations(graph);
1577
1578                         scheduledValueUpdates.clear();
1579
1580                         if (Development.DEVELOPMENT) {
1581                                 if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1582                                         System.err.println("== Query update ends ==");
1583                                 }
1584                         }
1585                         
1586                         return;
1587
1588                 }
1589
1590                 final TIntHashSet predicates = new TIntHashSet();
1591                 final TIntHashSet orderedSets = new TIntHashSet();
1592
1593                 THashSet primitiveUpdates;
1594                 synchronized (primitiveUpdateLock) {
1595                         primitiveUpdates = scheduledPrimitiveUpdates;
1596                         scheduledPrimitiveUpdates = new THashSet();
1597                 }
1598
1599                 scheduledValueUpdates.forEach(new TIntProcedure() {
1600
1601                         @Override
1602                         public boolean execute(int arg0) {
1603                                 ValueQuery valueQuery = QueryCache.entryValueQuery(QueryProcessor.this, arg0);
1604                                 if(valueQuery != null) markForUpdate(graph, valueQuery);
1605                                 return true;
1606                         }
1607
1608                 });
1609
1610                 scheduledInvalidates.forEach(new TIntProcedure() {
1611
1612                         @Override
1613                         public boolean execute(int resource) {
1614                                 
1615                                 ValueQuery valueQuery = QueryCache.entryValueQuery(QueryProcessor.this, resource);
1616                                 if(valueQuery != null) markForUpdate(graph, valueQuery);
1617                                 
1618                                 PrincipalTypes principalTypes = QueryCache.entryPrincipalTypes(QueryProcessor.this, resource);
1619                                 if(principalTypes != null) markForUpdate(graph, principalTypes);
1620                                 Types types = QueryCache.entryTypes(QueryProcessor.this, resource);
1621                                 if(types != null) markForUpdate(graph, types);
1622
1623                                 SuperRelations superRelations = SuperRelations.entry(QueryProcessor.this, resource);
1624                                 if(superRelations != null) markForUpdate(graph, superRelations);
1625
1626                                 predicates.add(resource);
1627                                 
1628                                 return true;
1629                         }
1630
1631                 });
1632
1633                 scheduledObjectUpdates.forEach(new TLongProcedure() {
1634
1635                         @Override
1636                         public boolean execute(long arg0) {
1637
1638                                 final int subject = (int)(arg0 >>> 32);
1639                                 final int predicate = (int)(arg0 & 0xffffffff);
1640
1641                                 if(predicate == instanceOf || predicate == inherits || predicate == subrelationOf) {
1642                                         PrincipalTypes principalTypes = QueryCache.entryPrincipalTypes(QueryProcessor.this, subject);
1643                                         if(principalTypes != null) markForUpdate(graph, principalTypes);
1644                                         Types types = QueryCache.entryTypes(QueryProcessor.this, subject);
1645                                         if(types != null) markForUpdate(graph, types);
1646                                 }
1647
1648                                 if(predicate == subrelationOf) {
1649                                         SuperRelations superRelations = SuperRelations.entry(QueryProcessor.this, subject);
1650                                         if(superRelations != null) markForUpdate(graph, superRelations);
1651                                 }
1652
1653                                 predicates.add(subject);
1654                                 orderedSets.add(predicate);
1655
1656                                 return true;
1657
1658                         }
1659
1660                 });
1661
1662                 predicates.forEach(new TIntProcedure() {
1663
1664                         @Override
1665                         public boolean execute(final int subject) {
1666
1667                                 for(Objects o : QueryCache.entriesObjects(QueryProcessor.this, subject)) markForUpdate(graph, o);
1668                                 for(DirectObjects o : QueryCache.entriesDirectObjects(QueryProcessor.this, subject)) markForUpdate(graph, o);
1669                                 for(Statements o : QueryCache.entriesStatements(QueryProcessor.this, subject)) markForUpdate(graph, o);
1670
1671                                 DirectPredicates entry = QueryCache.entryDirectPredicates(QueryProcessor.this, subject);
1672                                 if(entry != null) markForUpdate(graph, entry);
1673
1674                                 return true;
1675
1676                         }
1677
1678                 });
1679
1680                 orderedSets.forEach(new TIntProcedure() {
1681
1682                         @Override
1683                         public boolean execute(int orderedSet) {
1684
1685                                 OrderedSet entry = QueryCache.entryOrderedSet(QueryProcessor.this, orderedSet);
1686                                 if(entry != null) markForUpdate(graph, entry);
1687
1688                                 return true;
1689
1690                         }
1691
1692                 });
1693
1694                 updateRefutations(graph);
1695
1696                 primitiveUpdates.forEach(new TObjectProcedure() {
1697
1698                         @Override
1699                         public boolean execute(Object arg0) {
1700
1701                                 ExternalReadEntry query = (ExternalReadEntry)cache.externalReadEntryMap.get(arg0);
1702                                 if (query != null) {
1703                                         boolean listening = update(graph, query);
1704                                         if (!listening && !query.hasParents()) {
1705                                                 cache.externalReadEntryMap.remove(arg0);
1706                                                 query.discard();
1707                                         }
1708                                 }
1709                                 return true;
1710                         }
1711
1712                 });
1713                 
1714                 scheduledValueUpdates.clear();
1715                 scheduledObjectUpdates.clear();
1716                 scheduledInvalidates.clear();
1717                 
1718                 if (Development.DEVELOPMENT) {
1719                         if(Development.<Boolean>getProperty(DevelopmentKeys.QUERYPROCESSOR_UPDATE, Bindings.BOOLEAN)) {
1720                                 System.err.println("== Query update ends ==");
1721                         }
1722                 }
1723
1724         }
1725
1726         public void updateValue(final int resource) {
1727                 scheduledValueUpdates.add(resource);
1728                 cache.dirty = true;
1729         }
1730
1731         public void updateStatements(final int resource, final int predicate) {
1732                 scheduledObjectUpdates.add((((long)resource) << 32) + predicate);
1733                 cache.dirty = true;
1734         }
1735         
1736         private int lastInvalidate = 0;
1737         
1738         public void invalidateResource(final int resource) {
1739                 if(lastInvalidate == resource) return;
1740                 scheduledValueUpdates.add(resource);
1741                 lastInvalidate = resource;
1742                 cache.dirty = true;
1743         }
1744
1745         public void updatePrimitive(final ExternalRead primitive) {
1746
1747                 // External reads may be updated from arbitrary threads.
1748                 // Synchronize to prevent race-conditions.
1749                 synchronized (primitiveUpdateLock) {
1750                         scheduledPrimitiveUpdates.add(primitive);
1751                 }
1752                 querySupport.dirtyPrimitives();
1753
1754         }
1755
1756         @Override
1757         public synchronized String toString() {
1758                 return "QueryProvider [size = " + cache.size + ", hits = " + cache.hits + " misses = " + cache.misses + ", updates = " + cache.updates + "]";
1759         }
1760
1761         @Override
1762         protected void doDispose() {
1763
1764                 requests.release(Integer.MAX_VALUE / 2);
1765                 
1766                 for(int index = 0; index < THREADS; index++) { 
1767                         executors[index].dispose();
1768                 }
1769
1770                 // First just wait
1771                 for(int i=0;i<100;i++) {
1772
1773                         boolean alive = false;
1774                         for(int index = 0; index < THREADS; index++) { 
1775                                 alive |= executors[index].isAlive();
1776                         }
1777                         if(!alive) return;
1778                         try {
1779                                 Thread.sleep(5);
1780                         } catch (InterruptedException e) {
1781                                 Logger.defaultLogError(e);
1782                         }
1783
1784                 }
1785
1786                 // Then start interrupting
1787                 for(int i=0;i<100;i++) {
1788
1789                         boolean alive = false;
1790                         for(int index = 0; index < THREADS; index++) { 
1791                                 alive |= executors[index].isAlive();
1792                         }
1793                         if(!alive) return;
1794                         for(int index = 0; index < THREADS; index++) {
1795                                 executors[index].interrupt();
1796                         }
1797                 }
1798
1799                 //              // Then just destroy
1800                 //              for(int index = 0; index < THREADS; index++) {
1801                 //                      executors[index].destroy();
1802                 //              }
1803
1804                 for(int index = 0; index < THREADS; index++) {
1805                         try {
1806                                 executors[index].join(5000);
1807                         } catch (InterruptedException e) {
1808                                 Logger.defaultLogError("QueryThread " + index + " will not die.", e);
1809                         }
1810                         executors[index] = null;
1811                 }
1812
1813         }
1814
1815         public int getHits() {
1816                 return cache.hits;
1817         }
1818
1819         public int getMisses() {
1820                 return cache.misses;
1821         }
1822
1823         public int getSize() {
1824                 return cache.size;
1825         }
1826
1827         public Set<Long> getReferencedClusters() {
1828                 HashSet<Long> result = new HashSet<Long>();
1829                 for (CacheEntry entry : QueryCache.entriesObjects(this)) {
1830                         Objects query = (Objects) entry.getQuery();
1831                         result.add(querySupport.getClusterId(query.r1()));
1832                 }
1833                 for (CacheEntry entry : QueryCache.entriesDirectPredicates(this)) {
1834                         DirectPredicates query = (DirectPredicates) entry.getQuery();
1835                         result.add(querySupport.getClusterId(query.id));
1836                 }
1837                 for (CacheEntry entry : cache.valueQueryMap.values()) {
1838                         ValueQuery query = (ValueQuery) entry.getQuery();
1839                         result.add(querySupport.getClusterId(query.id));
1840                 }
1841                 return result;
1842         }
1843         
1844         public long cluster(int resource) {
1845             if(resource <= 0)
1846                 return 0;
1847             return querySupport.getClusterId(resource);
1848         }
1849
1850         public void assertDone() {
1851         }
1852
1853         CacheCollectionResult allCaches(CacheCollectionResult result) {
1854                 
1855                 return cache.allCaches(result);
1856
1857         }
1858
1859         public void printDiagnostics() {
1860         }
1861
1862         public void requestCluster(ReadGraphImpl graph, long clusterId, Runnable runnable) {
1863                 querySupport.requestCluster(graph, clusterId, runnable);
1864         }
1865
1866         public int clean() {
1867                 collector.collect(0, Integer.MAX_VALUE);
1868                 return cache.size;
1869         }
1870
1871         public void clean(final Collection<ExternalRead<?>> requests) {
1872                 QueryCollectorSupport collectorSupport = new QueryCollectorSupport() {
1873                         Iterator<ExternalRead<?>> iterator = requests.iterator();
1874                         @Override
1875                         public CacheCollectionResult allCaches() {
1876                                 throw new UnsupportedOperationException();
1877                         }
1878                         @Override
1879                         public CacheEntryBase iterate(int level) {
1880                                 if(iterator.hasNext()) {
1881                                         ExternalRead<?> request = iterator.next();
1882                                         ExternalReadEntry entry = cache.externalReadEntryMap.get(request);
1883                                         if (entry != null) return entry;
1884                                         else return iterate(level);
1885                                 } else {
1886                                         iterator = requests.iterator();
1887                                         return null;
1888                                 }
1889                         }
1890                         @Override
1891                         public void remove() {
1892                                 throw new UnsupportedOperationException();
1893                         }
1894                         @Override
1895                         public void setLevel(CacheEntryBase entry, int level) {
1896                                 throw new UnsupportedOperationException();
1897                         }
1898                         @Override
1899                         public Collection<CacheEntry> getRootList() {
1900                                 ArrayList<CacheEntry> result = new ArrayList<CacheEntry>(requests.size());
1901                                 for (ExternalRead<?> request : requests) {
1902                                         ExternalReadEntry entry = cache.externalReadEntryMap.get(request);
1903                                         if (entry != null)
1904                                                 result.add(entry);
1905                                 }
1906                                 return result;
1907                         }
1908                         @Override
1909                         public int getCurrentSize() {
1910                                 return cache.size;
1911                         }
1912                         @Override
1913                         public int calculateCurrentSize() {
1914                                 // This tells the collector to attempt collecting everything.
1915                                 return Integer.MAX_VALUE;
1916                         }
1917                         @Override
1918                         public boolean start(boolean flush) {
1919                                 return true;
1920                         }
1921                 };
1922                 new QueryCollectorImpl2(this, collectorSupport).collect(0, Integer.MAX_VALUE);
1923         }
1924
1925         public void scanPending() {
1926                 
1927                 cache.scanPending();
1928
1929         }
1930
1931         public ReadGraphImpl graphForVirtualRequest() {
1932                 return ReadGraphImpl.createAsync(this);
1933         }
1934
1935         
1936         private HashMap<Resource, Class<?>> builtinValues;
1937         
1938         public Class<?> getBuiltinValue(Resource r) {
1939                 if(builtinValues == null) initBuiltinValues();
1940                 return builtinValues.get(r);
1941         }
1942
1943         Exception callerException = null;
1944
1945     public interface AsyncBarrier {
1946         public void inc(); 
1947         public void dec();
1948         public void waitBarrier(Object request, ReadGraphImpl impl);
1949         public boolean isBlocking();
1950     }
1951
1952 //      final public QueryProcessor processor;
1953 //      final public QuerySupport support;
1954
1955         //    boolean disposed = false;
1956
1957         private void initBuiltinValues() {
1958
1959                 Layer0 b = getSession().peekService(Layer0.class);
1960                 if(b == null) return;
1961
1962                 builtinValues = new HashMap<Resource, Class<?>>();
1963
1964                 builtinValues.put(b.String, String.class);
1965                 builtinValues.put(b.Double, Double.class);
1966                 builtinValues.put(b.Float, Float.class);
1967                 builtinValues.put(b.Long, Long.class);
1968                 builtinValues.put(b.Integer, Integer.class);
1969                 builtinValues.put(b.Byte, Byte.class);
1970                 builtinValues.put(b.Boolean, Boolean.class);
1971
1972                 builtinValues.put(b.StringArray, String[].class);
1973                 builtinValues.put(b.DoubleArray, double[].class);
1974                 builtinValues.put(b.FloatArray, float[].class);
1975                 builtinValues.put(b.LongArray, long[].class);
1976                 builtinValues.put(b.IntegerArray, int[].class);
1977                 builtinValues.put(b.ByteArray, byte[].class);
1978                 builtinValues.put(b.BooleanArray, boolean[].class);
1979
1980         }
1981
1982 //      public ReadGraphSupportImpl(final QueryProcessor provider2) {
1983 //
1984 //              if (null == provider2) {
1985 //                      this.processor = null;
1986 //                      support = null;
1987 //                      return;
1988 //              }
1989 //              this.processor = provider2;
1990 //              support = provider2.getCore();
1991 //              initBuiltinValues();
1992 //
1993 //      }
1994
1995 //      final static public ReadGraphSupportImpl basedOn(ReadGraphSupportImpl impl) {
1996 //              return new ReadGraphSupportImpl(impl.processor);
1997 //      }
1998
1999         @Override
2000         final public Session getSession() {
2001                 return session;
2002         }
2003         
2004         final public ResourceSupport getResourceSupport() {
2005                 return resourceSupport;
2006         }
2007
2008         @Override
2009         final public void forEachPredicate(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
2010
2011         try {
2012
2013                 for(Resource predicate : getPredicates(impl, subject))
2014                     procedure.execute(impl, predicate);
2015
2016                 procedure.finished(impl);
2017
2018             } catch (Throwable e) {
2019                 procedure.exception(impl, e);
2020             }
2021
2022         }
2023
2024         @Override
2025         final public void forEachPredicate(final ReadGraphImpl impl, final Resource subject, final MultiProcedure<Resource> procedure) {
2026                 
2027                 throw new UnsupportedOperationException();
2028
2029 //              assert(subject != null);
2030 //              assert(procedure != null);
2031 //
2032 //              final ListenerBase listener = getListenerBase(procedure);
2033 //
2034 //              try {
2035 //                      QueryCache.runnerPredicates(impl, querySupport.getId(subject), impl.parent, listener, new IntProcedure() {
2036 //
2037 //                              @Override
2038 //                              public void execute(ReadGraphImpl graph, int i) {
2039 //                                      try {
2040 //                                              procedure.execute(querySupport.getResource(i));
2041 //                                      } catch (Throwable t2) {
2042 //                                              Logger.defaultLogError(t2);
2043 //                                      }
2044 //                              }
2045 //
2046 //                              @Override
2047 //                              public void finished(ReadGraphImpl graph) {
2048 //                                      try {
2049 //                                              procedure.finished();
2050 //                                      } catch (Throwable t2) {
2051 //                                              Logger.defaultLogError(t2);
2052 //                                      }
2053 ////                            impl.state.barrier.dec();
2054 //                              }
2055 //
2056 //                              @Override
2057 //                              public void exception(ReadGraphImpl graph, Throwable t) {
2058 //                                      try {
2059 //                                              procedure.exception(t);
2060 //                                      } catch (Throwable t2) {
2061 //                                              Logger.defaultLogError(t2);
2062 //                                      }
2063 ////                            impl.state.barrier.dec();
2064 //                              }
2065 //
2066 //                      });
2067 //              } catch (DatabaseException e) {
2068 //                      Logger.defaultLogError(e);
2069 //              }
2070
2071         }
2072         
2073         @Override
2074         final public IntSet getPredicates(final ReadGraphImpl impl, final Resource subject) throws Throwable {
2075                 return QueryCacheBase.resultPredicates(impl, querySupport.getId(subject), impl.parent, null); 
2076         }
2077
2078         @Override
2079         final public void forEachStatement(final ReadGraphImpl impl, final Resource subject,
2080                         final Resource predicate, final MultiProcedure<Statement> procedure) {
2081
2082                 assert(subject != null);
2083                 assert(predicate != null);
2084                 assert(procedure != null);
2085
2086                 final ListenerBase listener = getListenerBase(procedure);
2087
2088 //              impl.state.barrier.inc();
2089
2090                 try {
2091                         Statements.queryEach(impl, querySupport.getId(subject), querySupport.getId(predicate), this, impl.parent, listener, new TripleIntProcedureAdapter() {
2092
2093                                 @Override
2094                                 public void execute(ReadGraphImpl graph, int s, int p, int o) {
2095                                         try {
2096                                                 procedure.execute(querySupport.getStatement(s, p, o));
2097                                         } catch (Throwable t2) {
2098                                                 Logger.defaultLogError(t2);
2099                                         }
2100                                 }
2101
2102                                 @Override
2103                                 public void finished(ReadGraphImpl graph) {
2104                                         try {
2105                                                 procedure.finished();
2106                                         } catch (Throwable t2) {
2107                                                 Logger.defaultLogError(t2);
2108                                         }
2109 //                              impl.state.barrier.dec();
2110                                 }
2111
2112                                 @Override
2113                                 public void exception(ReadGraphImpl graph, Throwable t) {
2114                                         try {
2115                                                 procedure.exception(t);
2116                                         } catch (Throwable t2) {
2117                                                 Logger.defaultLogError(t2);
2118                                         }
2119 //                              impl.state.barrier.dec();
2120                                 }
2121
2122                         });
2123                 } catch (DatabaseException e) {
2124                         Logger.defaultLogError(e);
2125                 }
2126
2127         }
2128
2129         @Override
2130         final public void forEachStatement(final ReadGraphImpl impl, final Resource subject,
2131                         final Resource predicate, final AsyncMultiProcedure<Statement> procedure) {
2132
2133                 assert(subject != null);
2134                 assert(predicate != null);
2135                 assert(procedure != null);
2136
2137                 final ListenerBase listener = getListenerBase(procedure);
2138
2139                 TripleIntProcedureAdapter proc = new TripleIntProcedureAdapter() {
2140
2141                         boolean first = true;
2142
2143                         @Override
2144                         public void execute(ReadGraphImpl graph, int s, int p, int o) {
2145                                 try {
2146                                         if(first) {
2147                                                 procedure.execute(graph, querySupport.getStatement(s, p, o));
2148                                         } else {
2149                                                 procedure.execute(impl.newRestart(graph), querySupport.getStatement(s, p, o));
2150                                         }
2151                                 } catch (Throwable t2) {
2152                                         Logger.defaultLogError(t2);
2153                                 }
2154                         }
2155
2156                         @Override
2157                         public void finished(ReadGraphImpl graph) {
2158
2159                                 try {
2160                                         if(first) {
2161                                                 first = false;
2162                                                 procedure.finished(graph);
2163 //                                              impl.state.barrier.dec(this);
2164                                         } else {
2165                                                 procedure.finished(impl.newRestart(graph));
2166                                         }
2167                                 } catch (Throwable t2) {
2168                                         Logger.defaultLogError(t2);
2169                                 }
2170
2171                         }
2172
2173                         @Override
2174                         public void exception(ReadGraphImpl graph, Throwable t) {
2175
2176                                 try {
2177                                         if(first) {
2178                                                 first = false;
2179                                                 procedure.exception(graph, t);
2180 //                                              impl.state.barrier.dec(this);
2181                                         } else {
2182                                                 procedure.exception(impl.newRestart(graph), t);
2183                                         }
2184                                 } catch (Throwable t2) {
2185                                         Logger.defaultLogError(t2);
2186                                 }
2187
2188                         }
2189
2190                 };
2191
2192                 int sId = querySupport.getId(subject);
2193                 int pId = querySupport.getId(predicate);
2194
2195 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(proc, "#Statements" + sId + "#" + pId);
2196 //              else impl.state.barrier.inc(null, null);
2197
2198                 try {
2199                         Statements.queryEach(impl, sId, pId, this, impl.parent, listener, proc);
2200                 } catch (DatabaseException e) {
2201                         Logger.defaultLogError(e);
2202                 }
2203
2204         }
2205
2206         @Override
2207         final public void forEachStatement(final ReadGraphImpl impl, final Resource subject,
2208                         final Resource predicate, final StatementProcedure procedure) {
2209
2210                 assert(subject != null);
2211                 assert(predicate != null);
2212                 assert(procedure != null);
2213
2214                 final ListenerBase listener = getListenerBase(procedure);
2215
2216                 TripleIntProcedureAdapter proc = new TripleIntProcedureAdapter() {
2217
2218                         boolean first = true;
2219
2220                         @Override
2221                         public void execute(ReadGraphImpl graph, int s, int p, int o) {
2222                                 try {
2223                                         if(first) {
2224                                                 procedure.execute(graph, s, p, o);
2225                                         } else {
2226                                                 procedure.execute(impl.newRestart(graph), s, p, o);
2227                                         }
2228                                 } catch (Throwable t2) {
2229                                         Logger.defaultLogError(t2);
2230                                 }
2231                         }
2232
2233                         @Override
2234                         public void finished(ReadGraphImpl graph) {
2235
2236                                 try {
2237                                         if(first) {
2238                                                 first = false;
2239                                                 procedure.finished(graph);
2240 //                                              impl.state.barrier.dec(this);
2241                                         } else {
2242                                                 procedure.finished(impl.newRestart(graph));
2243                                         }
2244                                 } catch (Throwable t2) {
2245                                         Logger.defaultLogError(t2);
2246                                 }
2247
2248                         }
2249
2250                         @Override
2251                         public void exception(ReadGraphImpl graph, Throwable t) {
2252
2253                                 try {
2254                                         if(first) {
2255                                                 first = false;
2256                                                 procedure.exception(graph, t);
2257 //                                              impl.state.barrier.dec(this);
2258                                         } else {
2259                                                 procedure.exception(impl.newRestart(graph), t);
2260                                         }
2261                                 } catch (Throwable t2) {
2262                                         Logger.defaultLogError(t2);
2263                                 }
2264
2265                         }
2266
2267                 };
2268
2269                 int sId = querySupport.getId(subject);
2270                 int pId = querySupport.getId(predicate);
2271
2272 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(proc, "#Statements" + sId + "#" + pId);
2273 //              else impl.state.barrier.inc(null, null);
2274
2275                 try {
2276                         Statements.queryEach(impl, sId, pId, this, impl.parent, listener, proc);
2277                 } catch (DatabaseException e) {
2278                         Logger.defaultLogError(e);
2279                 }
2280
2281         }
2282         
2283         @Override
2284         final public void forStatementSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Statement> procedure) {
2285
2286                 assert(subject != null);
2287                 assert(predicate != null);
2288                 assert(procedure != null);
2289
2290                 forEachStatement(impl, subject, predicate, new AsyncMultiListener<Statement>() {
2291
2292                         private Set<Statement> current = null;
2293                         private Set<Statement> run = new HashSet<Statement>();
2294
2295                         @Override
2296                         public void execute(AsyncReadGraph graph, Statement result) {
2297
2298                                 boolean found = false;
2299
2300                                 if(current != null) {
2301
2302                                         found = current.remove(result);
2303
2304                                 }
2305
2306                                 if(!found) procedure.add(graph, result);
2307
2308                                 run.add(result);
2309
2310                         }
2311
2312                         @Override
2313                         public void finished(AsyncReadGraph graph) {
2314
2315                                 if(current != null) { 
2316                                         for(Statement r : current) procedure.remove(graph, r);
2317                                 }
2318
2319                                 current = run;
2320
2321                                 run = new HashSet<Statement>();
2322
2323                         }
2324
2325                         @Override
2326                         public void exception(AsyncReadGraph graph, Throwable t) {
2327                                 procedure.exception(graph, t);
2328                         }
2329
2330                         @Override
2331                         public boolean isDisposed() {
2332                                 return procedure.isDisposed();
2333                         }
2334
2335                 });
2336
2337         }
2338
2339         @Override
2340         final public void forEachAssertedStatement(final ReadGraphImpl impl, final Resource subject,
2341                         final Resource predicate, final AsyncMultiProcedure<Statement> procedure) {
2342
2343                 assert(subject != null);
2344                 assert(predicate != null);
2345                 assert(procedure != null);
2346
2347                 final ListenerBase listener = getListenerBase(procedure);
2348
2349 //              impl.state.barrier.inc();
2350
2351                 try {
2352                         QueryCache.runnerAssertedStatements(impl, querySupport.getId(subject), querySupport.getId(predicate), impl.parent, listener, new TripleIntProcedureAdapter() {
2353
2354                                 @Override
2355                                 public void execute(ReadGraphImpl graph, int s, int p, int o) {
2356                                         try {
2357                                                 procedure.execute(graph, querySupport.getStatement(s, p, o));
2358                                         } catch (Throwable t2) {
2359                                                 Logger.defaultLogError(t2);
2360                                         }
2361                                 }
2362
2363                                 @Override
2364                                 public void finished(ReadGraphImpl graph) {
2365                                         try {
2366                                                 procedure.finished(graph);
2367                                         } catch (Throwable t2) {
2368                                                 Logger.defaultLogError(t2);
2369                                         }
2370 //                              impl.state.barrier.dec();
2371                                 }
2372
2373                                 @Override
2374                                 public void exception(ReadGraphImpl graph, Throwable t) {
2375                                         try {
2376                                                 procedure.exception(graph, t);
2377                                         } catch (Throwable t2) {
2378                                                 Logger.defaultLogError(t2);
2379                                         }
2380 //                              impl.state.barrier.dec();
2381                                 }
2382
2383                         });
2384                 } catch (DatabaseException e) {
2385                         Logger.defaultLogError(e);
2386                 }
2387
2388         }
2389
2390         private static ListenerBase getListenerBase(Object procedure) {
2391                 if(procedure instanceof ListenerBase) return (ListenerBase)procedure;
2392                 else return null;
2393         }
2394
2395         @Override
2396         final public void forEachObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final MultiProcedure<Resource> procedure) {
2397
2398                 assert(subject != null);
2399                 assert(predicate != null);
2400                 assert(procedure != null);
2401
2402                 final ListenerBase listener = getListenerBase(procedure);
2403
2404 //              impl.state.barrier.inc();
2405
2406                 try {
2407                         QueryCache.runnerObjects(impl, querySupport.getId(subject), querySupport.getId(predicate), impl.parent, listener, new IntProcedure() {
2408
2409                                 @Override
2410                                 public void execute(ReadGraphImpl graph, int i) {
2411                                         try {
2412                                                 procedure.execute(querySupport.getResource(i));
2413                                         } catch (Throwable t2) {
2414                                                 Logger.defaultLogError(t2);
2415                                         }
2416                                 }
2417
2418                                 @Override
2419                                 public void finished(ReadGraphImpl graph) {
2420                                         try {
2421                                                 procedure.finished();
2422                                         } catch (Throwable t2) {
2423                                                 Logger.defaultLogError(t2);
2424                                         }
2425 //                              impl.state.barrier.dec();
2426                                 }
2427
2428                                 @Override
2429                                 public void exception(ReadGraphImpl graph, Throwable t) {
2430                                         System.out.println("forEachObject exception " + t);
2431                                         try {
2432                                                 procedure.exception(t);
2433                                         } catch (Throwable t2) {
2434                                                 Logger.defaultLogError(t2);
2435                                         }
2436 //                              impl.state.barrier.dec();
2437                                 }
2438
2439                         });
2440                 } catch (DatabaseException e) {
2441                         Logger.defaultLogError(e);
2442                 }
2443
2444         }
2445
2446         @Override
2447         final public void forEachDirectPredicate(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
2448
2449                 assert(subject != null);
2450                 assert(procedure != null);
2451
2452                 final ListenerBase listener = getListenerBase(procedure);
2453
2454                 int sId = querySupport.getId(subject);
2455
2456                 try {
2457                         QueryCache.runnerDirectPredicates(impl, sId, impl.parent, listener, new InternalProcedure<IntSet>() {
2458
2459                                 @Override
2460                                 public void execute(ReadGraphImpl graph, IntSet result) throws DatabaseException {
2461                                         procedure.execute(graph, result);
2462                                 }
2463
2464                                 @Override
2465                                 public void exception(ReadGraphImpl graph, Throwable throwable) throws DatabaseException {
2466                                         procedure.exception(graph, throwable);
2467                                 }
2468                                 
2469                         });
2470                 } catch (DatabaseException e) {
2471                         Logger.defaultLogError(e);
2472                 }
2473
2474         }
2475
2476         final public DirectStatements getDirectStatements(final ReadGraphImpl impl, final Resource subject, final boolean ignoreVirtual) {
2477
2478 //              assert(subject != null);
2479 //              assert(procedure != null);
2480 //
2481 //              final ListenerBase listener = getListenerBase(procedure);
2482 //
2483 //              org.simantics.db.impl.query.DirectStatements.queryEach(impl, querySupport.getId(subject), this, impl.parent, listener, procedure);
2484                 
2485                 return querySupport.getStatements(impl, querySupport.getId(subject), this, ignoreVirtual);
2486
2487         }
2488
2489 //      @Override
2490 //      final public void forEachDirectStatement(final ReadGraphImpl impl, final Resource subject, final SyncProcedure<DirectStatements> procedure, boolean ignoreVirtual) {
2491 //
2492 //              assert(subject != null);
2493 //              assert(procedure != null);
2494 //
2495 //              final ListenerBase listener = getListenerBase(procedure);
2496 //
2497 //              org.simantics.db.impl.query.DirectStatements.queryEach(impl, querySupport.getId(subject), this, impl.parent, listener, procedure, ignoreVirtual);
2498 //
2499 //      }
2500         
2501         private static final Resource INVALID_RESOURCE = new ResourceImpl(null, Integer.MIN_VALUE);
2502
2503         @Override
2504         final public void forPossibleObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncProcedure<Resource> procedure) {
2505
2506                 forEachObject(impl, subject, predicate, new AsyncMultiProcedure<Resource>() {
2507
2508                         private Resource single = null;
2509
2510                         @Override
2511                         public synchronized void execute(AsyncReadGraph graph, Resource result) {
2512                                 if(single == null) {
2513                                         single = result;
2514                                 } else {
2515                                         single = INVALID_RESOURCE;
2516                                 }
2517                         }
2518
2519                         @Override
2520                         public synchronized void finished(AsyncReadGraph graph) {
2521                                 if(single == null || single == INVALID_RESOURCE) procedure.execute(graph, null);
2522                                 else procedure.execute(graph, single);
2523                         }
2524
2525                         @Override
2526                         public synchronized void exception(AsyncReadGraph graph, Throwable throwable) {
2527                                 procedure.exception(graph, throwable);
2528                         }
2529
2530                 });
2531
2532         }
2533
2534         final public void forEachObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final ListenerBase listener, final IntProcedure procedure) {
2535                 
2536                 final int sId = querySupport.getId(subject);
2537                 final int pId = querySupport.getId(predicate);
2538
2539                 try {
2540                         QueryCache.runnerObjects(impl, sId, pId, impl.parent, listener, procedure);
2541                 } catch (DatabaseException e) {
2542                         Logger.defaultLogError(e);
2543                 }
2544                 
2545         }
2546         
2547         static class Runner2Procedure implements IntProcedure {
2548             
2549             public int single = 0;
2550             public Throwable t = null;
2551
2552             public void clear() {
2553                 single = 0;
2554                 t = null;
2555             }
2556             
2557         @Override
2558         public void execute(ReadGraphImpl graph, int i) {
2559             if(single == 0) single = i;
2560             else single = -1;
2561         }
2562
2563         @Override
2564         public void finished(ReadGraphImpl graph) {
2565             if(single == -1) single = 0;
2566         }
2567
2568         @Override
2569         public void exception(ReadGraphImpl graph, Throwable throwable) {
2570             single = 0;
2571             this.t = throwable;
2572         }
2573         
2574         public int get() throws DatabaseException {
2575             if(t != null) {
2576                 if(t instanceof DatabaseException) throw (DatabaseException)t;
2577                 else throw new DatabaseException(t);
2578             }
2579             return single;
2580         }
2581             
2582         }
2583         
2584         final public int getSingleObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate) throws DatabaseException {
2585                 
2586                 final int sId = querySupport.getId(subject);
2587                 final int pId = querySupport.getId(predicate);
2588
2589                 Runner2Procedure proc = new Runner2Procedure();
2590                 QueryCache.runnerObjects(impl, sId, pId, impl.parent, null, proc);
2591                 return proc.get();
2592             
2593         }
2594
2595         final public void forEachObject(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncMultiProcedure<Resource> procedure) {
2596
2597                 assert(subject != null);
2598                 assert(predicate != null);
2599
2600                 final ListenerBase listener = getListenerBase(procedure);
2601
2602                 if(impl.parent != null || listener != null) {
2603
2604                         IntProcedure ip = new IntProcedure() {
2605
2606                                 AtomicBoolean first = new AtomicBoolean(true);
2607
2608                                 @Override
2609                                 public void execute(ReadGraphImpl graph, int i) {
2610                                         try {
2611                                                 if(first.get()) {
2612                                                         procedure.execute(impl, querySupport.getResource(i));
2613                                                 } else {
2614                                                         procedure.execute(impl.newRestart(graph), querySupport.getResource(i));
2615                                                 }
2616                                         } catch (Throwable t2) {
2617                                                 Logger.defaultLogError(t2);
2618                                         }
2619
2620                                 }
2621
2622                                 @Override
2623                                 public void finished(ReadGraphImpl graph) {
2624                                         try {
2625                                                 if(first.compareAndSet(true, false)) {
2626                                                         procedure.finished(impl);
2627 //                                                      impl.state.barrier.dec(this);
2628                                                 } else {
2629                                                         procedure.finished(impl.newRestart(graph));
2630                                                 }
2631                                         } catch (Throwable t2) {
2632                                                 Logger.defaultLogError(t2);
2633                                         }
2634                                 }
2635
2636                                 @Override
2637                                 public void exception(ReadGraphImpl graph, Throwable t) {
2638                                         try {
2639                                                 procedure.exception(graph, t);
2640                                         } catch (Throwable t2) {
2641                                                 Logger.defaultLogError(t2);
2642                                         }
2643 //                                      impl.state.barrier.dec(this);
2644                                 }
2645
2646                                 @Override
2647                                 public String toString() {
2648                                         return "forEachObject with " + procedure;
2649                                 }
2650
2651                         };
2652
2653 //                      if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#Objects" + subject + "#" + predicate);
2654 //                      else impl.state.barrier.inc(null, null);
2655
2656                         forEachObject(impl, subject, predicate, listener, ip);
2657
2658                 } else {
2659
2660                         IntProcedure ip = new IntProcedure() {
2661
2662                                 @Override
2663                                 public void execute(ReadGraphImpl graph, int i) {
2664                                         procedure.execute(graph, querySupport.getResource(i));
2665                                 }
2666
2667                                 @Override
2668                                 public void finished(ReadGraphImpl graph) {
2669                                         procedure.finished(graph);
2670                                 }
2671
2672                                 @Override
2673                                 public void exception(ReadGraphImpl graph, Throwable t) {
2674                                         procedure.exception(graph, t);
2675                                 }
2676
2677                                 @Override
2678                                 public String toString() {
2679                                         return "forEachObject with " + procedure;
2680                                 }
2681
2682                         };
2683
2684                         forEachObject(impl, subject, predicate, listener, ip);
2685
2686                 }
2687
2688         }
2689
2690         @Override
2691         final public void forObjectSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Resource> procedure) {
2692
2693                 assert(subject != null);
2694                 assert(predicate != null);
2695                 assert(procedure != null);
2696
2697                 forEachObject(impl, subject, predicate, new AsyncMultiListener<Resource>() {
2698
2699                         private Set<Resource> current = null;
2700                         private Set<Resource> run = new HashSet<Resource>();
2701
2702                         @Override
2703                         public void execute(AsyncReadGraph graph, Resource result) {
2704
2705                                 boolean found = false;
2706
2707                                 if(current != null) {
2708
2709                                         found = current.remove(result);
2710
2711                                 }
2712
2713                                 if(!found) procedure.add(graph, result);
2714
2715                                 run.add(result);
2716
2717                         }
2718
2719                         @Override
2720                         public void finished(AsyncReadGraph graph) {
2721
2722                                 if(current != null) { 
2723                                         for(Resource r : current) procedure.remove(graph, r);
2724                                 }
2725
2726                                 current = run;
2727
2728                                 run = new HashSet<Resource>();
2729
2730                         }
2731
2732                         @Override
2733                         public boolean isDisposed() {
2734                                 return procedure.isDisposed();
2735                         }
2736
2737                         @Override
2738                         public void exception(AsyncReadGraph graph, Throwable t) {
2739                                 procedure.exception(graph, t);
2740                         }
2741
2742                         @Override
2743                         public String toString() {
2744                                 return "forObjectSet " + procedure;
2745                         }
2746
2747                 });
2748
2749         }
2750
2751         @Override
2752         final public void forPredicateSet(final ReadGraphImpl impl, final Resource subject, final AsyncSetListener<Resource> procedure) {
2753
2754                 assert(subject != null);
2755                 assert(procedure != null);
2756
2757                 forEachPredicate(impl, subject, new AsyncMultiListener<Resource>() {
2758
2759                         private Set<Resource> current = null;
2760                         private Set<Resource> run = new HashSet<Resource>();
2761
2762                         @Override
2763                         public void execute(AsyncReadGraph graph, Resource result) {
2764
2765                                 boolean found = false;
2766
2767                                 if(current != null) {
2768
2769                                         found = current.remove(result);
2770
2771                                 }
2772
2773                                 if(!found) procedure.add(graph, result);
2774
2775                                 run.add(result);
2776
2777                         }
2778
2779                         @Override
2780                         public void finished(AsyncReadGraph graph) {
2781
2782                                 if(current != null) { 
2783                                         for(Resource r : current) procedure.remove(graph, r);
2784                                 }
2785
2786                                 current = run;
2787
2788                                 run = new HashSet<Resource>();
2789
2790                         }
2791
2792                         @Override
2793                         public boolean isDisposed() {
2794                                 return procedure.isDisposed();
2795                         }
2796
2797                         @Override
2798                         public void exception(AsyncReadGraph graph, Throwable t) {
2799                                 procedure.exception(graph, t);
2800                         }
2801
2802                         @Override
2803                         public String toString() {
2804                                 return "forPredicateSet " + procedure;
2805                         }
2806
2807                 });
2808
2809         }
2810
2811         @Override
2812         final public void forPrincipalTypeSet(final ReadGraphImpl impl, final Resource subject, final AsyncSetListener<Resource> procedure) {
2813
2814                 assert(subject != null);
2815                 assert(procedure != null);
2816
2817                 forEachPrincipalType(impl, subject, new AsyncMultiListener<Resource>() {
2818
2819                         private Set<Resource> current = null;
2820                         private Set<Resource> run = new HashSet<Resource>();
2821
2822                         @Override
2823                         public void execute(AsyncReadGraph graph, Resource result) {
2824
2825                                 boolean found = false;
2826
2827                                 if(current != null) {
2828
2829                                         found = current.remove(result);
2830
2831                                 }
2832
2833                                 if(!found) procedure.add(graph, result);
2834
2835                                 run.add(result);
2836
2837                         }
2838
2839                         @Override
2840                         public void finished(AsyncReadGraph graph) {
2841
2842                                 if(current != null) { 
2843                                         for(Resource r : current) procedure.remove(graph, r);
2844                                 }
2845
2846                                 current = run;
2847
2848                                 run = new HashSet<Resource>();
2849
2850                         }
2851
2852                         @Override
2853                         public boolean isDisposed() {
2854                                 return procedure.isDisposed();
2855                         }
2856
2857                         @Override
2858                         public void exception(AsyncReadGraph graph, Throwable t) {
2859                                 procedure.exception(graph, t);
2860                         }
2861
2862                         @Override
2863                         public String toString() {
2864                                 return "forPrincipalTypeSet " + procedure;
2865                         }
2866
2867                 });
2868
2869         }
2870
2871         @Override
2872         final public void forAssertedObjectSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Resource> procedure) {
2873
2874                 assert(subject != null);
2875                 assert(predicate != null);
2876                 assert(procedure != null);
2877
2878                 forEachAssertedObject(impl, subject, predicate, new AsyncMultiListener<Resource>() {
2879
2880                         private Set<Resource> current = null;
2881                         private Set<Resource> run = new HashSet<Resource>();
2882
2883                         @Override
2884                         public void execute(AsyncReadGraph graph, Resource result) {
2885
2886                                 boolean found = false;
2887
2888                                 if(current != null) {
2889
2890                                         found = current.remove(result);
2891
2892                                 }
2893
2894                                 if(!found) procedure.add(graph, result);
2895
2896                                 run.add(result);
2897
2898                         }
2899
2900                         @Override
2901                         public void finished(AsyncReadGraph graph) {
2902
2903                                 if(current != null) { 
2904                                         for(Resource r : current) procedure.remove(graph, r);
2905                                 }
2906
2907                                 current = run;
2908
2909                                 run = new HashSet<Resource>();
2910
2911                         }
2912
2913                         @Override
2914                         public boolean isDisposed() {
2915                                 return procedure.isDisposed();
2916                         }
2917
2918                         @Override
2919                         public void exception(AsyncReadGraph graph, Throwable t) {
2920                                 procedure.exception(graph, t);
2921                         }
2922
2923                         @Override
2924                         public String toString() {
2925                                 return "forObjectSet " + procedure;
2926                         }
2927
2928                 });
2929
2930         }
2931
2932         @Override
2933         final public void forAssertedStatementSet(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncSetListener<Statement> procedure) {
2934
2935                 assert(subject != null);
2936                 assert(predicate != null);
2937                 assert(procedure != null);
2938
2939                 forEachAssertedStatement(impl, subject, predicate, new AsyncMultiListener<Statement>() {
2940
2941                         private Set<Statement> current = null;
2942                         private Set<Statement> run = new HashSet<Statement>();
2943
2944                         @Override
2945                         public void execute(AsyncReadGraph graph, Statement result) {
2946
2947                                 boolean found = false;
2948
2949                                 if(current != null) {
2950
2951                                         found = current.remove(result);
2952
2953                                 }
2954
2955                                 if(!found) procedure.add(graph, result);
2956
2957                                 run.add(result);
2958
2959                         }
2960
2961                         @Override
2962                         public void finished(AsyncReadGraph graph) {
2963
2964                                 if(current != null) { 
2965                                         for(Statement s : current) procedure.remove(graph, s);
2966                                 }
2967
2968                                 current = run;
2969
2970                                 run = new HashSet<Statement>();
2971
2972                         }
2973
2974                         @Override
2975                         public boolean isDisposed() {
2976                                 return procedure.isDisposed();
2977                         }
2978
2979                         @Override
2980                         public void exception(AsyncReadGraph graph, Throwable t) {
2981                                 procedure.exception(graph, t);
2982                         }
2983
2984                         @Override
2985                         public String toString() {
2986                                 return "forStatementSet " + procedure;
2987                         }
2988
2989                 });
2990
2991         }
2992
2993         @Override
2994         final public void forEachAssertedObject(final ReadGraphImpl impl, final Resource subject,
2995                         final Resource predicate, final AsyncMultiProcedure<Resource> procedure) {
2996
2997                 assert(subject != null);
2998                 assert(predicate != null);
2999                 assert(procedure != null);
3000
3001                 final ListenerBase listener = getListenerBase(procedure);
3002
3003                 try {
3004                         QueryCache.runnerAssertedStatements(impl, querySupport.getId(subject), querySupport.getId(predicate), impl.parent, listener, new TripleIntProcedure() {
3005
3006                                 @Override
3007                                 public void execute(ReadGraphImpl graph, int s, int p, int o) {
3008                                         try {
3009                                                 procedure.execute(graph, querySupport.getResource(o));
3010                                         } catch (Throwable t2) {
3011                                                 Logger.defaultLogError(t2);
3012                                         }
3013                                 }
3014
3015                                 @Override
3016                                 public void finished(ReadGraphImpl graph) {
3017                                         try {               
3018                                                 procedure.finished(graph);
3019                                         } catch (Throwable t2) {
3020                                                 Logger.defaultLogError(t2);
3021                                         }
3022 //                              impl.state.barrier.dec();
3023                                 }
3024
3025                                 @Override
3026                                 public void exception(ReadGraphImpl graph, Throwable t) {
3027                                         try {
3028                                                 procedure.exception(graph, t);
3029                                         } catch (Throwable t2) {
3030                                                 Logger.defaultLogError(t2);
3031                                         }
3032 //                              impl.state.barrier.dec();
3033                                 }
3034
3035                         });
3036                 } catch (DatabaseException e) {
3037                         Logger.defaultLogError(e);
3038                 }
3039
3040         }
3041
3042         @Override
3043         final public void forEachPrincipalType(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
3044
3045                 assert(subject != null);
3046                 assert(procedure != null);
3047
3048                 final ListenerBase listener = getListenerBase(procedure);
3049
3050                 IntProcedure ip = new IntProcedure() {
3051
3052                         @Override
3053                         public void execute(ReadGraphImpl graph, int i) {
3054                                 try {
3055                                         procedure.execute(graph, querySupport.getResource(i));
3056                                 } catch (Throwable t2) {
3057                                         Logger.defaultLogError(t2);
3058                                 }
3059                         }
3060
3061                         @Override
3062                         public void finished(ReadGraphImpl graph) {
3063                                 try {
3064                                         procedure.finished(graph);
3065                                 } catch (Throwable t2) {
3066                                         Logger.defaultLogError(t2);
3067                                 }
3068 //                              impl.state.barrier.dec(this);
3069                         }
3070
3071                         @Override
3072                         public void exception(ReadGraphImpl graph, Throwable t) {
3073                                 try {
3074                                         procedure.exception(graph, t);
3075                                 } catch (Throwable t2) {
3076                                         Logger.defaultLogError(t2);
3077                                 }
3078 //                              impl.state.barrier.dec(this);
3079                         }
3080
3081                 };
3082
3083                 int sId = querySupport.getId(subject);
3084
3085 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#PrincipalTypes#" + sId);
3086 //              else impl.state.barrier.inc(null, null);
3087
3088                 try {
3089                         QueryCache.runnerPrincipalTypes(impl, sId, impl.parent, listener, ip);
3090                 } catch (DatabaseException e) {
3091                         Logger.defaultLogError(e);
3092                 }
3093
3094         }
3095
3096         @Override
3097         final public void forEachPrincipalType(final ReadGraphImpl impl, final Resource subject, final MultiProcedure<Resource> procedure) {
3098
3099                 assert(subject != null);
3100                 assert(procedure != null);
3101
3102                 final ListenerBase listener = getListenerBase(procedure);
3103
3104 //              impl.state.barrier.inc();
3105
3106                 try {
3107                         QueryCache.runnerPrincipalTypes(impl, querySupport.getId(subject), impl.parent, listener, new IntProcedure() {
3108
3109                                 @Override
3110                                 public void execute(ReadGraphImpl graph, int i) {
3111                                         try {
3112                                                 procedure.execute(querySupport.getResource(i));
3113                                         } catch (Throwable t2) {
3114                                                 Logger.defaultLogError(t2);
3115                                         }
3116                                 }
3117
3118                                 @Override
3119                                 public void finished(ReadGraphImpl graph) {
3120                                         try {
3121                                                 procedure.finished();
3122                                         } catch (Throwable t2) {
3123                                                 Logger.defaultLogError(t2);
3124                                         }
3125 //                              impl.state.barrier.dec();
3126                                 }
3127
3128                                 @Override
3129                                 public void exception(ReadGraphImpl graph, Throwable t) {
3130                                         try {
3131                                                 procedure.exception(t);
3132                                         } catch (Throwable t2) {
3133                                                 Logger.defaultLogError(t2);
3134                                         }
3135 //                              impl.state.barrier.dec();
3136                                 }
3137
3138                         });
3139                 } catch (DatabaseException e) {
3140                         Logger.defaultLogError(e);
3141                 }
3142         }
3143
3144     final public void forTypes(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
3145
3146         assert(subject != null);
3147         assert(procedure != null);
3148
3149         final ListenerBase listener = getListenerBase(procedure);
3150         assert(listener == null);
3151
3152         InternalProcedure<IntSet> ip = new InternalProcedure<IntSet>() {
3153
3154             @Override
3155             public void execute(final ReadGraphImpl graph, IntSet set) {
3156                 procedure.execute(graph, set);
3157             }
3158
3159             @Override
3160             public void exception(ReadGraphImpl graph, Throwable t) {
3161                 procedure.exception(graph, t);
3162             }
3163
3164         };
3165
3166         int sId = querySupport.getId(subject);
3167
3168         try {
3169                         QueryCache.runnerTypes(impl, sId, impl.parent, listener, ip);
3170                 } catch (DatabaseException e) {
3171                         Logger.defaultLogError(e);
3172                 }
3173
3174     }
3175     
3176         @Override
3177         final public IntSet getTypes(final ReadGraphImpl impl, final Resource subject) throws Throwable {
3178
3179                 assert(subject != null);
3180                 
3181                 return QueryCacheBase.resultTypes(impl, querySupport.getId(subject), impl.parent, null);
3182
3183         }
3184
3185         @Override
3186         final public RelationInfo getRelationInfo(final ReadGraphImpl impl, final Resource subject) throws DatabaseException {
3187                 
3188                 assert(subject != null);
3189
3190                 return QueryCache.resultRelationInfoQuery(impl, querySupport.getId(subject), impl.parent, null);
3191
3192         }
3193
3194         @Override
3195         final public void forSupertypes(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
3196
3197                 assert(subject != null);
3198                 assert(procedure != null);
3199
3200                 final ListenerBase listener = getListenerBase(procedure);
3201
3202                 try {
3203                         QueryCache.runnerSuperTypes(impl, querySupport.getId(subject), impl.parent, listener, new InternalProcedure<IntSet>() {
3204
3205                                 AtomicBoolean first = new AtomicBoolean(true);
3206
3207                                 @Override
3208                                 public void execute(final ReadGraphImpl graph, IntSet set) {
3209 //                              final HashSet<Resource> result = new HashSet<Resource>();
3210 //                              set.forEach(new TIntProcedure() {
3211 //
3212 //                                      @Override
3213 //                                      public boolean execute(int type) {
3214 //                                              result.add(querySupport.getResource(type));
3215 //                                              return true;
3216 //                                      }
3217 //
3218 //                              });
3219                                         try {
3220                                                 if(first.compareAndSet(true, false)) {
3221                                                         procedure.execute(graph, set);
3222 //                                              impl.state.barrier.dec();
3223                                                 } else {
3224                                                         procedure.execute(impl.newRestart(graph), set);
3225                                                 }
3226                                         } catch (Throwable t2) {
3227                                                 Logger.defaultLogError(t2);
3228                                         }
3229                                 }
3230
3231                                 @Override
3232                                 public void exception(ReadGraphImpl graph, Throwable t) {
3233                                         try {
3234                                                 if(first.compareAndSet(true, false)) {
3235                                                         procedure.exception(graph, t);
3236 //                                              impl.state.barrier.dec();
3237                                                 } else {
3238                                                         procedure.exception(impl.newRestart(graph), t);
3239                                                 }
3240                                         } catch (Throwable t2) {
3241                                                 Logger.defaultLogError(t2);
3242                                         }
3243                                 }
3244
3245                         });
3246                 } catch (DatabaseException e) {
3247                         Logger.defaultLogError(e);
3248                 }
3249
3250         }
3251
3252         @Override
3253         final public void forDirectSuperrelations(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
3254
3255                 assert(subject != null);
3256                 assert(procedure != null);
3257
3258                 final ListenerBase listener = getListenerBase(procedure);
3259
3260                 IntProcedure ip = new IntProcedureAdapter() {
3261
3262                         @Override
3263                         public void execute(final ReadGraphImpl graph, int superRelation) {
3264                                 try {
3265                                         procedure.execute(graph, querySupport.getResource(superRelation));
3266                                 } catch (Throwable t2) {
3267                                         Logger.defaultLogError(t2);
3268                                 }
3269                         }
3270
3271                         @Override
3272                         public void finished(final ReadGraphImpl graph) {
3273                                 try {
3274                                         procedure.finished(graph);
3275                                 } catch (Throwable t2) {
3276                                         Logger.defaultLogError(t2);
3277                                 }
3278 //                              impl.state.barrier.dec(this);
3279                         }
3280
3281
3282                         @Override
3283                         public void exception(ReadGraphImpl graph, Throwable t) {
3284                                 try {
3285                                         procedure.exception(graph, t);
3286                                 } catch (Throwable t2) {
3287                                         Logger.defaultLogError(t2);
3288                                 }
3289 //                              impl.state.barrier.dec(this);
3290                         }
3291
3292                 };
3293
3294                 int sId = querySupport.getId(subject); 
3295
3296 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#DirectSuperRelations#" + sId);
3297 //              else impl.state.barrier.inc(null, null);
3298
3299                 try {
3300                         QueryCache.runnerDirectSuperRelations(impl, sId, impl.parent, listener, ip);
3301                 } catch (DatabaseException e) {
3302                         Logger.defaultLogError(e);
3303                 }
3304                 
3305 //              DirectSuperRelations.queryEach(impl, sId, this, impl.parent, listener, ip);
3306
3307         }
3308
3309         @Override
3310         final public void forPossibleSuperrelation(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Resource> procedure) {
3311
3312                 assert(subject != null);
3313                 assert(procedure != null);
3314
3315                 final ListenerBase listener = getListenerBase(procedure);
3316
3317 //              impl.state.barrier.inc();
3318
3319                 PossibleSuperRelation.queryEach(impl, querySupport.getId(subject), this, impl.parent, listener, procedure);
3320
3321         }
3322
3323         @Override
3324         final public void forSuperrelations(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Set<Resource>> procedure) {
3325
3326                 assert(subject != null);
3327                 assert(procedure != null);
3328
3329                 final ListenerBase listener = getListenerBase(procedure);
3330
3331                 InternalProcedure<IntSet> ip = new InternalProcedure<IntSet>() {
3332
3333                         @Override
3334                         public void execute(final ReadGraphImpl graph, IntSet set) {
3335 //                              final HashSet<Resource> result = new HashSet<Resource>();
3336 //                              set.forEach(new TIntProcedure() {
3337 //
3338 //                                      @Override
3339 //                                      public boolean execute(int type) {
3340 //                                              result.add(querySupport.getResource(type));
3341 //                                              return true;
3342 //                                      }
3343 //
3344 //                              });
3345                                 try {
3346                                         procedure.execute(graph, set);
3347                                 } catch (Throwable t2) {
3348                                         Logger.defaultLogError(t2);
3349                                 }
3350 //                              impl.state.barrier.dec(this);
3351                         }
3352
3353                         @Override
3354                         public void exception(ReadGraphImpl graph, Throwable t) {
3355                                 try {
3356                                         procedure.exception(graph, t);
3357                                 } catch (Throwable t2) {
3358                                         Logger.defaultLogError(t2);
3359                                 }
3360 //                              impl.state.barrier.dec(this);
3361                         }
3362
3363                 };
3364
3365                 int sId = querySupport.getId(subject);
3366
3367 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#SuperRelations#" + sId);
3368 //              else impl.state.barrier.inc(null, null);
3369
3370                 try {
3371                         QueryCache.runnerSuperRelations(impl, sId, impl.parent, listener, ip);
3372                 } catch (DatabaseException e) {
3373                         Logger.defaultLogError(e);
3374                 }
3375
3376         }
3377
3378         final public byte[] getValue(final ReadGraphImpl impl, final Resource subject) throws DatabaseException {
3379           return getValue(impl, querySupport.getId(subject));
3380         }
3381
3382         final public byte[] getValue(final ReadGraphImpl impl, final int subject) throws DatabaseException {
3383                 return QueryCache.resultValueQuery(impl, subject, impl.parent, null); 
3384         }
3385
3386         @Override
3387         final public void forValue(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<byte[]> procedure) {
3388
3389                 assert(subject != null);
3390                 assert(procedure != null);
3391
3392                 int sId = querySupport.getId(subject);
3393
3394 //              if(procedure != null) {
3395                 
3396                         final ListenerBase listener = getListenerBase(procedure);
3397
3398                         InternalProcedure<byte[]> ip = new InternalProcedure<byte[]>() {
3399
3400                                 AtomicBoolean first = new AtomicBoolean(true);
3401
3402                                 @Override
3403                                 public void execute(ReadGraphImpl graph, byte[] result) {
3404                                         try {
3405                                                 if(first.compareAndSet(true, false)) {
3406                                                         procedure.execute(graph, result);
3407 //                                                      impl.state.barrier.dec(this);
3408                                                 } else {
3409                                                         procedure.execute(impl.newRestart(graph), result);
3410                                                 }
3411                                         } catch (Throwable t2) {
3412                                                 Logger.defaultLogError(t2);
3413                                         }
3414                                 }
3415
3416                                 @Override
3417                                 public void exception(ReadGraphImpl graph, Throwable t) {
3418                                         try {
3419                                                 if(first.compareAndSet(true, false)) {
3420                                                         procedure.exception(graph, t);
3421 //                                                      impl.state.barrier.dec(this);
3422                                                 } else {
3423                                                         procedure.exception(impl.newRestart(graph), t);
3424                                                 }
3425                                         } catch (Throwable t2) {
3426                                                 Logger.defaultLogError(t2);
3427                                         }
3428                                 }
3429
3430                         };
3431
3432 //                      if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#Value" + sId);
3433 //                      else impl.state.barrier.inc(null, null);
3434
3435                         try {
3436                                 QueryCache.runnerValueQuery(impl, sId, impl.parent, listener, ip);
3437                         } catch (DatabaseException e) {
3438                                 throw new IllegalStateException("Internal error");
3439                         }
3440
3441 //              } else {
3442 //
3443 //                      return QueryCacheBase.runnerValueQuery(impl, sId, impl.parent, null, null);
3444 //                      
3445 //              }
3446 //              
3447 //              throw new IllegalStateException("Internal error");
3448
3449         }
3450
3451         @Override
3452         final public void forPossibleValue(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<byte[]> procedure) {
3453
3454                 assert(subject != null);
3455                 assert(procedure != null);
3456
3457                 final ListenerBase listener = getListenerBase(procedure);
3458
3459                 if(impl.parent != null || listener != null) {
3460
3461                         InternalProcedure<byte[]> ip = new InternalProcedure<byte[]>() {
3462
3463                                 AtomicBoolean first = new AtomicBoolean(true);
3464
3465                                 @Override
3466                                 public void execute(ReadGraphImpl graph, byte[] result) {
3467                                         try {
3468                                                 if(first.compareAndSet(true, false)) {
3469                                                         procedure.execute(graph, result);
3470 //                                                      impl.state.barrier.dec(this);
3471                                                 } else {
3472                                                         procedure.execute(impl.newRestart(graph), result);
3473                                                 }
3474                                         } catch (Throwable t2) {
3475                                                 Logger.defaultLogError(t2);
3476                                         }
3477                                 }
3478
3479                                 @Override
3480                                 public void exception(ReadGraphImpl graph, Throwable t) {
3481                                         try {
3482                                                 if(first.compareAndSet(true, false)) {
3483                                                         procedure.exception(graph, t);
3484 //                                                      impl.state.barrier.dec(this);
3485                                                 } else {
3486                                                         procedure.exception(impl.newRestart(graph), t);
3487                                                 }
3488                                         } catch (Throwable t2) {
3489                                                 Logger.defaultLogError(t2);
3490                                         }
3491                                 }
3492
3493                         };
3494
3495                         int sId = querySupport.getId(subject);
3496
3497 //                      if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#Value" + sId);
3498 //                      else impl.state.barrier.inc(null, null);
3499
3500                         try {
3501                                 QueryCache.runnerValueQuery(impl, sId, impl.parent, listener, ip);
3502                         } catch (DatabaseException e) {
3503                                 Logger.defaultLogError(e);
3504                         }
3505
3506                 } else {
3507
3508                         InternalProcedure<byte[]> ip = new InternalProcedure<byte[]>() {
3509
3510                                 @Override
3511                                 public void execute(ReadGraphImpl graph, byte[] result) {
3512
3513                                         procedure.execute(graph, result);
3514
3515                                 }
3516
3517                                 @Override
3518                                 public void exception(ReadGraphImpl graph, Throwable t) {
3519
3520                                         procedure.exception(graph, t);
3521
3522                                 }
3523
3524                         };
3525
3526                         int sId = querySupport.getId(subject);
3527
3528                         try {
3529                                 QueryCache.runnerValueQuery(impl, sId, impl.parent, listener, ip);
3530                         } catch (DatabaseException e) {
3531                                 Logger.defaultLogError(e);
3532                         }
3533
3534                 }
3535
3536         }
3537
3538         @Override
3539         final public void forInverse(final ReadGraphImpl impl, final Resource relation, final AsyncProcedure<Resource> procedure) {
3540
3541                 assert(relation != null);
3542                 assert(procedure != null);
3543
3544                 final ListenerBase listener = getListenerBase(procedure);
3545
3546                 IntProcedure ip = new IntProcedure() {
3547
3548                         private int result = 0;
3549                         
3550                     final AtomicBoolean found = new AtomicBoolean(false);
3551                     final AtomicBoolean done = new AtomicBoolean(false);
3552
3553                         @Override
3554                         public void finished(ReadGraphImpl graph) {
3555                                 
3556                         // Shall fire exactly once!
3557                         if(done.compareAndSet(false, true)) {
3558                                 try {
3559                                         if(result == 0) {
3560                                                         procedure.exception(graph, new NoInverseException(""));
3561 //                                              impl.state.barrier.dec(this);
3562                                         } else {
3563                                                 procedure.execute(graph, querySupport.getResource(result));
3564 //                                              impl.state.barrier.dec(this);
3565                                         }
3566                                 } catch (Throwable t) {
3567                                         Logger.defaultLogError(t);
3568                                 }
3569                         }
3570                         
3571                         }
3572
3573                         @Override
3574                         public void execute(ReadGraphImpl graph, int i) {
3575                                 
3576                                 if(found.compareAndSet(false, true)) {
3577                                         this.result = i;
3578                                 } else {
3579                                         // Shall fire exactly once!
3580                                         if(done.compareAndSet(false, true)) {
3581                                                 try {
3582                                                         procedure.exception(graph, new ManyObjectsForFunctionalRelationException("Multiple items e.g. " + this.result + " and " + result));
3583 //                                                      impl.state.barrier.dec(this);
3584                                                 } catch (Throwable t) {
3585                                                 Logger.defaultLogError(t);
3586                                                 }
3587                                         }
3588                                 }
3589                                 
3590                         }
3591
3592                         @Override
3593                         public void exception(ReadGraphImpl graph, Throwable t) {
3594                                 // Shall fire exactly once!
3595                                 if(done.compareAndSet(false, true)) {
3596                                         try {
3597                                                 procedure.exception(graph, t);
3598 //                                              impl.state.barrier.dec(this);
3599                                         } catch (Throwable t2) {
3600                                         Logger.defaultLogError(t2);
3601                                         }
3602                                 }
3603                         }
3604
3605                 };
3606
3607                 int sId = querySupport.getId(relation);
3608
3609 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#DirectObjects#" + sId);
3610 //              else impl.state.barrier.inc(null, null);
3611
3612                 try {
3613                         QueryCache.runnerObjects(impl, sId, getInverseOf(), impl.parent, listener, ip);
3614                 } catch (DatabaseException e) {
3615                         Logger.defaultLogError(e);
3616                 }
3617
3618         }
3619
3620         @Override
3621         final public void forResource(final ReadGraphImpl impl, final String id, final AsyncProcedure<Resource> procedure) {
3622
3623                 assert(id != null);
3624                 assert(procedure != null);
3625
3626                 InternalProcedure<Integer> ip = new InternalProcedure<Integer>() {
3627
3628                         @Override
3629                         public void execute(ReadGraphImpl graph, Integer result) {
3630                                 try {
3631                                         procedure.execute(graph, querySupport.getResource(result));
3632                                 } catch (Throwable t2) {
3633                                         Logger.defaultLogError(t2);
3634                                 }
3635 //                              impl.state.barrier.dec(this);
3636                         }   
3637
3638                         @Override
3639                         public void exception(ReadGraphImpl graph, Throwable t) {
3640
3641                                 try {
3642                                         procedure.exception(graph, t);
3643                                 } catch (Throwable t2) {
3644                                         Logger.defaultLogError(t2);
3645                                 }
3646 //                              impl.state.barrier.dec(this);
3647                         }
3648
3649                 };
3650
3651 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "Resource");
3652 //              else impl.state.barrier.inc(null, null);
3653
3654                 forResource(impl, id, impl.parent, ip);
3655
3656         }
3657
3658         @Override
3659         final public void forBuiltin(final ReadGraphImpl impl, final String id, final AsyncProcedure<Resource> procedure) {
3660
3661                 assert(id != null);
3662                 assert(procedure != null);
3663
3664 //              impl.state.barrier.inc();
3665
3666                 try {
3667                         forBuiltin(impl, id, impl.parent, new InternalProcedure<Integer>() {
3668
3669                                 @Override
3670                                 public void execute(ReadGraphImpl graph, Integer result) {
3671                                         try {
3672                                                 procedure.execute(graph, querySupport.getResource(result)); 
3673                                         } catch (Throwable t2) {
3674                                                 Logger.defaultLogError(t2);
3675                                         }
3676 //                              impl.state.barrier.dec();
3677                                 }   
3678
3679                                 @Override
3680                                 public void exception(ReadGraphImpl graph, Throwable t) {
3681                                         try {
3682                                                 procedure.exception(graph, t);
3683                                         } catch (Throwable t2) {
3684                                                 Logger.defaultLogError(t2);
3685                                         }
3686 //                              impl.state.barrier.dec();
3687                                 }
3688
3689                         });
3690                 } catch (DatabaseException e) {
3691                         Logger.defaultLogError(e);
3692                 }
3693
3694         }
3695
3696         @Override
3697         final public void forHasStatement(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Boolean> procedure) {
3698
3699                 assert(subject != null);
3700                 assert(procedure != null);
3701
3702                 final ListenerBase listener = getListenerBase(procedure);
3703
3704                 try {
3705                         IntSet result = QueryCache.resultDirectPredicates(impl, querySupport.getId(subject), impl.parent, listener);
3706                         procedure.execute(impl, !result.isEmpty());
3707                 } catch (DatabaseException e) {
3708                         procedure.exception(impl, e);
3709                 }
3710                         
3711         }
3712
3713         @Override
3714         final public void forHasStatement(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final AsyncProcedure<Boolean> procedure) {
3715
3716                 assert(subject != null);
3717                 assert(predicate != null);
3718                 assert(procedure != null);
3719
3720                 AsyncMultiProcedure<Resource> ip = new AsyncMultiProcedureAdapter<Resource>() {
3721
3722                         boolean found = false;
3723
3724                         @Override
3725                         synchronized public void execute(AsyncReadGraph graph, Resource resource) {
3726                                 found = true;
3727                         }
3728
3729                         @Override
3730                         synchronized public void finished(AsyncReadGraph graph) {
3731                                 try {
3732                                         procedure.execute(graph, found);
3733                                 } catch (Throwable t2) {
3734                                         Logger.defaultLogError(t2);
3735                                 }
3736 //                              impl.state.barrier.dec(this);
3737                         }
3738
3739                         @Override
3740                         public void exception(AsyncReadGraph graph, Throwable t) {
3741                                 try {
3742                                         procedure.exception(graph, t);
3743                                 } catch (Throwable t2) {
3744                                         Logger.defaultLogError(t2);
3745                                 }
3746 //                              impl.state.barrier.dec(this);
3747                         }
3748
3749                 };
3750
3751 //              if(AsyncBarrierImpl.BOOKKEEPING) impl.state.barrier.inc(ip, "#ForEachObject#" + subject + "#" + predicate);
3752 //              else impl.state.barrier.inc(null, null);
3753
3754                 forEachObject(impl, subject, predicate, ip);
3755
3756         }
3757
3758         @Override
3759         final public void forHasStatement(final ReadGraphImpl impl, final Resource subject, final Resource predicate, final Resource object, final AsyncProcedure<Boolean> procedure) {
3760
3761                 assert(subject != null);
3762                 assert(predicate != null);
3763                 assert(procedure != null);
3764
3765 //              impl.state.barrier.inc();
3766
3767                 forEachObject(impl, subject, predicate, new AsyncMultiProcedureAdapter<Resource>() {
3768
3769                         boolean found = false;
3770
3771                         @Override
3772                         synchronized public void execute(AsyncReadGraph graph, Resource resource) {
3773                                 if(resource.equals(object)) found = true;
3774                         }
3775
3776                         @Override
3777                         synchronized public void finished(AsyncReadGraph graph) {
3778                                 try {
3779                                         procedure.execute(graph, found);
3780                                 } catch (Throwable t2) {
3781                                         Logger.defaultLogError(t2);
3782                                 }
3783 //                              impl.state.barrier.dec();
3784                         }
3785
3786                         @Override
3787                         public void exception(AsyncReadGraph graph, Throwable t) {
3788                                 try {
3789                                         procedure.exception(graph, t);
3790                                 } catch (Throwable t2) {
3791                                         Logger.defaultLogError(t2);
3792                                 }
3793 //                              impl.state.barrier.dec();
3794                         }
3795
3796                 });
3797
3798         }
3799
3800         @Override
3801         final public void forHasValue(final ReadGraphImpl impl, final Resource subject, final AsyncProcedure<Boolean> procedure) {
3802
3803                 assert(subject != null);
3804                 assert(procedure != null);
3805
3806                 final ListenerBase listener = getListenerBase(procedure);
3807
3808 //              impl.state.barrier.inc();
3809
3810                 try {
3811                         QueryCache.runnerValueQuery(impl, querySupport.getId(subject), impl.parent, listener, new InternalProcedure<byte[]>() {
3812
3813                                 @Override
3814                                 public void execute(ReadGraphImpl graph, byte[] object) {
3815                                         boolean result = object != null;
3816                                         try {
3817                                                 procedure.execute(graph, result);
3818                                         } catch (Throwable t2) {
3819                                                 Logger.defaultLogError(t2);
3820                                         }
3821 //                              impl.state.barrier.dec();
3822                                 }
3823
3824                                 @Override
3825                                 public void exception(ReadGraphImpl graph, Throwable t) {
3826                                         try {
3827                                                 procedure.exception(graph, t);
3828                                         } catch (Throwable t2) {
3829                                                 Logger.defaultLogError(t2);
3830                                         }
3831 //                              impl.state.barrier.dec();
3832                                 }
3833
3834                         });
3835                 } catch (DatabaseException e) {
3836                         Logger.defaultLogError(e);
3837                 }
3838
3839         }
3840
3841         @Override
3842         final public void forOrderedSet(final ReadGraphImpl impl, final Resource subject, final AsyncMultiProcedure<Resource> procedure) {
3843
3844                 assert(subject != null);
3845                 assert(procedure != null);
3846
3847                 final ListenerBase listener = getListenerBase(procedure);
3848
3849                 try {
3850                         
3851                         QueryCache.runnerOrderedSet(impl, querySupport.getId(subject), impl.parent, listener, new IntProcedure() {
3852
3853                                 @Override
3854                                 public void exception(ReadGraphImpl graph, Throwable t) {
3855                                         try {
3856                                                 procedure.exception(graph, t);
3857                                         } catch (Throwable t2) {
3858                                                 Logger.defaultLogError(t2);
3859                                         }
3860 //                              impl.state.barrier.dec();
3861                                 }
3862
3863                                 @Override
3864                                 public void execute(ReadGraphImpl graph, int i) {
3865                                         try {
3866                                                 procedure.execute(graph, querySupport.getResource(i));
3867                                         } catch (Throwable t2) {
3868                                                 Logger.defaultLogError(t2);
3869                                         }
3870                                 }
3871
3872                                 @Override
3873                                 public void finished(ReadGraphImpl graph) {
3874                                         try {
3875                                                 procedure.finished(graph);
3876                                         } catch (Throwable t2) {
3877                                                 Logger.defaultLogError(t2);
3878                                         }
3879 //                              impl.state.barrier.dec();
3880                                 }
3881
3882                         });
3883                 } catch (DatabaseException e) {
3884                         Logger.defaultLogError(e);
3885                 }
3886
3887         }
3888
3889 //      @Override
3890 //      final public <T> void query(final ReadGraphImpl impl, final AsyncRead<T> request, final CacheEntry parent, final AsyncProcedure<T> procedure, ListenerBase listener) throws DatabaseException {
3891 //
3892 //              assert(request != null);
3893 //              assert(procedure != null);
3894 //
3895 //              QueryCache.runnerAsyncReadEntry(impl, request, parent, listener, procedure);
3896 //
3897 //      }
3898
3899 //      @Override
3900 //      final public <T> T tryQuery(final ReadGraphImpl graph, final Read<T> request) throws DatabaseException {
3901 //
3902 //              assert(graph != null);
3903 //              assert(request != null);
3904 //
3905 //              final ReadEntry entry = (ReadEntry)cache.getCached(request);
3906 //              if(entry != null && entry.isReady()) {
3907 //                  return (T)entry.get(graph, this, null);
3908 //              } else {
3909 //                      return request.perform(graph);
3910 //              }
3911 //
3912 //      }
3913
3914 //    final public <T> T tryQuery(final ReadGraphImpl graph, final ExternalRead<T> request) throws DatabaseException {
3915 //
3916 //        assert(graph != null);
3917 //        assert(request != null);
3918 //
3919 //        final ExternalReadEntry<T> entry = cache.externalReadMap.get(request);
3920 //        if(entry != null && entry.isReady()) {
3921 //            if(entry.isExcepted()) {
3922 //                Throwable t = (Throwable)entry.getResult();
3923 //                if(t instanceof DatabaseException) throw (DatabaseException)t;
3924 //                else throw new DatabaseException(t);
3925 //            } else {
3926 //                return (T)entry.getResult();
3927 //            }            
3928 //        } else {
3929 //
3930 //            final DataContainer<T> result = new DataContainer<T>();
3931 //            final DataContainer<Throwable> exception = new DataContainer<Throwable>();
3932 //            
3933 //            request.register(graph, new Listener<T>() {
3934 //                
3935 //                @Override
3936 //                public void exception(Throwable t) {
3937 //                    exception.set(t);
3938 //                }
3939 //
3940 //                @Override
3941 //                public void execute(T t) {
3942 //                    result.set(t);
3943 //                }
3944 //
3945 //                @Override
3946 //                public boolean isDisposed() {
3947 //                    return true;
3948 //                }
3949 //            
3950 //            });
3951 //            
3952 //            Throwable t = exception.get();
3953 //            if(t != null) {
3954 //                if(t instanceof DatabaseException) throw (DatabaseException)t;
3955 //                else throw new DatabaseException(t);
3956 //            }
3957 //            
3958 //            return result.get();
3959 //
3960 //        }
3961 //
3962 //    }
3963         
3964 //      @Override
3965 //      final public <T> void tryQuery(final ReadGraphImpl graph, final AsyncRead<T> request, AsyncProcedure<T> procedure) {
3966 //
3967 //              assert(graph != null);
3968 //              assert(request != null);
3969 //
3970 //              final AsyncReadEntry entry = cache.asyncReadMap.get(request);
3971 //              if(entry != null && entry.isReady()) {
3972 //                      if(entry.isExcepted()) {
3973 //                              procedure.exception(graph, (Throwable)entry.getResult());
3974 //                      } else {
3975 //                              procedure.execute(graph, (T)entry.getResult());
3976 //                      }
3977 //              } else {
3978 //                      request.perform(graph, procedure);
3979 //              }
3980 //
3981 //      }
3982
3983         @Override
3984         final public <T> void query(final ReadGraphImpl impl, final MultiRead<T> request, final CacheEntry parent, final SyncMultiProcedure<T> procedure, ListenerBase listener) {
3985
3986                 assert(request != null);
3987                 assert(procedure != null);
3988
3989                 try {
3990
3991                         queryMultiRead(impl, request, parent, listener, procedure);
3992                         
3993                 } catch (DatabaseException e) {
3994                         
3995                         throw new IllegalStateException(e);
3996                         
3997                 }
3998
3999         }
4000
4001         @Override
4002         final public <T> void query(final ReadGraphImpl impl, final AsyncMultiRead<T> request, final CacheEntry parent, final AsyncMultiProcedure<T> procedure, ListenerBase listener) {
4003
4004                 assert(request != null);
4005                 assert(procedure != null);
4006
4007 //              impl.state.barrier.inc();
4008
4009                 runAsyncMultiRead(impl, request, parent, listener, new AsyncMultiProcedure<T>() {
4010
4011                         public void execute(AsyncReadGraph graph, T result) {
4012
4013                                 try {
4014                                         procedure.execute(graph, result);
4015                                 } catch (Throwable t2) {
4016                                         Logger.defaultLogError(t2);
4017                                 }
4018                         }
4019
4020                         @Override
4021                         public void finished(AsyncReadGraph graph) {
4022
4023                                 try {
4024                                         procedure.finished(graph);
4025                                 } catch (Throwable t2) {
4026                                         Logger.defaultLogError(t2);
4027                                 }
4028
4029 //                              impl.state.barrier.dec();
4030
4031                         }
4032
4033                         @Override
4034                         public String toString() {
4035                                 return procedure.toString();
4036                         }
4037
4038                         @Override
4039                         public void exception(AsyncReadGraph graph, Throwable t) {
4040
4041                                 try {
4042                                         procedure.exception(graph, t);
4043                                 } catch (Throwable t2) {
4044                                         Logger.defaultLogError(t2);
4045                                 }
4046
4047 //                              impl.state.barrier.dec();
4048
4049                         }
4050
4051                 });
4052
4053         }
4054
4055 //      @Override
4056 //      final public <T> void query(final ReadGraphImpl impl, final ExternalRead<T> request, final CacheEntry parent, final Procedure<T> procedure, ListenerBase listener) throws DatabaseException {
4057 //
4058 //              assert(request != null);
4059 //              assert(procedure != null);
4060 //
4061 //              try {
4062 //              
4063 //                      queryPrimitiveRead(impl, request, parent, listener, new AsyncProcedure<T>() {
4064 //      
4065 //                              @Override
4066 //                              public String toString() {
4067 //                                      return procedure.toString();
4068 //                              }
4069 //      
4070 //                              @Override
4071 //                              public void execute(AsyncReadGraph graph, T result) {
4072 //                                      try {
4073 //                                              procedure.execute(result);
4074 //                                      } catch (Throwable t2) {
4075 //                                              Logger.defaultLogError(t2);
4076 //                                      }
4077 //                              }
4078 //
4079 //                              @Override
4080 //                              public void exception(AsyncReadGraph graph, Throwable throwable) {
4081 //                                      try {
4082 //                                              procedure.exception(throwable);
4083 //                                      } catch (Throwable t2) {
4084 //                                              Logger.defaultLogError(t2);
4085 //                                      }
4086 //                              }
4087 //      
4088 //                      });
4089 //                      
4090 //              } catch (DatabaseException e) {
4091 //                      
4092 //                      throw new IllegalStateException(e);
4093 //                      
4094 //              }
4095 //
4096 //      }
4097
4098         @Override
4099         public VirtualGraph getProvider(Resource subject, Resource predicate, Resource object) {
4100                 
4101                 return querySupport.getProvider(querySupport.getId(subject), querySupport.getId(predicate), querySupport.getId(object));
4102                 
4103         }
4104         
4105         @Override
4106         public VirtualGraph getProvider(Resource subject, Resource predicate) {
4107                 
4108                 return querySupport.getProvider(querySupport.getId(subject), querySupport.getId(predicate));
4109                 
4110         }
4111
4112         @Override
4113         public VirtualGraph getValueProvider(Resource subject) {
4114                 
4115                 return querySupport.getValueProvider(querySupport.getId(subject));
4116                 
4117         }
4118
4119         public boolean resumeTasks(ReadGraphImpl graph) {
4120
4121                 return querySupport.resume(graph);
4122
4123         }
4124         
4125         public boolean isImmutable(int resourceId) {
4126                 return querySupport.isImmutable(resourceId);
4127         }
4128
4129         public boolean isImmutable(Resource resource) {
4130                 ResourceImpl impl = (ResourceImpl)resource;
4131                 return isImmutable(impl.id);
4132         }
4133         
4134         private Layer0 L0;
4135         
4136         public Layer0 getL0(ReadGraph graph) {
4137                 if(L0 == null) {
4138                         L0 = Layer0.getInstance(graph);
4139                 }
4140                 return L0;
4141         }
4142
4143         public Layer0 getL0() {
4144                 return L0;
4145         }
4146
4147     public static ThreadLocal<Integer> thread = new ThreadLocal<Integer>() {
4148         protected Integer initialValue() {
4149             return -1;
4150         }
4151     };
4152         
4153 }