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