UNPKG

11.4 kBJavaScriptView Raw
1"use strict";
2// Copyright (c) Jupyter Development Team.
3// Distributed under the terms of the Modified BSD License.
4Object.defineProperty(exports, "__esModule", { value: true });
5exports.KernelManager = void 0;
6const polling_1 = require("@lumino/polling");
7const signaling_1 = require("@lumino/signaling");
8const __1 = require("..");
9const basemanager_1 = require("../basemanager");
10const restapi_1 = require("./restapi");
11const default_1 = require("./default");
12/**
13 * An implementation of a kernel manager.
14 */
15class KernelManager extends basemanager_1.BaseManager {
16 /**
17 * Construct a new kernel manager.
18 *
19 * @param options - The default options for kernel.
20 */
21 constructor(options = {}) {
22 var _a;
23 super(options);
24 this._isReady = false;
25 this._kernelConnections = new Set();
26 this._models = new Map();
27 this._runningChanged = new signaling_1.Signal(this);
28 this._connectionFailure = new signaling_1.Signal(this);
29 // Start model and specs polling with exponential backoff.
30 this._pollModels = new polling_1.Poll({
31 auto: false,
32 factory: () => this.requestRunning(),
33 frequency: {
34 interval: 10 * 1000,
35 backoff: true,
36 max: 300 * 1000
37 },
38 name: `@jupyterlab/services:KernelManager#models`,
39 standby: (_a = options.standby) !== null && _a !== void 0 ? _a : 'when-hidden'
40 });
41 // Initialize internal data.
42 this._ready = (async () => {
43 await this._pollModels.start();
44 await this._pollModels.tick;
45 this._isReady = true;
46 })();
47 }
48 /**
49 * Test whether the manager is ready.
50 */
51 get isReady() {
52 return this._isReady;
53 }
54 /**
55 * A promise that fulfills when the manager is ready.
56 */
57 get ready() {
58 return this._ready;
59 }
60 /**
61 * A signal emitted when the running kernels change.
62 */
63 get runningChanged() {
64 return this._runningChanged;
65 }
66 /**
67 * A signal emitted when there is a connection failure.
68 */
69 get connectionFailure() {
70 return this._connectionFailure;
71 }
72 /**
73 * Dispose of the resources used by the manager.
74 */
75 dispose() {
76 if (this.isDisposed) {
77 return;
78 }
79 this._models.clear();
80 this._kernelConnections.forEach(x => x.dispose());
81 this._pollModels.dispose();
82 super.dispose();
83 }
84 /**
85 * Connect to an existing kernel.
86 *
87 * @returns The new kernel connection.
88 *
89 * #### Notes
90 * This will use the manager's server settings and ignore any server
91 * settings passed in the options.
92 */
93 connectTo(options) {
94 var _a;
95 const { id } = options.model;
96 let handleComms = (_a = options.handleComms) !== null && _a !== void 0 ? _a : true;
97 // By default, handle comms only if no other kernel connection is.
98 if (options.handleComms === undefined) {
99 for (const kc of this._kernelConnections) {
100 if (kc.id === id && kc.handleComms) {
101 handleComms = false;
102 break;
103 }
104 }
105 }
106 const kernelConnection = new default_1.KernelConnection({
107 handleComms,
108 ...options,
109 serverSettings: this.serverSettings
110 });
111 this._onStarted(kernelConnection);
112 if (!this._models.has(id)) {
113 // We trust the user to connect to an existing kernel, but we verify
114 // asynchronously.
115 void this.refreshRunning().catch(() => {
116 /* no-op */
117 });
118 }
119 return kernelConnection;
120 }
121 /**
122 * Create an iterator over the most recent running kernels.
123 *
124 * @returns A new iterator over the running kernels.
125 */
126 running() {
127 return this._models.values();
128 }
129 /**
130 * Force a refresh of the running kernels.
131 *
132 * @returns A promise that resolves when the running list has been refreshed.
133 *
134 * #### Notes
135 * This is not typically meant to be called by the user, since the
136 * manager maintains its own internal state.
137 */
138 async refreshRunning() {
139 await this._pollModels.refresh();
140 await this._pollModels.tick;
141 }
142 /**
143 * Start a new kernel.
144 *
145 * @param createOptions - The kernel creation options
146 *
147 * @param connectOptions - The kernel connection options
148 *
149 * @returns A promise that resolves with the kernel connection.
150 *
151 * #### Notes
152 * The manager `serverSettings` will be always be used.
153 */
154 async startNew(createOptions = {}, connectOptions = {}) {
155 const model = await (0, restapi_1.startNew)(createOptions, this.serverSettings);
156 return this.connectTo({
157 ...connectOptions,
158 model
159 });
160 }
161 /**
162 * Shut down a kernel by id.
163 *
164 * @param id - The id of the target kernel.
165 *
166 * @returns A promise that resolves when the operation is complete.
167 */
168 async shutdown(id) {
169 await (0, restapi_1.shutdownKernel)(id, this.serverSettings);
170 await this.refreshRunning();
171 }
172 /**
173 * Shut down all kernels.
174 *
175 * @returns A promise that resolves when all of the kernels are shut down.
176 */
177 async shutdownAll() {
178 // Update the list of models to make sure our list is current.
179 await this.refreshRunning();
180 // Shut down all models.
181 await Promise.all([...this._models.keys()].map(id => (0, restapi_1.shutdownKernel)(id, this.serverSettings)));
182 // Update the list of models to clear out our state.
183 await this.refreshRunning();
184 }
185 /**
186 * Find a kernel by id.
187 *
188 * @param id - The id of the target kernel.
189 *
190 * @returns A promise that resolves with the kernel's model.
191 */
192 async findById(id) {
193 if (this._models.has(id)) {
194 return this._models.get(id);
195 }
196 await this.refreshRunning();
197 return this._models.get(id);
198 }
199 /**
200 * Execute a request to the server to poll running kernels and update state.
201 */
202 async requestRunning() {
203 var _a, _b;
204 let models;
205 try {
206 models = await (0, restapi_1.listRunning)(this.serverSettings);
207 }
208 catch (err) {
209 // Handle network errors, as well as cases where we are on a
210 // JupyterHub and the server is not running. JupyterHub returns a
211 // 503 (<2.0) or 424 (>2.0) in that case.
212 if (err instanceof __1.ServerConnection.NetworkError ||
213 ((_a = err.response) === null || _a === void 0 ? void 0 : _a.status) === 503 ||
214 ((_b = err.response) === null || _b === void 0 ? void 0 : _b.status) === 424) {
215 this._connectionFailure.emit(err);
216 }
217 throw err;
218 }
219 if (this.isDisposed) {
220 return;
221 }
222 if (this._models.size === models.length &&
223 models.every(model => {
224 const existing = this._models.get(model.id);
225 if (!existing) {
226 return false;
227 }
228 return (existing.connections === model.connections &&
229 existing.execution_state === model.execution_state &&
230 existing.last_activity === model.last_activity &&
231 existing.name === model.name &&
232 existing.reason === model.reason &&
233 existing.traceback === model.traceback);
234 })) {
235 // Identical models list (presuming models does not contain duplicate
236 // ids), so just return
237 return;
238 }
239 this._models = new Map(models.map(x => [x.id, x]));
240 // For any kernel connection to a kernel that doesn't exist, notify it of
241 // the shutdown.
242 this._kernelConnections.forEach(kc => {
243 if (!this._models.has(kc.id)) {
244 kc.handleShutdown();
245 }
246 });
247 this._runningChanged.emit(models);
248 }
249 /**
250 * Handle a kernel starting.
251 */
252 _onStarted(kernelConnection) {
253 this._kernelConnections.add(kernelConnection);
254 kernelConnection.statusChanged.connect(this._onStatusChanged, this);
255 kernelConnection.disposed.connect(this._onDisposed, this);
256 }
257 _onDisposed(kernelConnection) {
258 this._kernelConnections.delete(kernelConnection);
259 // A dispose emission could mean the server session is deleted, or that
260 // the kernel JS object is disposed and the kernel still exists on the
261 // server, so we refresh from the server to make sure we reflect the
262 // server state.
263 void this.refreshRunning().catch(() => {
264 /* no-op */
265 });
266 }
267 _onStatusChanged(kernelConnection, status) {
268 if (status === 'dead') {
269 // We asynchronously update our list of kernels, which asynchronously
270 // will dispose them. We do not want to immediately dispose them because
271 // there may be other signal handlers that want to be called.
272 void this.refreshRunning().catch(() => {
273 /* no-op */
274 });
275 }
276 }
277}
278exports.KernelManager = KernelManager;
279/**
280 * The namespace for `KernelManager` class statics.
281 */
282(function (KernelManager) {
283 /**
284 * A no-op kernel manager to be used when starting kernels.
285 */
286 class NoopManager extends KernelManager {
287 constructor() {
288 super(...arguments);
289 this._readyPromise = new Promise(() => {
290 /* no-op */
291 });
292 }
293 /**
294 * Whether the manager is active.
295 */
296 get isActive() {
297 return false;
298 }
299 /**
300 * Used for testing.
301 */
302 get parentReady() {
303 return super.ready;
304 }
305 /**
306 * Start a new kernel - throws an error since it is not supported.
307 */
308 async startNew(createOptions = {}, connectOptions = {}) {
309 return Promise.reject(new Error('Not implemented in no-op Kernel Manager'));
310 }
311 /**
312 * Connect to an existing kernel - throws an error since it is not supported.
313 */
314 connectTo(options) {
315 throw new Error('Not implemented in no-op Kernel Manager');
316 }
317 /**
318 * Shut down a kernel by id - throws an error since it is not supported.
319 */
320 async shutdown(id) {
321 return Promise.reject(new Error('Not implemented in no-op Kernel Manager'));
322 }
323 /**
324 * A promise that fulfills when the manager is ready (never).
325 */
326 get ready() {
327 return this.parentReady.then(() => this._readyPromise);
328 }
329 /**
330 * Execute a request to the server to poll running kernels and update state.
331 */
332 async requestRunning() {
333 return Promise.resolve();
334 }
335 }
336 KernelManager.NoopManager = NoopManager;
337})(KernelManager || (exports.KernelManager = KernelManager = {}));
338//# sourceMappingURL=manager.js.map
\No newline at end of file