Files
meteor/tools/safe-pathwatcher.js
Ben Newman 0c8edc1511 Use both pathwatcher and polling to detect changes more robustly.
This strategy was suggested by @glasser after we realized just how
hopeless it is to probe the file system to test pathwatcher:
https://github.com/meteor/meteor/issues/3285#issuecomment-67296961

I've made some attempt to de-duplicate these events (since we're
effectively watching twice now), but we have other mechanisms for dealing
with bursty file change events, so these measures do not need to be
completely bulletproof.

Fixes #3284 (again).
2014-12-17 14:16:41 -05:00

62 lines
1.9 KiB
JavaScript

var fs = require("fs");
// Set this env variable to a truthy value to force fs.watchFile instead
// of pathwatcher.watch.
var canUsePathwatcher = !process.env.METEOR_WATCH_FORCE_POLLING;
var pollingInterval = canUsePathwatcher
// Set this env variable to alter the watchFile polling interval.
? ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000
: 500;
exports.watch = function watch(absPath, callback) {
var lastPathwatcherEventTime = 0;
function pathwatcherWrapper() {
// It's tempting to call fs.unwatchFile(absPath, watchFileWrapper)
// here, but previous pathwatcher success is no guarantee of future
// pathwatcher reliability. For example, pathwatcher works just fine
// when file changes originate from within a Vagrant VM, but changes
// to shared files made outside the VM are invisible to pathwatcher,
// so our only hope of catching them is to continue polling.
lastPathwatcherEventTime = +new Date;
callback.apply(this, arguments);
}
var watcher = canUsePathwatcher &&
require("pathwatcher").watch(absPath, pathwatcherWrapper);
function watchFileWrapper() {
// If a pathwatcher event fired in the last polling interval, ignore
// this event.
if (new Date - lastPathwatcherEventTime > pollingInterval) {
callback.apply(this, arguments);
}
}
// We use fs.watchFile in addition to pathwatcher.watch as a fail-safe
// to detect file changes even on network file systems. However (unless
// canUsePathwatcher is false), we use a relatively long default polling
// interval of 5000ms to save CPU cycles.
fs.watchFile(absPath, {
persistent: false,
interval: pollingInterval
}, watchFileWrapper);
var polling = true;
return {
close: function close() {
if (watcher) {
watcher.close();
watcher = null;
}
if (polling) {
polling = false;
fs.unwatchFile(absPath, watchFileWrapper);
}
}
};
};