1 module d.semantic.scheduler;
2 
3 import d.ir.symbol;
4 
5 import d.semantic.semantic;
6 import d.semantic.symbol;
7 
8 import std.algorithm;
9 import std.range;
10 import std.traits;
11 
12 import core.thread;
13 
14 final class Scheduler {
15 	SemanticPass pass;
16 	
17 	Process[Symbol] processes;
18 	
19 	private Process[] pool;
20 	
21 	this(SemanticPass pass) {
22 		this.pass = pass;
23 	}
24 	
25 	void terminate() {
26 		auto f = new Fiber({
27 			while (processes.length) {
28 				foreach (s; processes.keys) {
29 					require(s);
30 				}
31 			}
32 		});
33 		
34 		while (f.state != Fiber.State.TERM) {
35 			f.call();
36 		}
37 	}
38 	
39 	void require(Symbol s, Step step = LastStep) {
40 		if (s.step >= step) {
41 			return;
42 		}
43 		
44 		// Overloadset sadness...
45 		if (auto os = cast(OverloadSet) s) {
46 			require(os, step);
47 			return;
48 		}
49 		
50 		auto state = pass.state;
51 		scope(exit) pass.state = state;
52 		
53 		while(s.step < step) {
54 			auto p = s in processes;
55 			assert(p, "No Fiber found for " ~ s.name.toString(pass.context));
56 			
57 			auto f = *p;
58 			if (f.state == Fiber.State.EXEC) {
59 				// TODO: Check for possible forward reference problem.
60 			}
61 			
62 			if (f.state == Fiber.State.HOLD) {
63 				f.call();
64 			}
65 			
66 			if (f.state == Fiber.State.TERM) {
67 				processes.remove(s);
68 				
69 				pool ~= f;
70 			}
71 			
72 			if (s.step >= step) {
73 				return;
74 			}
75 			
76 			/+
77 			import std.stdio;
78 			writefln("%s (%s) %s", s.name.toString(pass.context), typeid(s).toString(), step);
79 			//+
80 			try {
81 				throw new Exception("Require call stack");
82 			} catch(Exception e) {
83 				writeln(e);
84 			}
85 			// +/
86 			writeln("Yield !");
87 			
88 			Thread.sleep(dur!"seconds"(1));
89 			// +/
90 			Fiber.yield();
91 		}
92 	}
93 	
94 	void require(R)(R syms, Step step = LastStep) if(isSymbolRange!R) {
95 		foreach (s; syms) {
96 			require(s, step);
97 		}
98 	}
99 	
100 	void require(OverloadSet os, Step step = LastStep) {
101 		if (os.step >= step) {
102 			return;
103 		}
104 		
105 		foreach (s; os.set) {
106 			require(s, step);
107 			os.hasContext = os.hasContext || s.hasContext;
108 			os.hasThis = os.hasThis || s.hasThis;
109 		}
110 		
111 		os.step = step;
112 	}
113 	
114 	private Process getProcess() {
115 		Process p;
116 		
117 		// XXX: it seems that if(pool) test for the pointer, not the content.
118 		// Seems to me like a weird conflation of identity and value.
119 		if (pool.length) {
120 			p = pool[$ - 1];
121 			
122 			pool = pool[0 .. $ - 1];
123 			pool.assumeSafeAppend();
124 		} else {
125 			p = new Process(pass);
126 		}
127 		
128 		return p;
129 	}
130 	
131 	void schedule(D, S)(D d, S s) if(isSchedulable!(D, S)) in {
132 		assert(s.step == SemanticPass.Step.Parsed, "Symbol processing already started.");
133 	} do {
134 		auto p = getProcess();
135 		p.init(d, s);
136 		
137 		processes[s] = p;
138 	}
139 	
140 	void schedule(Template t, TemplateInstance i) in {
141 		assert(i.step == SemanticPass.Step.Parsed, "Symbol processing already started.");
142 	} do {
143 		auto p = getProcess();
144 		p.init(t, i);
145 		
146 		processes[i] = p;
147 	}
148 	
149 	// FIXME: We should consider a generic way to get things in there.
150 	// It is clearly not going to scale that way.
151 	import d.ast.expression;
152 	void schedule(AstExpression dv, Variable v) in {
153 		assert(v.step == SemanticPass.Step.Parsed, "Symbol processing already started.");
154 	} do {
155 		auto p = getProcess();
156 		p.init(dv, v);
157 		
158 		processes[v] = p;
159 	}
160 }
161 
162 private:
163 final class Process : Fiber {
164 	enum StackSize = 32 * 4096;
165 	
166 	SemanticPass pass;
167 	
168 	this(SemanticPass pass) {
169 		this.pass = pass;
170 		
171 		super(function() {
172 			assert(0, "You must initialize process before using it.");
173 		}, StackSize);
174 	}
175 	
176 	void init(D, S)(D d, S s) {
177 		auto state = pass.state;
178 		reset({
179 			pass.state = state;
180 			SymbolAnalyzer(pass).analyze(d, s);
181 		});
182 	}
183 }
184 
185 alias Step = SemanticPass.Step;
186 enum LastStep = EnumMembers!Step[$ - 1];
187 
188 bool checkEnumElements() {
189 	uint i;
190 	foreach (s; EnumMembers!Step) {
191 		if (s != i++) {
192 			return false;
193 		}
194 	}
195 
196 	return i > 0;
197 }
198 
199 static assert(is(Step : uint) && checkEnumElements(), "Step enum is ill defined.");
200 
201 enum isSymbolRange(R) = isInputRange!R && is(ElementType!R : Symbol);
202