Skip to content

Commit

Permalink
Hacked out cluster mode
Browse files Browse the repository at this point in the history
Signed-off-by: Rob Moran <rob.moran@arm.com>
  • Loading branch information
thegecko committed Apr 10, 2019
1 parent 844e686 commit 36eba77
Show file tree
Hide file tree
Showing 22 changed files with 169 additions and 847 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Breaking changes:

- [node] moved to using Node.js version 10, dropping support for Node.js version 8.
- [electron] removed cluster mode and startup timeout setting
- [dialog] `validate` and `accept` methods are now Promisified [#4764](https://github.com/theia-ide/theia/pull/4764)
- [editor] turn off autoSave by default to align with VS Code [#4777](https://github.com/theia-ide/theia/pull/4777)
- default settings can be overriden in application package.json:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ const { BackendApplicationConfigProvider } = require('@theia/core/lib/node/backe
BackendApplicationConfigProvider.set(${this.prettyStringify(this.pck.props.backend.config)});
const serverPath = require('path').resolve(__dirname, 'server');
const address = require('@theia/core/lib/node/cluster/main').default(serverPath);
const address = require('@theia/core/lib/node/main').default(serverPath);
address.then(function (address) {
if (process && process.send) {
process.send(address.port.toString());
Expand Down
284 changes: 139 additions & 145 deletions dev-packages/application-manager/src/generator/frontend-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,167 +124,161 @@ process.env.LC_NUMERIC = 'C';
const electron = require('electron');
const { join, resolve } = require('path');
const { isMaster } = require('cluster');
const { fork } = require('child_process');
const { app, shell, BrowserWindow, ipcMain, Menu } = electron;
const applicationName = \`${this.pck.props.frontend.config.applicationName}\`;
if (isMaster) {
const Storage = require('electron-store');
const electronStore = new Storage();
app.on('ready', () => {
const { screen } = electron;
// Remove the default electron menus, waiting for the application to set its own.
Menu.setApplicationMenu(Menu.buildFromTemplate([{
role: 'help', submenu: [{ role: 'toggledevtools'}]
}]));
function createNewWindow(theUrl) {
// We must center by hand because \`browserWindow.center()\` fails on multi-screen setups
// See: https://github.com/electron/electron/issues/3490
const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint());
const height = Math.floor(bounds.height * (2/3));
const width = Math.floor(bounds.width * (2/3));
const y = Math.floor(bounds.y + (bounds.height - height) / 2);
const x = Math.floor(bounds.x + (bounds.width - width) / 2);
const WINDOW_STATE = 'windowstate';
const windowState = electronStore.get(WINDOW_STATE, {
width, height, x, y
});
let windowOptions = {
show: false,
title: applicationName,
width: windowState.width,
height: windowState.height,
minWidth: 200,
minHeight: 120,
x: windowState.x,
y: windowState.y,
isMaximized: windowState.isMaximized
};
// Always hide the window, we will show the window when it is ready to be shown in any case.
const newWindow = new BrowserWindow(windowOptions);
if (windowOptions.isMaximized) {
newWindow.maximize();
}
newWindow.on('ready-to-show', () => newWindow.show());
// Prevent calls to "window.open" from opening an ElectronBrowser window,
// and rather open in the OS default web browser.
newWindow.webContents.on('new-window', (event, url) => {
event.preventDefault();
shell.openExternal(url);
});
// Save the window geometry state on every change
const saveWindowState = () => {
try {
let bounds;
if (newWindow.isMaximized()) {
bounds = electronStore.get(WINDOW_STATE, {});
} else {
bounds = newWindow.getBounds();
}
electronStore.set(WINDOW_STATE, {
isMaximized: newWindow.isMaximized(),
width: bounds.width,
height: bounds.height,
x: bounds.x,
y: bounds.y
});
} catch (e) {
console.error("Error while saving window state.", e);
}
};
let delayedSaveTimeout;
const saveWindowStateDelayed = () => {
if (delayedSaveTimeout) {
clearTimeout(delayedSaveTimeout);
}
delayedSaveTimeout = setTimeout(saveWindowState, 1000);
};
newWindow.on('close', saveWindowState);
newWindow.on('resize', saveWindowStateDelayed);
newWindow.on('move', saveWindowStateDelayed);
if (!!theUrl) {
newWindow.loadURL(theUrl);
}
return newWindow;
}
const Storage = require('electron-store');
const electronStore = new Storage();
app.on('window-all-closed', () => {
app.quit();
});
ipcMain.on('create-new-window', (event, url) => {
createNewWindow(url);
app.on('ready', () => {
const { screen } = electron;
// Remove the default electron menus, waiting for the application to set its own.
Menu.setApplicationMenu(Menu.buildFromTemplate([{
role: 'help', submenu: [{ role: 'toggledevtools'}]
}]));
function createNewWindow(theUrl) {
// We must center by hand because \`browserWindow.center()\` fails on multi-screen setups
// See: https://github.com/electron/electron/issues/3490
const { bounds } = screen.getDisplayNearestPoint(screen.getCursorScreenPoint());
const height = Math.floor(bounds.height * (2/3));
const width = Math.floor(bounds.width * (2/3));
const y = Math.floor(bounds.y + (bounds.height - height) / 2);
const x = Math.floor(bounds.x + (bounds.width - width) / 2);
const WINDOW_STATE = 'windowstate';
const windowState = electronStore.get(WINDOW_STATE, {
width, height, x, y
});
ipcMain.on('open-external', (event, url) => {
let windowOptions = {
show: false,
title: applicationName,
width: windowState.width,
height: windowState.height,
minWidth: 200,
minHeight: 120,
x: windowState.x,
y: windowState.y,
isMaximized: windowState.isMaximized
};
// Always hide the window, we will show the window when it is ready to be shown in any case.
const newWindow = new BrowserWindow(windowOptions);
if (windowOptions.isMaximized) {
newWindow.maximize();
}
newWindow.on('ready-to-show', () => newWindow.show());
// Prevent calls to "window.open" from opening an ElectronBrowser window,
// and rather open in the OS default web browser.
newWindow.webContents.on('new-window', (event, url) => {
event.preventDefault();
shell.openExternal(url);
});
// Check whether we are in bundled application or development mode.
// @ts-ignore
const devMode = process.defaultApp || /node_modules[\/]electron[\/]/.test(process.execPath);
const mainWindow = createNewWindow();
const loadMainWindow = (port) => {
if (!mainWindow.isDestroyed()) {
mainWindow.loadURL('file://' + join(__dirname, '../../lib/index.html') + '?port=' + port);
// Save the window geometry state on every change
const saveWindowState = () => {
try {
let bounds;
if (newWindow.isMaximized()) {
bounds = electronStore.get(WINDOW_STATE, {});
} else {
bounds = newWindow.getBounds();
}
electronStore.set(WINDOW_STATE, {
isMaximized: newWindow.isMaximized(),
width: bounds.width,
height: bounds.height,
x: bounds.x,
y: bounds.y
});
} catch (e) {
console.error("Error while saving window state.", e);
}
};
let delayedSaveTimeout;
const saveWindowStateDelayed = () => {
if (delayedSaveTimeout) {
clearTimeout(delayedSaveTimeout);
}
delayedSaveTimeout = setTimeout(saveWindowState, 1000);
};
newWindow.on('close', saveWindowState);
newWindow.on('resize', saveWindowStateDelayed);
newWindow.on('move', saveWindowStateDelayed);
// We cannot use the \`process.cwd()\` as the application project path (the location of the \`package.json\` in other words)
// in a bundled electron application because it depends on the way we start it. For instance, on OS X, these are a differences:
// https://github.com/theia-ide/theia/issues/3297#issuecomment-439172274
process.env.THEIA_APP_PROJECT_PATH = resolve(__dirname, '..', '..');
// Set the electron version for both the dev and the production mode. (https://github.com/theia-ide/theia/issues/3254)
// Otherwise, the forked backend processes will not know that they're serving the electron frontend.
const { versions } = process;
// @ts-ignore
if (versions && typeof versions.electron !== 'undefined') {
// @ts-ignore
process.env.THEIA_ELECTRON_VERSION = versions.electron;
if (!!theUrl) {
newWindow.loadURL(theUrl);
}
return newWindow;
}
const mainPath = join(__dirname, '..', 'backend', 'main');
// We need to distinguish between bundled application and development mode when starting the clusters.
// See: https://github.com/electron/electron/issues/6337#issuecomment-230183287
if (devMode) {
require(mainPath).then(address => {
loadMainWindow(address.port);
}).catch((error) => {
console.error(error);
app.exit(1);
});
} else {
const cp = fork(mainPath, [], { env: Object.assign({}, process.env) });
cp.on('message', (message) => {
loadMainWindow(message);
});
cp.on('error', (error) => {
console.error(error);
app.exit(1);
});
app.on('quit', () => {
// If we forked the process for the clusters, we need to manually terminate it.
// See: https://github.com/theia-ide/theia/issues/835
process.kill(cp.pid);
});
}
app.on('window-all-closed', () => {
app.quit();
});
} else {
require('../backend/main');
}
ipcMain.on('create-new-window', (event, url) => {
createNewWindow(url);
});
ipcMain.on('open-external', (event, url) => {
shell.openExternal(url);
});
// Check whether we are in bundled application or development mode.
// @ts-ignore
const devMode = process.defaultApp || /node_modules[\/]electron[\/]/.test(process.execPath);
const mainWindow = createNewWindow();
const loadMainWindow = (port) => {
if (!mainWindow.isDestroyed()) {
mainWindow.loadURL('file://' + join(__dirname, '../../lib/index.html') + '?port=' + port);
}
};
// We cannot use the \`process.cwd()\` as the application project path (the location of the \`package.json\` in other words)
// in a bundled electron application because it depends on the way we start it. For instance, on OS X, these are a differences:
// https://github.com/theia-ide/theia/issues/3297#issuecomment-439172274
process.env.THEIA_APP_PROJECT_PATH = resolve(__dirname, '..', '..');
// Set the electron version for both the dev and the production mode. (https://github.com/theia-ide/theia/issues/3254)
// Otherwise, the forked backend processes will not know that they're serving the electron frontend.
const { versions } = process;
// @ts-ignore
if (versions && typeof versions.electron !== 'undefined') {
// @ts-ignore
process.env.THEIA_ELECTRON_VERSION = versions.electron;
}
const mainPath = join(__dirname, '..', 'backend', 'main');
// We need to distinguish between bundled application and development mode when starting the clusters.
// See: https://github.com/electron/electron/issues/6337#issuecomment-230183287
if (devMode) {
require(mainPath).then(address => {
loadMainWindow(address.port);
}).catch((error) => {
console.error(error);
app.exit(1);
});
} else {
const cp = fork(mainPath, [], { env: Object.assign({}, process.env) });
cp.on('message', (message) => {
loadMainWindow(message);
});
cp.on('error', (error) => {
console.error(error);
app.exit(1);
});
app.on('quit', () => {
// If we forked the process for the clusters, we need to manually terminate it.
// See: https://github.com/theia-ide/theia/issues/835
process.kill(cp.pid);
});
}
});
`;
}

Expand Down
7 changes: 0 additions & 7 deletions dev-packages/application-package/src/application-props.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,13 +116,6 @@ export interface FrontendApplicationConfig extends ApplicationConfig {
*/
export interface BackendApplicationConfig extends ApplicationConfig {

/**
* The default backend startup timeout in milliseconds. If specified here, then this value will be used as opposed to the default timeout.
* If the `--startup-timeout` option is defined, this configuration value has no effect. A negative values will disable the timeout.
* For more details, see [`startupTimeoutOption`](MasterProcess#startupTimeoutOption).
*/
readonly startupTimeout?: number;

}

/**
Expand Down
11 changes: 1 addition & 10 deletions packages/core/src/node/backend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,14 @@
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/

import { ContainerModule, interfaces, decorate, injectable } from 'inversify';
import { ContainerModule, decorate, injectable } from 'inversify';
import { ApplicationPackage } from '@theia/application-package';
import {
bindContributionProvider, MessageService, MessageClient, ConnectionHandler, JsonRpcConnectionHandler,
CommandService, commandServicePath, messageServicePath
} from '../common';
import { BackendApplication, BackendApplicationContribution, BackendApplicationCliContribution } from './backend-application';
import { CliManager, CliContribution } from './cli';
import { ServerProcess, RemoteMasterProcessFactory, clusterRemoteMasterProcessFactory } from './cluster';
import { IPCConnectionProvider } from './messaging';
import { ApplicationServerImpl } from './application-server';
import { ApplicationServer, applicationPath } from '../common/application-protocol';
Expand All @@ -33,12 +32,6 @@ import { QuickPickService, quickPickServicePath } from '../common/quick-pick-ser

decorate(injectable(), ApplicationPackage);

export function bindServerProcess(bind: interfaces.Bind, masterFactory: RemoteMasterProcessFactory): void {
bind(RemoteMasterProcessFactory).toConstantValue(masterFactory);
bind(ServerProcess).toSelf().inSingletonScope();
bind(BackendApplicationContribution).toService(ServerProcess);
}

const commandConnectionModule = ConnectionContainerModule.create(({ bindFrontendService }) => {
bindFrontendService(commandServicePath, CommandService);
});
Expand Down Expand Up @@ -66,8 +59,6 @@ export const backendApplicationModule = new ContainerModule(bind => {
bind(BackendApplication).toSelf().inSingletonScope();
bindContributionProvider(bind, BackendApplicationContribution);

bindServerProcess(bind, clusterRemoteMasterProcessFactory);

bind(IPCConnectionProvider).toSelf().inSingletonScope();

bind(ApplicationServerImpl).toSelf().inSingletonScope();
Expand Down
Loading

0 comments on commit 36eba77

Please sign in to comment.