ELECTRON-730 (Implement aggressive refresh to optimize refresh) (#488)

* ELECTRON-730 - Implement aggressive refresh to optimize refresh

* ELECTRON-730 - Increase memory threshold to 800MB
This commit is contained in:
Kiran Niranjan 2018-09-01 16:35:53 +05:30 committed by Vishwas Shashidhar
parent c2ce4e9120
commit 96707907c7
6 changed files with 168 additions and 30 deletions

View File

@ -21,6 +21,7 @@ const cmds = keyMirror({
openScreenPickerWindow: null, openScreenPickerWindow: null,
popupMenu: null, popupMenu: null,
optimizeMemoryConsumption: null, optimizeMemoryConsumption: null,
optimizeMemoryRegister: null,
setIsInMeeting: null, setIsInMeeting: null,
setLocale: null, setLocale: null,
keyPress: null, keyPress: null,

View File

@ -10,6 +10,7 @@ const squirrelStartup = require('electron-squirrel-startup');
const urlParser = require('url'); const urlParser = require('url');
const nodePath = require('path'); const nodePath = require('path');
const compareSemVersions = require('./utils/compareSemVersions.js'); const compareSemVersions = require('./utils/compareSemVersions.js');
const eventEmitter = require('./eventEmitter');
// Local Dependencies // Local Dependencies
const { const {
@ -185,6 +186,12 @@ setChromeFlags();
* Some APIs can only be used after this event occurs. * Some APIs can only be used after this event occurs.
*/ */
app.on('ready', () => { app.on('ready', () => {
electron.powerMonitor.on('lock-screen', () => {
eventEmitter.emit('sys-locked');
});
electron.powerMonitor.on('unlock-screen', () => {
eventEmitter.emit('sys-unlocked');
});
checkFirstTimeLaunch() checkFirstTimeLaunch()
.then(readConfigThenOpenMainWindow) .then(readConfigThenOpenMainWindow)
.catch(readConfigThenOpenMainWindow); .catch(readConfigThenOpenMainWindow);

View File

@ -17,7 +17,7 @@ const { bringToFront } = require('./bringToFront.js');
const eventEmitter = require('./eventEmitter'); const eventEmitter = require('./eventEmitter');
const { isMac } = require('./utils/misc'); const { isMac } = require('./utils/misc');
const { openScreenPickerWindow } = require('./desktopCapturer'); const { openScreenPickerWindow } = require('./desktopCapturer');
const { optimizeMemory, setIsInMeeting } = require('./memoryMonitor'); const { setPreloadMemoryInfo, setIsInMeeting, setPreloadWindow } = require('./memoryMonitor');
const apiEnums = require('./enums/api.js'); const apiEnums = require('./enums/api.js');
const apiCmds = apiEnums.cmds; const apiCmds = apiEnums.cmds;
@ -153,9 +153,12 @@ electron.ipcMain.on(apiName, (event, arg) => {
} }
case apiCmds.optimizeMemoryConsumption: case apiCmds.optimizeMemoryConsumption:
if (typeof arg.memory === 'object' && typeof arg.cpuUsage === 'object' && typeof arg.memory.workingSetSize === 'number') { if (typeof arg.memory === 'object' && typeof arg.cpuUsage === 'object' && typeof arg.memory.workingSetSize === 'number') {
optimizeMemory(arg.memory, arg.cpuUsage); setPreloadMemoryInfo(arg.memory, arg.cpuUsage);
} }
break; break;
case apiCmds.optimizeMemoryRegister:
setPreloadWindow(event.sender);
break;
case apiCmds.setIsInMeeting: case apiCmds.setIsInMeeting:
if (typeof arg.isInMeeting === 'boolean') { if (typeof arg.isInMeeting === 'boolean') {
setIsInMeeting(arg.isInMeeting); setIsInMeeting(arg.isInMeeting);

View File

@ -1,15 +1,23 @@
'use strict'; 'use strict';
const eventEmitter = require('./eventEmitter');
const log = require('./log.js'); const log = require('./log.js');
const logLevels = require('./enums/logLevels.js'); const logLevels = require('./enums/logLevels.js');
const { getMainWindow, setIsAutoReload } = require('./windowMgr'); const { getMainWindow, setIsAutoReload, getIsOnline } = require('./windowMgr');
const systemIdleTime = require('@paulcbetts/system-idle-time');
const { getConfigField } = require('./config'); const { getConfigField } = require('./config');
const maxMemory = 800; const maxMemory = 800;
const defaultInterval = 30 * 1000;
const memoryRefreshThreshold = 60 * 60 * 1000;
const cpuUsageThreshold = 5;
let maxIdleTime = 4 * 60 * 60 * 1000;
let isInMeeting = false; let isInMeeting = false;
let canReload = true;
let appMinimizedTimer;
let powerMonitorTimer;
let preloadMemory;
let preloadWindow;
// once a minute // once a minute
setInterval(gatherMemory, 1000 * 60); setInterval(gatherMemory, 1000 * 60);
@ -31,16 +39,22 @@ function gatherMemory() {
* Method that checks memory usage every minute * Method that checks memory usage every minute
* and verify if the user in inactive if so it reloads the * and verify if the user in inactive if so it reloads the
* application to free up some memory consumption * application to free up some memory consumption
*
* @param memoryInfo
* @param cpuUsage
*/ */
function optimizeMemory(memoryInfo, cpuUsage) { function optimizeMemory() {
const memoryConsumed = (memoryInfo && memoryInfo.workingSetSize / 1024) || 0;
if (!preloadMemory || !preloadMemory.memoryInfo || !preloadMemory.cpuUsage) {
log.send(logLevels.INFO, `Memory info not available`);
return;
}
const memoryConsumed = (preloadMemory.memoryInfo && preloadMemory.memoryInfo.workingSetSize / 1024) || 0;
const cpuUsagePercentage = preloadMemory.cpuUsage.percentCPUUsage;
if (memoryConsumed > maxMemory if (memoryConsumed > maxMemory
&& systemIdleTime.getIdleTime() > maxIdleTime && cpuUsagePercentage <= cpuUsageThreshold
&& !isInMeeting && !isInMeeting
&& getIsOnline()
&& canReload
) { ) {
getConfigField('memoryRefresh') getConfigField('memoryRefresh')
.then((enabled) => { .then((enabled) => {
@ -49,15 +63,29 @@ function optimizeMemory(memoryInfo, cpuUsage) {
if (mainWindow && !mainWindow.isDestroyed()) { if (mainWindow && !mainWindow.isDestroyed()) {
setIsAutoReload(true); setIsAutoReload(true);
log.send(logLevels.INFO, 'Reloading the app to optimize memory usage as' + log.send(logLevels.INFO, `Reloading the app to optimize memory usage as
' memory consumption was ' + memoryConsumed + memory consumption was ${memoryConsumed}
' CPU usage percentage was ' + cpuUsage.percentCPUUsage + CPU usage percentage was ${preloadMemory.cpuUsage.percentCPUUsage}
' user activity tick was ' + systemIdleTime.getIdleTime() + user was in a meeting? ${isInMeeting}
' user was in a meeting? ' + isInMeeting ); is network online? ${getIsOnline()}`);
mainWindow.reload(); mainWindow.reload();
// do not refresh for another 1hrs
canReload = false;
setTimeout(() => {
canReload = true;
}, memoryRefreshThreshold);
} }
} else {
log.send(logLevels.INFO, `Memory refresh not enabled by the user so Not Reloading the app`);
} }
}); });
} else {
log.send(logLevels.INFO, `Not Reloading the app as
memory consumption was ${memoryConsumed}
CPU usage percentage was ${preloadMemory.cpuUsage.percentCPUUsage}
user was in a meeting? ${isInMeeting}
is network online? ${getIsOnline()}`);
} }
} }
@ -69,7 +97,93 @@ function setIsInMeeting(meetingStatus) {
isInMeeting = meetingStatus; isInMeeting = meetingStatus;
} }
/**
* Sets preload memory info and calls optimize memory func
*
* @param memoryInfo
* @param cpuUsage
*/
function setPreloadMemoryInfo(memoryInfo, cpuUsage) {
log.send(logLevels.INFO, 'Memory info received from preload process now running optimize memory logic');
preloadMemory = { memoryInfo, cpuUsage };
optimizeMemory();
}
/**
* Called whenever the application is minimized
* and waits for 30s to optimize memory
*/
eventEmitter.on('appMinimized', () => {
appMinimizedTimer = setTimeout(() => {
const mainWindow = getMainWindow();
if (mainWindow && !mainWindow.isDestroyed() && mainWindow.isMinimized()) {
log.send(logLevels.INFO, 'Application is minimised for more than 30s so calling requestMemoryInfo');
requestMemoryInfo();
}
}, defaultInterval);
});
/**
* Called whenever the application is restored from minimized state
*
* Clears appMinimizedTimer if the app is restored within 30s
* from minimized state
*/
eventEmitter.on('appRestored', () => {
log.send(logLevels.INFO, 'Application was restored from minimized state');
if (appMinimizedTimer) {
clearTimeout(appMinimizedTimer);
}
});
/**
* Called whenever the system in locked
* and waits for 30s to optimize memory
*/
eventEmitter.on('sys-locked', () => {
log.send(logLevels.INFO, 'System screen was locked');
powerMonitorTimer = setTimeout(() => {
log.send(logLevels.INFO, 'System screen was locked for more than 30s so calling requestMemoryInfo');
requestMemoryInfo();
}, defaultInterval);
});
/**
* Called whenever the system in locked
*
* Clears powerMonitorTimer if the system is unlocked within 30s
* from locked state
*/
eventEmitter.on('sys-unlocked', () => {
log.send(logLevels.INFO, 'System screen was unlocked');
if (powerMonitorTimer) {
clearTimeout(powerMonitorTimer);
}
});
/**
* Sets the preload window
*
* @param win - preload window
*/
function setPreloadWindow(win) {
log.send(logLevels.INFO, 'Preload window registered');
preloadWindow = win;
}
/**
* Request memory info from the registered preload window
* which invokes the optimize memory func
*/
function requestMemoryInfo() {
if (preloadWindow) {
log.send(logLevels.INFO, 'Requesting memory information from the preload script');
preloadWindow.send('memory-info-request');
}
}
module.exports = { module.exports = {
optimizeMemory, setIsInMeeting,
setIsInMeeting setPreloadMemoryInfo,
setPreloadWindow,
}; };

View File

@ -22,7 +22,6 @@ const getMediaSource = require('../desktopCapturer/getSource');
const { TitleBar } = require('../windowsTitlebar'); const { TitleBar } = require('../windowsTitlebar');
const titleBar = new TitleBar(); const titleBar = new TitleBar();
const { buildNumber } = require('../../package.json'); const { buildNumber } = require('../../package.json');
const memoryMonitorInterval = 1000 * 60 * 60;
const SnackBar = require('../snackBar').SnackBar; const SnackBar = require('../snackBar').SnackBar;
const KeyCodes = { const KeyCodes = {
Esc: 27, Esc: 27,
@ -105,6 +104,13 @@ const throttledSetIsInMeetingStatus = throttle(1000, function (isInMeeting) {
local.ipcRenderer.on('on-page-load', () => { local.ipcRenderer.on('on-page-load', () => {
loadSpellChecker(); loadSpellChecker();
snackBar = new SnackBar(); snackBar = new SnackBar();
// only registers main window's preload
if (window.name === 'main') {
local.ipcRenderer.send(apiName, {
cmd: apiCmds.optimizeMemoryRegister,
});
}
}); });
const throttledActivate = throttle(1000, function (windowName) { const throttledActivate = throttle(1000, function (windowName) {
@ -129,17 +135,6 @@ const throttledSetLocale = throttle(1000, function (locale) {
}); });
}); });
// Gathers renderer process memory
setInterval(() => {
const memory = process.getProcessMemoryInfo();
const cpuUsage = process.getCPUUsage();
local.ipcRenderer.send(apiName, {
cmd: apiCmds.optimizeMemoryConsumption,
memory: memory,
cpuUsage: cpuUsage
});
}, memoryMonitorInterval);
createAPI(); createAPI();
// creates API exposed from electron. // creates API exposed from electron.
@ -519,6 +514,18 @@ function createAPI() {
} }
}); });
local.ipcRenderer.on('memory-info-request', () => {
if (window.name === 'main') {
const memory = process.getProcessMemoryInfo();
const cpuUsage = process.getCPUUsage();
local.ipcRenderer.send(apiName, {
cmd: apiCmds.optimizeMemoryConsumption,
memory: memory,
cpuUsage: cpuUsage
});
}
});
function updateOnlineStatus() { function updateOnlineStatus() {
local.ipcRenderer.send(apiName, { local.ipcRenderer.send(apiName, {
cmd: apiCmds.isOnline, cmd: apiCmds.isOnline,

View File

@ -216,6 +216,12 @@ function doCreateMainWindow(initialUrl, initialBounds, isCustomTitleBar) {
// event sent to renderer process to remove snack bar // event sent to renderer process to remove snack bar
mainWindow.webContents.send('window-leave-full-screen'); mainWindow.webContents.send('window-leave-full-screen');
}); });
mainWindow.on('minimize', () => {
eventEmitter.emit('appMinimized');
});
mainWindow.on('restore', () => {
eventEmitter.emit('appRestored');
});
if (initialBounds) { if (initialBounds) {
// maximizes the application if previously maximized // maximizes the application if previously maximized