Skip to content

Commit 811260c

Browse files
code-asherkylecarbs
authored andcommitted
Hook up shared process sorta
1 parent d827015 commit 811260c

15 files changed

Lines changed: 198 additions & 246 deletions

File tree

packages/ide/src/client.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1+
import { Event } from "@coder/events";
12
import { field, logger, time, Time } from "@coder/logger";
2-
import { InitData } from "@coder/protocol";
3+
import { InitData, ISharedProcessData } from "@coder/protocol";
34
import { retry, Retry } from "./retry";
45
import { client } from "./fill/client";
56
import { Clipboard, clipboard } from "./fill/clipboard";
@@ -167,6 +168,10 @@ export abstract class Client {
167168
return client.initData;
168169
}
169170

171+
public get onSharedProcessActive(): Event<ISharedProcessData> {
172+
return client.onSharedProcessActive;
173+
}
174+
170175
/**
171176
* Initialize the IDE.
172177
*/

packages/ide/src/fill/net.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
import { Net } from "@coder/protocol";
2+
import { client } from "./client";
23

3-
export = new Net();
4+
export = new Net(client);

packages/protocol/src/browser/client.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { ReadWriteConnection, InitData, OperatingSystem } from "../common/connection";
1+
import { ReadWriteConnection, InitData, OperatingSystem, ISharedProcessData } from "../common/connection";
22
import { NewEvalMessage, ServerMessage, EvalDoneMessage, EvalFailedMessage, TypedValue, ClientMessage, NewSessionMessage, TTYDimensions, SessionOutputMessage, CloseSessionInputMessage, WorkingInitMessage, NewConnectionMessage } from "../proto";
3-
import { Emitter } from "@coder/events";
3+
import { Emitter, Event } from "@coder/events";
44
import { logger, field } from "@coder/logger";
55
import { ChildProcess, SpawnOptions, ServerProcess, ServerSocket, Socket } from "./command";
66

@@ -19,17 +19,17 @@ export class Client {
1919
private readonly connections: Map<number, ServerSocket> = new Map();
2020

2121
private _initData: InitData | undefined;
22-
private initDataEmitter: Emitter<InitData> = new Emitter();
22+
private initDataEmitter = new Emitter<InitData>();
2323
private initDataPromise: Promise<InitData>;
2424

25+
private sharedProcessActiveEmitter = new Emitter<ISharedProcessData>();
26+
2527
/**
2628
* @param connection Established connection to the server
2729
*/
2830
public constructor(
2931
private readonly connection: ReadWriteConnection,
3032
) {
31-
this.initDataEmitter = new Emitter();
32-
3333
connection.onMessage((data) => {
3434
try {
3535
this.handleMessage(ServerMessage.deserializeBinary(data));
@@ -47,6 +47,10 @@ export class Client {
4747
return this.initDataPromise;
4848
}
4949

50+
public get onSharedProcessActive(): Event<ISharedProcessData> {
51+
return this.sharedProcessActiveEmitter.event;
52+
}
53+
5054
public evaluate<R>(func: () => R | Promise<R>): Promise<R>;
5155
public evaluate<R, T1>(func: (a1: T1) => R | Promise<R>, a1: T1): Promise<R>;
5256
public evaluate<R, T1, T2>(func: (a1: T1, a2: T2) => R | Promise<R>, a1: T1, a2: T2): Promise<R>;
@@ -315,6 +319,10 @@ export class Client {
315319
}
316320
c.emit("end");
317321
this.connections.delete(message.getConnectionFailure()!.getId());
322+
} else if (message.hasSharedProcessActive()) {
323+
this.sharedProcessActiveEmitter.emit({
324+
socketPath: message.getSharedProcessActive()!.getSocketPath(),
325+
});
318326
}
319327
}
320328
}

packages/protocol/src/browser/modules/net.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as net from "net";
2-
import { Client } from '../client';
2+
import { Client } from "../client";
33

44
type NodeNet = typeof net;
55

@@ -24,6 +24,7 @@ export class Net implements NodeNet {
2424
throw new Error("not implemented");
2525
}
2626

27+
// tslint:disable-next-line no-any
2728
public createConnection(...args: any[]): net.Socket {
2829
//@ts-ignore
2930
return this.client.createConnection(...args) as net.Socket;

packages/protocol/src/common/connection.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,4 +20,8 @@ export interface InitData {
2020
readonly workingDirectory: string;
2121
readonly homeDirectory: string;
2222
readonly tmpDirectory: string;
23-
}
23+
}
24+
25+
export interface ISharedProcessData {
26+
readonly socketPath: string;
27+
}

packages/server/src/cli.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,11 @@ export class Entry extends Command {
117117
app.wss.on("connection", (ws, req) => {
118118
const id = clientId++;
119119

120-
if (sharedProcess.state === SharedProcessState.Ready) {
121-
sendSharedProcessReady(ws);
122-
}
120+
ws.on("open", () => {
121+
if (sharedProcess.state === SharedProcessState.Ready) {
122+
sendSharedProcessReady(ws);
123+
}
124+
});
123125

124126
logger.info(`WebSocket opened \u001B[0m${req.url}`, field("client", id), field("ip", req.socket.remoteAddress));
125127

packages/server/src/server.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,6 @@ export const createApp = (registerMiddleware?: (app: express.Application) => voi
2525
};
2626

2727
wss.on("connection", (ws: WebSocket, req) => {
28-
const spm = (<any>req).sharedProcessInit as SharedProcessInitMessage;
29-
if (!spm) {
30-
ws.close();
31-
return;
32-
}
33-
3428
const connection: ReadWriteConnection = {
3529
onMessage: (cb): void => {
3630
ws.addEventListener("message", (event) => cb(event.data));
@@ -44,7 +38,7 @@ export const createApp = (registerMiddleware?: (app: express.Application) => voi
4438
...options,
4539
forkProvider: (message: NewSessionMessage): ChildProcess => {
4640
let proc: ChildProcess;
47-
41+
4842
if (message.getIsBootstrapFork()) {
4943
proc = forkModule(message.getCommand());
5044
} else {

packages/vscode/src/client.ts

Lines changed: 48 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,55 @@
11
import "./fill/require";
22
import "./fill/storageDatabase";
33
import "./fill/windowsService";
4+
import * as paths from "./fill/paths";
5+
import "./fill/dom";
6+
import "./vscode.scss";
47

5-
import { fork } from "child_process";
8+
import { createConnection } from "net";
69
import { Client as IDEClient, IURI, IURIFactory } from "@coder/ide";
7-
import { logger } from "@coder/logger";
810

911
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
1012
import { LogLevel } from "vs/platform/log/common/log";
1113
import { toLocalISOString } from "vs/base/common/date";
1214
// import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
1315
import { URI } from "vs/base/common/uri";
1416

15-
import { Protocol, ISharedProcessInitData } from "./protocol";
16-
import * as paths from "./fill/paths";
17-
import "./firefox";
17+
import { Protocol } from "vs/base/parts/ipc/node/ipc.net";
1818

1919
export class Client extends IDEClient {
2020

21-
private readonly sharedProcessLogger = logger.named("shr proc");
2221
private readonly windowId = parseInt(toLocalISOString(new Date()).replace(/[-:.TZ]/g, ""), 10);
23-
private readonly version = "hello"; // TODO: pull from package.json probably
24-
private readonly bootstrapForkLocation = "/bootstrap"; // TODO: location.
22+
2523
public readonly protocolPromise: Promise<Protocol>;
26-
private protoResolve: ((protocol: Protocol) => void) | undefined;
24+
public protoResolve: ((protocol: Protocol) => void) | undefined;
2725

2826
public constructor() {
2927
super();
30-
process.env.VSCODE_LOGS = "/tmp/vscode-online/logs"; // TODO: use tmpdir or get log directory from init data.
3128
this.protocolPromise = new Promise((resolve): void => {
3229
this.protoResolve = resolve;
3330
});
3431
}
3532

3633
protected initialize(): Promise<void> {
37-
this.task("Start shared process", 5, async () => {
38-
const protocol = await this.forkSharedProcess();
39-
this.protoResolve!(protocol);
34+
this.task("Connect to shared process", 5, async () => {
35+
await new Promise((resolve, reject): void => {
36+
const listener = this.onSharedProcessActive((data) => {
37+
listener.dispose();
38+
const socket = createConnection(data.socketPath, resolve);
39+
socket.once("error", () => {
40+
reject();
41+
});
42+
this.protoResolve!(new Protocol(socket));
43+
});
44+
});
4045
}).catch(() => undefined);
4146

4247
registerContextMenuListener();
4348

4449
return this.task("Start workbench", 1000, async (initData) => {
50+
paths.paths.appData = initData.dataDirectory;
51+
paths.paths.defaultUserData = initData.dataDirectory;
52+
4553
const { startup } = require("./startup");
4654
await startup({
4755
machineId: "1",
@@ -57,6 +65,33 @@ export class Client extends IDEClient {
5765
folderUri: URI.file(initData.dataDirectory),
5866
});
5967

68+
// TODO: Set notification service for retrying.
69+
// this.retry.setNotificationService({
70+
// prompt: (severity, message, buttons, onCancel) => {
71+
// const handle = getNotificationService().prompt(severity, message, buttons, onCancel);
72+
// return {
73+
// close: () => handle.close(),
74+
// updateMessage: (message) => handle.updateMessage(message),
75+
// updateButtons: (buttons) => handle.updateActions({
76+
// primary: buttons.map((button) => ({
77+
// id: undefined,
78+
// label: button.label,
79+
// tooltip: undefined,
80+
// class: undefined,
81+
// enabled: true,
82+
// checked: false,
83+
// radio: false,
84+
// dispose: () => undefined,
85+
// run: () => {
86+
// button.run();
87+
// return Promise.resolve();
88+
// },
89+
// })),
90+
// }),
91+
// };
92+
// }
93+
// });
94+
6095
// TODO: Set up clipboard context.
6196
// const workbench = workbenchShell.workbench;
6297
// const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService;
@@ -69,50 +104,6 @@ export class Client extends IDEClient {
69104
}, this.initData);
70105
}
71106

72-
public async forkSharedProcess(): Promise<Protocol> {
73-
const childProcess = fork(this.bootstrapForkLocation, ["--shared"], {
74-
env: {
75-
"VSCODE_ALLOW_IO": "true",
76-
"AMD_ENTRYPOINT": "vs/code/electron-browser/sharedProcess/sharedProcessClient",
77-
},
78-
});
79-
80-
childProcess.stderr.on("data", (data) => {
81-
this.sharedProcessLogger.error("stderr: " + data);
82-
});
83-
84-
const protocol = Protocol.fromProcess(childProcess);
85-
await new Promise((resolve, reject): void => {
86-
protocol.onClose(() => {
87-
reject(new Error("unable to establish connection to shared process"));
88-
});
89-
90-
const listener = protocol.onMessage((message) => {
91-
const messageStr = message.toString();
92-
this.sharedProcessLogger.debug(messageStr);
93-
switch (messageStr) {
94-
case "handshake:hello":
95-
protocol.send(Buffer.from(JSON.stringify({
96-
// Using the version so if we get a new mount, it spins up a new
97-
// shared process.
98-
socketPath: `/tmp/vscode-online/shared-${this.version}.sock`,
99-
serviceUrl: "", // TODO
100-
logsDir: process.env.VSCODE_LOGS,
101-
windowId: this.windowId,
102-
logLevel: LogLevel.Info,
103-
} as ISharedProcessInitData)));
104-
break;
105-
case "handshake:ready":
106-
listener.dispose();
107-
resolve();
108-
break;
109-
}
110-
});
111-
});
112-
113-
return protocol;
114-
}
115-
116107
protected createUriFactory(): IURIFactory {
117108
return {
118109
// TODO: not sure why this is an error.
@@ -126,8 +117,3 @@ export class Client extends IDEClient {
126117
}
127118

128119
export const client = new Client();
129-
130-
client.initData.then((initData) => {
131-
paths.appData = initData.dataDirectory;
132-
paths.defaultUserData = initData.dataDirectory;
133-
});
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import "./firefox.scss";
2-
1+
// Firefox has no implementation of toElement.
32
if (!("toElement" in MouseEvent.prototype)) {
43
Object.defineProperty(MouseEvent.prototype, "toElement", {
54
get: function (): EventTarget | null {

packages/vscode/src/fill/paths.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
const paths = {
1+
export const paths = {
22
appData: "/tmp",
33
defaultUserData: "/tmp",
44
};

0 commit comments

Comments
 (0)