DB request scheduling scheme fails with district diagrams
[simantics/platform.git] / bundles / org.simantics.db.impl / src / org / simantics / db / impl / BlockingAsyncProcedure.java
1 /*******************************************************************************
2  * Copyright (c) 2018 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  *     Semantum Oy - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.db.impl;
13
14 import org.simantics.db.AsyncReadGraph;
15 import org.simantics.db.exception.DatabaseException;
16 import org.simantics.db.impl.graph.BarrierTracing;
17 import org.simantics.db.impl.graph.ReadGraphImpl;
18 import org.simantics.db.impl.query.AsyncReadEntry;
19 import org.simantics.db.impl.query.PendingTaskSupport;
20 import org.simantics.db.procedure.AsyncProcedure;
21
22 public class BlockingAsyncProcedure<Result> implements AsyncProcedure<Result> {
23
24     private static final Object NO_RESULT = new Object();
25
26     public final Object key;
27     public final ReadGraphImpl queryGraph;
28     public final ReadGraphImpl callerGraph;
29     public final AsyncProcedure<Result> procedure;
30     public PendingTaskSupport pendingTaskSupport;
31     public final boolean needsToBlock;
32     public Object result = NO_RESULT;
33     public Throwable exception = null;
34
35     private ReadGraphImpl queryGraph() {
36         return queryGraph;
37     }
38     
39     public BlockingAsyncProcedure(ReadGraphImpl callerGraph, AsyncReadEntry<Result> entry, AsyncProcedure<Result> procedure, Object key, boolean needsToBlock) {
40         
41         // A new graph for evaluating the query with correct parent and asyncBarrier
42         queryGraph = callerGraph.withParent(entry, () -> {
43
44             dispatchProcedure(queryGraph(), callerGraph, entry, procedure, needsToBlock);
45             
46         }, needsToBlock);
47         
48         queryGraph.asyncBarrier.inc();
49
50         this.procedure = procedure;
51         this.key = key;
52         this.queryGraph.asyncBarrier.inc();
53         this.callerGraph = callerGraph;
54         this.needsToBlock = needsToBlock;
55         if (BarrierTracing.BOOKKEEPING) {
56             BarrierTracing.registerBAP(this);
57         }
58     }
59
60     @Override
61     public void execute(AsyncReadGraph graph_, Result result) {
62         
63         this.result = result;
64         queryGraph.asyncBarrier.dec();
65         
66     }
67
68     @Override
69     public void exception(AsyncReadGraph graph_, Throwable t) {
70
71         this.exception = t;
72         queryGraph.asyncBarrier.dec();
73         
74     }
75     
76     public void waitBarrier() {
77         queryGraph.asyncBarrier.waitBarrier(key, queryGraph);
78     }
79     
80     public void dec() {
81
82         queryGraph.asyncBarrier.dec();
83         
84     }
85
86     @SuppressWarnings("unchecked")
87     public Result get() throws DatabaseException {
88
89         if(needsToBlock)
90             queryGraph.asyncBarrier.waitBarrier(key, queryGraph);
91
92         if(exception != null) {
93             if(exception instanceof DatabaseException) throw (DatabaseException)exception;
94             throw new DatabaseException(exception);
95         } else {
96             return (Result)result;
97         }
98
99     }
100
101     @SuppressWarnings("unchecked")
102     public Result getResult() {
103         return (Result)result;
104     }
105
106     public Throwable getException() {
107         return exception;
108     }
109
110     @Override
111     public String toString() {
112         return "." + procedure; 
113     }
114     
115     private void dispatchProcedure(ReadGraphImpl queryGraph, ReadGraphImpl parentGraph, AsyncReadEntry<Result> entry, AsyncProcedure<Result> procedure_, boolean needsToBlock) {
116         
117         AsyncProcedure<Result> procedure = entry != null ? entry : procedure_;
118
119         ReadGraphImpl executeGraph = parentGraph.withParent(parentGraph.parent, null, needsToBlock);
120         executeGraph.asyncBarrier.inc();
121         try {
122             if(procedure != null) {
123                 procedure.execute(executeGraph, get());
124             }
125         } catch (DatabaseException e) {
126             if(procedure != null) procedure.exception(executeGraph, e);
127             exception = e;
128         } catch (Throwable t) {
129             DatabaseException dbe = new DatabaseException(t);
130             if(procedure != null) procedure.exception(executeGraph, dbe);
131             exception = dbe;
132         } finally {
133
134             if (entry != null) {
135                 assert(entry.isReady());
136                 // This does not throw
137                 entry.performFromCache(executeGraph, procedure_);
138             }
139
140             executeGraph.asyncBarrier.dec();
141             if(needsToBlock)
142                 executeGraph.asyncBarrier.waitBarrier(procedure, executeGraph);
143         }
144
145         if (BarrierTracing.BOOKKEEPING) {
146             BarrierTracing.unregisterBAP(this);
147         }
148
149     }
150
151 }