OLD | NEW |
| (Empty) |
1 // Copyright (c) 2015, the Dartino project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE.md file. | |
4 | |
5 library fletchc.hub.session_manager; | |
6 | |
7 import 'dart:async' show | |
8 Future, | |
9 Timer; | |
10 | |
11 import 'client_commands.dart' show | |
12 CommandSender; | |
13 | |
14 import 'hub_main.dart' show | |
15 WorkerConnection; | |
16 | |
17 export 'hub_main.dart' show | |
18 WorkerConnection; | |
19 | |
20 import '../worker/developer.dart' show | |
21 Settings; | |
22 | |
23 import '../diagnostic.dart' show | |
24 DiagnosticKind, | |
25 DiagnosticParameter, | |
26 InputError, | |
27 throwFatalError, | |
28 throwInternalError; | |
29 | |
30 import '../../fletch_system.dart' show | |
31 FletchDelta; | |
32 | |
33 export '../../fletch_system.dart' show | |
34 FletchDelta; | |
35 | |
36 import '../../vm_session.dart' show | |
37 Session; | |
38 | |
39 export '../../vm_session.dart' show | |
40 Session; | |
41 | |
42 import '../../fletch_compiler.dart' show | |
43 FletchCompiler; | |
44 | |
45 export '../../fletch_compiler.dart' show | |
46 FletchCompiler; | |
47 | |
48 import '../../incremental/fletchc_incremental.dart' show | |
49 IncrementalCompiler; | |
50 | |
51 export '../../incremental/fletchc_incremental.dart' show | |
52 IncrementalCompiler; | |
53 | |
54 import '../../fletch_vm.dart' show | |
55 FletchVm; | |
56 | |
57 export '../../fletch_vm.dart' show | |
58 FletchVm; | |
59 | |
60 // TODO(karlklose): we need a better API for session management. | |
61 class Sessions { | |
62 static Iterable<String> get names { | |
63 return internalSessions.keys; | |
64 } | |
65 } | |
66 | |
67 final Map<String, UserSession> internalSessions = <String, UserSession>{}; | |
68 | |
69 // TODO(ahe): We need a command to switch to another session. | |
70 String internalCurrentSession = "local"; | |
71 | |
72 String get currentSession => internalCurrentSession; | |
73 | |
74 Future<UserSession> createSession( | |
75 String name, | |
76 Future<WorkerConnection> allocateWorker()) async { | |
77 if (name == null) { | |
78 throw new ArgumentError("session name must not be `null`."); | |
79 } | |
80 | |
81 UserSession session = lookupSession(name); | |
82 if (session != null) { | |
83 throwFatalError(DiagnosticKind.sessionAlreadyExists, sessionName: name); | |
84 } | |
85 session = new UserSession(name, await allocateWorker()); | |
86 internalSessions[name] = session; | |
87 return session; | |
88 } | |
89 | |
90 UserSession lookupSession(String name) => internalSessions[name]; | |
91 | |
92 // Remove the session named [name] from [internalSessions], but caller must | |
93 // ensure that [worker] has its static state cleaned up and returned to the | |
94 // isolate pool. | |
95 UserSession endSession(String name) { | |
96 UserSession session = internalSessions.remove(name); | |
97 if (session == null) { | |
98 throwFatalError(DiagnosticKind.noSuchSession, sessionName: name); | |
99 } | |
100 return session; | |
101 } | |
102 | |
103 void endAllSessions() { | |
104 internalSessions.forEach((String name, UserSession session) { | |
105 print("Ending session: $name"); | |
106 session.worker.endSession(); | |
107 }); | |
108 internalSessions.clear(); | |
109 } | |
110 | |
111 /// A session in the hub (main isolate). | |
112 class UserSession { | |
113 final String name; | |
114 | |
115 final WorkerConnection worker; | |
116 | |
117 bool hasActiveWorkerTask = false; | |
118 | |
119 UserSession(this.name, this.worker); | |
120 | |
121 void kill(void printLineOnStderr(String line)) { | |
122 worker.isolate.kill(); | |
123 internalSessions.remove(name); | |
124 InputError error = new InputError( | |
125 DiagnosticKind.terminatedSession, | |
126 <DiagnosticParameter, dynamic>{DiagnosticParameter.sessionName: name}); | |
127 printLineOnStderr(error.asDiagnostic().formatMessage()); | |
128 } | |
129 } | |
130 | |
131 typedef void SendBytesFunction(List<int> bytes); | |
132 | |
133 class BufferingOutputSink implements Sink<List<int>> { | |
134 SendBytesFunction sendBytes; | |
135 | |
136 List<List<int>> buffer = new List(); | |
137 | |
138 void attachCommandSender(SendBytesFunction sendBytes) { | |
139 for (List<int> data in buffer) { | |
140 sendBytes(data); | |
141 } | |
142 buffer = new List(); | |
143 this.sendBytes = sendBytes; | |
144 } | |
145 | |
146 void detachCommandSender() { | |
147 assert(sendBytes != null); | |
148 sendBytes = null; | |
149 } | |
150 | |
151 void add(List<int> bytes) { | |
152 if (sendBytes != null) { | |
153 sendBytes(bytes); | |
154 } else { | |
155 buffer.add(bytes); | |
156 } | |
157 } | |
158 | |
159 void close() { | |
160 throwInternalError("Unimplemented"); | |
161 } | |
162 } | |
163 | |
164 /// The state stored in a worker isolate of a [UserSession]. | |
165 /// TODO(wibling): This should be moved into a worker specific file. | |
166 class SessionState { | |
167 final String name; | |
168 | |
169 final BufferingOutputSink stdoutSink = new BufferingOutputSink(); | |
170 | |
171 final BufferingOutputSink stderrSink = new BufferingOutputSink(); | |
172 | |
173 final FletchCompiler compilerHelper; | |
174 | |
175 final IncrementalCompiler compiler; | |
176 | |
177 final List<FletchDelta> compilationResults = <FletchDelta>[]; | |
178 | |
179 final List<String> loggedMessages = <String>[]; | |
180 | |
181 Uri script; | |
182 | |
183 Session session; | |
184 | |
185 FletchVm fletchVm; | |
186 | |
187 int fletchAgentVmId; | |
188 | |
189 Settings settings; | |
190 | |
191 bool explicitAttach = false; | |
192 | |
193 SessionState(this.name, this.compilerHelper, this.compiler, this.settings); | |
194 | |
195 bool get hasRemoteVm => fletchAgentVmId != null; | |
196 | |
197 bool get colorsDisabled => session == null ? false : session.colorsDisabled; | |
198 | |
199 void addCompilationResult(FletchDelta delta) { | |
200 compilationResults.add(delta); | |
201 } | |
202 | |
203 void resetCompiler() { | |
204 compilationResults.clear(); | |
205 } | |
206 | |
207 Future terminateSession() async { | |
208 if (session != null) { | |
209 if (!session.terminated) { | |
210 bool done = false; | |
211 Timer timer = new Timer(const Duration(seconds: 5), () { | |
212 if (!done) { | |
213 print("Timed out waiting for Fletch VM to shutdown; killing " | |
214 "session"); | |
215 session.kill(); | |
216 } | |
217 }); | |
218 await session.terminateSession(); | |
219 done = true; | |
220 timer.cancel(); | |
221 } | |
222 explicitAttach = false; | |
223 } | |
224 session = null; | |
225 } | |
226 | |
227 void attachCommandSender(CommandSender sender) { | |
228 stdoutSink.attachCommandSender((d) => sender.sendStdoutBytes(d)); | |
229 stderrSink.attachCommandSender((d) => sender.sendStderrBytes(d)); | |
230 } | |
231 | |
232 void detachCommandSender() { | |
233 stdoutSink.detachCommandSender(); | |
234 stderrSink.detachCommandSender(); | |
235 } | |
236 | |
237 void log(message) { | |
238 loggedMessages.add("[$name: ${new DateTime.now()} $message]"); | |
239 } | |
240 | |
241 String getLog() => loggedMessages.join("\n"); | |
242 | |
243 static SessionState internalCurrent; | |
244 | |
245 /// Don't use this as a shortcut to get a [SessionState] object. Generally, a | |
246 /// [SessionState] object should be passed as a parameter to any user of it, | |
247 /// otherwise API that takes a [SessionState] object doesn't work correctly | |
248 /// and testing becomes hard. | |
249 /// | |
250 /// TODO(ahe): Perhaps we can remove this getter, and [internalCurrent] by | |
251 /// storing this object in ../worker/worker_main.dart. For example, | |
252 /// [workerMain] holds a reference to an instance of [SessionState] and | |
253 /// passes this reference to [WorkerSideTask] which can in turn pass it on to | |
254 /// the verb/task in [WorkerSideTask.performTask]. | |
255 static SessionState get current => internalCurrent; | |
256 } | |
OLD | NEW |