Skip to content

Commit 949f9a4

Browse files
Merge pull request #4322 from NativeScript/vladimirov/fix-high-cpu-usage
fix: high cpu usage during livesync
2 parents d70fe8b + 2dab797 commit 949f9a4

File tree

2 files changed

+91
-62
lines changed

2 files changed

+91
-62
lines changed

lib/common/mobile/mobile-core/devices-service.ts

+48-33
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { performanceLog } from "../../decorators";
1313

1414
export class DevicesService extends EventEmitter implements Mobile.IDevicesService {
1515
private static DEVICE_LOOKING_INTERVAL = 200;
16+
private static EMULATOR_IMAGES_DETECTION_INTERVAL = 60 * 1000;
1617
private _devices: IDictionary<Mobile.IDevice> = {};
1718
private _availableEmulators: IDictionary<Mobile.IDeviceInfo> = {};
1819
private _platform: string;
@@ -21,8 +22,7 @@ export class DevicesService extends EventEmitter implements Mobile.IDevicesServi
2122
private _data: Mobile.IDevicesServicesInitializationOptions;
2223
private _otherDeviceDiscoveries: Mobile.IDeviceDiscovery[] = [];
2324
private _allDeviceDiscoveries: Mobile.IDeviceDiscovery[] = [];
24-
private deviceDetectionInterval: any;
25-
private isDeviceDetectionIntervalInProgress: boolean;
25+
private deviceDetectionIntervals: NodeJS.Timer[] = [];
2626

2727
constructor(private $logger: ILogger,
2828
private $errors: IErrors,
@@ -287,43 +287,51 @@ export class DevicesService extends EventEmitter implements Mobile.IDevicesServi
287287
}
288288
}
289289

290-
protected async startDeviceDetectionInterval(deviceInitOpts: Mobile.IDevicesServicesInitializationOptions = {}): Promise<void> {
290+
protected async startDeviceDetectionIntervals(deviceInitOpts: Mobile.IDevicesServicesInitializationOptions = {}): Promise<void> {
291291
this.$processService.attachToProcessExitSignals(this, this.clearDeviceDetectionInterval);
292292

293-
if (this.deviceDetectionInterval) {
294-
this.$logger.trace("Device detection interval is already started. New Interval will not be started.");
293+
if (this.deviceDetectionIntervals.length) {
294+
this.$logger.trace("Device detection intervals are already started. New intervals will not be started.");
295295
return;
296296
}
297-
let isFirstExecution = true;
298297

299-
return new Promise<void>((resolve, reject) => {
300-
this.deviceDetectionInterval = setInterval(async () => {
301-
if (this.isDeviceDetectionIntervalInProgress) {
302-
return;
303-
}
298+
let isDeviceDetectionIntervalInProgress = false;
299+
const deviceDetectionInterval = setInterval(async () => {
300+
if (isDeviceDetectionIntervalInProgress) {
301+
return;
302+
}
304303

305-
this.isDeviceDetectionIntervalInProgress = true;
304+
isDeviceDetectionIntervalInProgress = true;
306305

307-
await this.detectCurrentlyAttachedDevices(deviceInitOpts);
308-
await this.detectCurrentlyAvailableEmulators();
306+
await this.detectCurrentlyAttachedDevices(deviceInitOpts);
309307

310-
try {
311-
const trustedDevices = _.filter(this._devices, device => device.deviceInfo.status === constants.CONNECTED_STATUS);
312-
await settlePromises(_.map(trustedDevices, device => device.applicationManager.checkForApplicationUpdates()));
313-
} catch (err) {
314-
this.$logger.trace("Error checking for application updates on devices.", err);
315-
}
308+
try {
309+
const trustedDevices = _.filter(this._devices, device => device.deviceInfo.status === constants.CONNECTED_STATUS);
310+
await settlePromises(_.map(trustedDevices, device => device.applicationManager.checkForApplicationUpdates()));
311+
} catch (err) {
312+
this.$logger.trace("Error checking for application updates on devices.", err);
313+
}
316314

317-
if (isFirstExecution) {
318-
isFirstExecution = false;
319-
resolve();
320-
this.deviceDetectionInterval.unref();
321-
}
315+
isDeviceDetectionIntervalInProgress = false;
322316

323-
this.isDeviceDetectionIntervalInProgress = false;
317+
}, DevicesService.DEVICE_LOOKING_INTERVAL);
324318

325-
}, DevicesService.DEVICE_LOOKING_INTERVAL);
326-
});
319+
deviceDetectionInterval.unref();
320+
this.deviceDetectionIntervals.push(deviceDetectionInterval);
321+
322+
let isEmulatorDetectionIntervalRunning = false;
323+
const emulatorDetectionInterval = setInterval(async () => {
324+
if (isEmulatorDetectionIntervalRunning) {
325+
return;
326+
}
327+
328+
isEmulatorDetectionIntervalRunning = true;
329+
await this.detectCurrentlyAvailableEmulators();
330+
isEmulatorDetectionIntervalRunning = false;
331+
}, DevicesService.EMULATOR_IMAGES_DETECTION_INTERVAL);
332+
333+
emulatorDetectionInterval.unref();
334+
this.deviceDetectionIntervals.push(emulatorDetectionInterval);
327335
}
328336

329337
/**
@@ -348,14 +356,17 @@ export class DevicesService extends EventEmitter implements Mobile.IDevicesServi
348356
/**
349357
* Starts looking for running devices. All found devices are pushed to _devices variable.
350358
*/
351-
private startLookingForDevices(deviceInitOpts?: Mobile.IDevicesServicesInitializationOptions): Promise<void> {
359+
private async startLookingForDevices(deviceInitOpts?: Mobile.IDevicesServicesInitializationOptions): Promise<void> {
352360
this.$logger.trace("startLookingForDevices; platform is %s", this._platform);
353361

354362
if (this._platform) {
355363
return this.detectCurrentlyAttachedDevices(deviceInitOpts);
356364
}
357365

358-
return this.startDeviceDetectionInterval(deviceInitOpts);
366+
await this.detectCurrentlyAttachedDevices(deviceInitOpts);
367+
await this.detectCurrentlyAvailableEmulators();
368+
369+
await this.startDeviceDetectionIntervals(deviceInitOpts);
359370
}
360371

361372
/**
@@ -690,10 +701,14 @@ export class DevicesService extends EventEmitter implements Mobile.IDevicesServi
690701
}
691702

692703
private clearDeviceDetectionInterval(): void {
693-
if (this.deviceDetectionInterval) {
694-
clearInterval(this.deviceDetectionInterval);
704+
if (this.deviceDetectionIntervals.length) {
705+
for (const interval of this.deviceDetectionIntervals) {
706+
clearInterval(interval);
707+
}
708+
709+
this.deviceDetectionIntervals.splice(0, this.deviceDetectionIntervals.length);
695710
} else {
696-
this.$logger.trace("Device detection interval is not started, so it cannot be stopped.");
711+
this.$logger.trace("Device detection intervals are not started, so it cannot be stopped.");
697712
}
698713
}
699714

lib/common/test/unit-tests/mobile/devices-service.ts

+43-29
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ class DevicesServiceInheritor extends DevicesService {
2828
return super.startEmulatorIfNecessary(data);
2929
}
3030

31-
public startDeviceDetectionInterval(deviceInitOpts: Mobile.IDevicesServicesInitializationOptions = {}): Promise<void> {
32-
return super.startDeviceDetectionInterval(deviceInitOpts);
31+
public startDeviceDetectionIntervals(deviceInitOpts: Mobile.IDevicesServicesInitializationOptions = {}): Promise<void> {
32+
return super.startDeviceDetectionIntervals(deviceInitOpts);
3333
}
3434

3535
public detectCurrentlyAttachedDevices(options?: Mobile.IDevicesServicesInitializationOptions): Promise<void> {
@@ -236,6 +236,15 @@ function resetDefaultSetInterval(): void {
236236
global.setInterval = originalSetInterval;
237237
}
238238

239+
async function assertOnNextTick(assertionFunction: Function): Promise<void> {
240+
await new Promise(resolve => {
241+
process.nextTick(() => {
242+
assertionFunction();
243+
resolve();
244+
});
245+
});
246+
}
247+
239248
describe("devicesService", () => {
240249
let counter = 0;
241250
const iOSDevice = {
@@ -1184,7 +1193,7 @@ describe("devicesService", () => {
11841193
});
11851194
});
11861195

1187-
describe("startDeviceDetectionInterval", () => {
1196+
describe("startDeviceDetectionIntervals", () => {
11881197
let setIntervalsCalledCount: number;
11891198

11901199
beforeEach(() => {
@@ -1203,7 +1212,7 @@ describe("devicesService", () => {
12031212
hasStartedDeviceDetectionInterval = true;
12041213
});
12051214

1206-
await devicesService.startDeviceDetectionInterval();
1215+
await devicesService.startDeviceDetectionIntervals();
12071216

12081217
assert.isTrue(hasStartedDeviceDetectionInterval);
12091218
});
@@ -1227,11 +1236,11 @@ describe("devicesService", () => {
12271236
};
12281237
};
12291238

1230-
await devicesService.startDeviceDetectionInterval();
1231-
await devicesService.startDeviceDetectionInterval();
1232-
await devicesService.startDeviceDetectionInterval();
1239+
await devicesService.startDeviceDetectionIntervals();
1240+
await devicesService.startDeviceDetectionIntervals();
1241+
await devicesService.startDeviceDetectionIntervals();
12331242

1234-
assert.deepEqual(setIntervalsCalledCount, 1);
1243+
assert.deepEqual(setIntervalsCalledCount, 2);
12351244
});
12361245

12371246
describe("ios devices check", () => {
@@ -1248,15 +1257,15 @@ describe("devicesService", () => {
12481257
hasCheckedForIosDevices = true;
12491258
};
12501259

1251-
await devicesService.startDeviceDetectionInterval();
1260+
await devicesService.startDeviceDetectionIntervals();
12521261

12531262
assert.isTrue(hasCheckedForIosDevices);
12541263
});
12551264

12561265
it("should not throw if ios device check fails throws an exception.", async () => {
12571266
(<any>$iOSDeviceDiscovery).checkForDevices = throwErrorFunction;
12581267

1259-
await assert.isFulfilled(devicesService.startDeviceDetectionInterval());
1268+
await assert.isFulfilled(devicesService.startDeviceDetectionIntervals());
12601269
});
12611270
});
12621271

@@ -1267,22 +1276,22 @@ describe("devicesService", () => {
12671276
$androidDeviceDiscovery = testInjector.resolve("androidDeviceDiscovery");
12681277
});
12691278

1270-
it("should check for android devices.", async () => {
1279+
it("should start interval that will check for android devices.", async () => {
12711280
let hasCheckedForAndroidDevices = false;
12721281

12731282
$androidDeviceDiscovery.startLookingForDevices = async (): Promise<void> => {
12741283
hasCheckedForAndroidDevices = true;
12751284
};
12761285

1277-
await devicesService.startDeviceDetectionInterval();
1278-
1279-
assert.isTrue(hasCheckedForAndroidDevices);
1286+
mockSetInterval();
1287+
await devicesService.startDeviceDetectionIntervals();
1288+
await assertOnNextTick(() => assert.isTrue(hasCheckedForAndroidDevices));
12801289
});
12811290

12821291
it("should not throw if android device check fails throws an exception.", async () => {
12831292
$androidDeviceDiscovery.startLookingForDevices = throwErrorFunction;
12841293

1285-
await assert.isFulfilled(devicesService.startDeviceDetectionInterval());
1294+
await assert.isFulfilled(devicesService.startDeviceDetectionIntervals());
12861295
});
12871296
});
12881297

@@ -1305,7 +1314,7 @@ describe("devicesService", () => {
13051314
it("should not throw if ios simulator check fails throws an exception.", async () => {
13061315
(<any>$iOSSimulatorDiscovery).checkForDevices = throwErrorFunction;
13071316

1308-
await assert.isFulfilled(devicesService.startDeviceDetectionInterval());
1317+
await assert.isFulfilled(devicesService.startDeviceDetectionIntervals());
13091318
});
13101319
});
13111320

@@ -1317,22 +1326,23 @@ describe("devicesService", () => {
13171326
devicesService.addDeviceDiscovery(customDeviceDiscovery);
13181327
});
13191328

1320-
it("should check for devices.", async () => {
1329+
it("should check for devices in interval", async () => {
13211330
let hasCheckedForDevices = false;
13221331

13231332
customDeviceDiscovery.startLookingForDevices = async (): Promise<void> => {
13241333
hasCheckedForDevices = true;
13251334
};
13261335

1327-
await devicesService.startDeviceDetectionInterval();
1336+
mockSetInterval();
1337+
await devicesService.startDeviceDetectionIntervals();
13281338

1329-
assert.isTrue(hasCheckedForDevices);
1339+
await assertOnNextTick(() => assert.isTrue(hasCheckedForDevices));
13301340
});
13311341

13321342
it("should not throw if device check fails throws an exception.", async () => {
13331343
customDeviceDiscovery.startLookingForDevices = throwErrorFunction;
13341344

1335-
await assert.isFulfilled(devicesService.startDeviceDetectionInterval());
1345+
await assert.isFulfilled(devicesService.startDeviceDetectionIntervals());
13361346
});
13371347
});
13381348

@@ -1361,34 +1371,38 @@ describe("devicesService", () => {
13611371
});
13621372

13631373
it("should check for application updates for all connected devices.", async () => {
1364-
await devicesService.startDeviceDetectionInterval();
1374+
await devicesService.startDeviceDetectionIntervals();
13651375

1366-
assert.isTrue(hasCheckedForAndroidAppUpdates);
1367-
assert.isTrue(hasCheckedForIosAppUpdates);
1376+
await assertOnNextTick(() => {
1377+
assert.isTrue(hasCheckedForAndroidAppUpdates);
1378+
assert.isTrue(hasCheckedForIosAppUpdates);
1379+
});
13681380
});
13691381

13701382
it("should check for application updates if the check on one device throws an exception.", async () => {
13711383
iOSDevice.applicationManager.checkForApplicationUpdates = throwErrorFunction;
13721384

1373-
await devicesService.startDeviceDetectionInterval();
1385+
await devicesService.startDeviceDetectionIntervals();
13741386

1375-
assert.isTrue(hasCheckedForAndroidAppUpdates);
1387+
await assertOnNextTick(() => assert.isTrue(hasCheckedForAndroidAppUpdates));
13761388
});
13771389

13781390
it("should check for application updates only on devices with status Connected", async () => {
13791391
androidDevice.deviceInfo.status = constants.UNREACHABLE_STATUS;
1380-
await devicesService.startDeviceDetectionInterval();
1392+
await devicesService.startDeviceDetectionIntervals();
13811393

1382-
assert.isFalse(hasCheckedForAndroidAppUpdates);
1383-
assert.isTrue(hasCheckedForIosAppUpdates);
1394+
await assertOnNextTick(() => {
1395+
assert.isFalse(hasCheckedForAndroidAppUpdates);
1396+
assert.isTrue(hasCheckedForIosAppUpdates);
1397+
});
13841398
});
13851399

13861400
it("should not throw if all checks for application updates on all devices throw exceptions.", () => {
13871401
iOSDevice.applicationManager.checkForApplicationUpdates = throwErrorFunction;
13881402
androidDevice.applicationManager.checkForApplicationUpdates = throwErrorFunction;
13891403

13901404
const callback = () => {
1391-
devicesService.startDeviceDetectionInterval.call(devicesService);
1405+
devicesService.startDeviceDetectionIntervals.call(devicesService);
13921406
};
13931407

13941408
assert.doesNotThrow(callback);

0 commit comments

Comments
 (0)