2016-03-10 16:12:34 +00:00
|
|
|
/**
|
|
|
|
* Copyright (c) 2015-present, Facebook, Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* This source code is licensed under the BSD-style license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
*
|
|
|
|
* @providesModule JSEventLoopWatchdog
|
|
|
|
* @flow
|
|
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
|
2016-09-07 02:45:08 +00:00
|
|
|
const infoLog = require('infoLog');
|
2016-08-26 00:36:33 +00:00
|
|
|
const performanceNow = require('fbjs/lib/performanceNow');
|
2016-03-10 16:12:34 +00:00
|
|
|
|
|
|
|
type Handler = {
|
2016-08-09 13:32:41 +00:00
|
|
|
onIterate?: () => void,
|
2016-09-07 02:45:08 +00:00
|
|
|
onStall: (params: {lastInterval: number, busyTime: number}) => ?string,
|
2016-03-10 16:12:34 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A utility for tracking stalls in the JS event loop that prevent timers and
|
|
|
|
* other events from being processed in a timely manner.
|
|
|
|
*
|
|
|
|
* The "stall" time is defined as the amount of time in access of the acceptable
|
|
|
|
* threshold, which is typically around 100-200ms. So if the treshold is set to
|
|
|
|
* 100 and a timer fires 150 ms later than it was scheduled because the event
|
|
|
|
* loop was tied up, that would be considered a 50ms stall.
|
|
|
|
*
|
|
|
|
* By default, logs stall events to the console when installed. Can also be
|
|
|
|
* queried with `getStats`.
|
|
|
|
*/
|
|
|
|
const JSEventLoopWatchdog = {
|
|
|
|
getStats: function(): Object {
|
|
|
|
return {stallCount, totalStallTime, longestStall, acceptableBusyTime};
|
|
|
|
},
|
|
|
|
reset: function() {
|
2016-09-07 02:45:08 +00:00
|
|
|
infoLog('JSEventLoopWatchdog: reset');
|
2016-03-10 16:12:34 +00:00
|
|
|
totalStallTime = 0;
|
|
|
|
stallCount = 0;
|
|
|
|
longestStall = 0;
|
2016-05-04 02:19:56 +00:00
|
|
|
lastInterval = performanceNow();
|
2016-03-10 16:12:34 +00:00
|
|
|
},
|
|
|
|
addHandler: function(handler: Handler) {
|
|
|
|
handlers.push(handler);
|
|
|
|
},
|
|
|
|
install: function({thresholdMS}: {thresholdMS: number}) {
|
|
|
|
acceptableBusyTime = thresholdMS;
|
|
|
|
if (installed) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
installed = true;
|
2016-05-04 02:19:56 +00:00
|
|
|
lastInterval = performanceNow();
|
2016-03-10 16:12:34 +00:00
|
|
|
function iteration() {
|
|
|
|
const now = performanceNow();
|
|
|
|
const busyTime = now - lastInterval;
|
|
|
|
if (busyTime >= thresholdMS) {
|
|
|
|
const stallTime = busyTime - thresholdMS;
|
|
|
|
stallCount++;
|
|
|
|
totalStallTime += stallTime;
|
|
|
|
longestStall = Math.max(longestStall, stallTime);
|
|
|
|
let msg = `JSEventLoopWatchdog: JS thread busy for ${busyTime}ms. ` +
|
|
|
|
`${totalStallTime}ms in ${stallCount} stalls so far. `;
|
|
|
|
handlers.forEach((handler) => {
|
2016-09-07 02:45:08 +00:00
|
|
|
msg += handler.onStall({lastInterval, busyTime}) || '';
|
2016-03-10 16:12:34 +00:00
|
|
|
});
|
2016-09-07 02:45:08 +00:00
|
|
|
infoLog(msg);
|
2016-03-10 16:12:34 +00:00
|
|
|
}
|
|
|
|
handlers.forEach((handler) => {
|
|
|
|
handler.onIterate && handler.onIterate();
|
|
|
|
});
|
|
|
|
lastInterval = now;
|
|
|
|
setTimeout(iteration, thresholdMS / 5);
|
|
|
|
}
|
|
|
|
iteration();
|
|
|
|
},
|
|
|
|
};
|
|
|
|
|
|
|
|
let acceptableBusyTime = 0;
|
|
|
|
let installed = false;
|
|
|
|
let totalStallTime = 0;
|
|
|
|
let stallCount = 0;
|
|
|
|
let longestStall = 0;
|
2016-05-04 02:19:56 +00:00
|
|
|
let lastInterval = 0;
|
2016-03-10 16:12:34 +00:00
|
|
|
const handlers: Array<Handler> = [];
|
|
|
|
|
|
|
|
module.exports = JSEventLoopWatchdog;
|