GeezerVison is showing github.com — fs: add .ref() and .unref() methods to watcher classes · nodejs/node@1dcf66c
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/fs.md +67Lines changed: 67 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -579,6 +579,72 @@ added: v0.5.8 | 579 | 579 | Stop watching for changes on the given `fs.FSWatcher`. Once stopped, the | 580 | 580 | `fs.FSWatcher` object is no longer usable. | 581 | 581 |
| | 582 | +### `watcher.ref()` | | 583 | +<!-- YAML | | 584 | +added: REPLACEME | | 585 | +--> | | 586 | + | | 587 | +* Returns: {fs.FSWatcher} | | 588 | + | | 589 | +When called, requests that the Node.js event loop *not* exit so long as the | | 590 | +`FSWatcher` is active. Calling `watcher.ref()` multiple times will have | | 591 | +no effect. | | 592 | + | | 593 | +By default, all `FSWatcher` objects are "ref'ed", making it normally | | 594 | +unnecessary to call `watcher.ref()` unless `watcher.unref()` had been | | 595 | +called previously. | | 596 | + | | 597 | +### `watcher.unref()` | | 598 | +<!-- YAML | | 599 | +added: REPLACEME | | 600 | +--> | | 601 | + | | 602 | +* Returns: {fs.FSWatcher} | | 603 | + | | 604 | +When called, the active `FSWatcher` object will not require the Node.js | | 605 | +event loop to remain active. If there is no other activity keeping the | | 606 | +event loop running, the process may exit before the `FSWatcher` object's | | 607 | +callback is invoked. Calling `watcher.unref()` multiple times will have | | 608 | +no effect. | | 609 | + | | 610 | +## Class: `fs.StatWatcher` | | 611 | +<!-- YAML | | 612 | +added: REPLACEME | | 613 | +--> | | 614 | + | | 615 | +* Extends {EventEmitter} | | 616 | + | | 617 | +A successful call to `fs.watchFile()` method will return a new `fs.StatWatcher` | | 618 | +object. | | 619 | + | | 620 | +### `watcher.ref()` | | 621 | +<!-- YAML | | 622 | +added: REPLACEME | | 623 | +--> | | 624 | + | | 625 | +* Returns: {fs.StatWatcher} | | 626 | + | | 627 | +When called, requests that the Node.js event loop *not* exit so long as the | | 628 | +`StatWatcher` is active. Calling `watcher.ref()` multiple times will have | | 629 | +no effect. | | 630 | + | | 631 | +By default, all `StatWatcher` objects are "ref'ed", making it normally | | 632 | +unnecessary to call `watcher.ref()` unless `watcher.unref()` had been | | 633 | +called previously. | | 634 | + | | 635 | +### `watcher.unref()` | | 636 | +<!-- YAML | | 637 | +added: REPLACEME | | 638 | +--> | | 639 | + | | 640 | +* Returns: {fs.StatWatcher} | | 641 | + | | 642 | +When called, the active `StatWatcher` object will not require the Node.js | | 643 | +event loop to remain active. If there is no other activity keeping the | | 644 | +event loop running, the process may exit before the `StatWatcher` object's | | 645 | +callback is invoked. Calling `watcher.unref()` multiple times will have | | 646 | +no effect. | | 647 | + | 582 | 648 | ## Class: `fs.ReadStream` | 583 | 649 | <!-- YAML | 584 | 650 | added: v0.1.93 | @@ -3958,6 +4024,7 @@ changes: | 3958 | 4024 | * `listener` {Function} | 3959 | 4025 | * `current` {fs.Stats} | 3960 | 4026 | * `previous` {fs.Stats} | | 4027 | +* Returns: {fs.StatWatcher} | 3961 | 4028 |
| 3962 | 4029 | Watch for changes on `filename`. The callback `listener` will be called each | 3963 | 4030 | time the file is accessed. | |
Collapse file Copy file name to clipboardExpand all lines: lib/fs.js +6Lines changed: 6 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -1489,6 +1489,8 @@ function watchFile(filename, options, listener) { | 1489 | 1489 | stat[watchers.kFSStatWatcherStart](filename, | 1490 | 1490 | options.persistent, options.interval); | 1491 | 1491 | statWatchers.set(filename, stat); | | 1492 | + } else { | | 1493 | + stat[watchers.kFSStatWatcherAddOrCleanRef]('add'); | 1492 | 1494 | } | 1493 | 1495 |
| 1494 | 1496 | stat.addListener('change', listener); | @@ -1503,9 +1505,13 @@ function unwatchFile(filename, listener) { | 1503 | 1505 | if (stat === undefined) return; | 1504 | 1506 |
| 1505 | 1507 | if (typeof listener === 'function') { | | 1508 | + const beforeListenerCount = stat.listenerCount('change'); | 1506 | 1509 | stat.removeListener('change', listener); | | 1510 | + if (stat.listenerCount('change') < beforeListenerCount) | | 1511 | + stat[watchers.kFSStatWatcherAddOrCleanRef]('clean'); | 1507 | 1512 | } else { | 1508 | 1513 | stat.removeAllListeners('change'); | | 1514 | + stat[watchers.kFSStatWatcherAddOrCleanRef]('cleanAll'); | 1509 | 1515 | } | 1510 | 1516 |
| 1511 | 1517 | if (stat.listenerCount('change') === 0) { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/fs/watchers.js +53-2Lines changed: 53 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -31,6 +31,9 @@ const kUseBigint = Symbol('kUseBigint'); | 31 | 31 |
| 32 | 32 | const kFSWatchStart = Symbol('kFSWatchStart'); | 33 | 33 | const kFSStatWatcherStart = Symbol('kFSStatWatcherStart'); | | 34 | +const KFSStatWatcherRefCount = Symbol('KFSStatWatcherRefCount'); | | 35 | +const KFSStatWatcherMaxRefCount = Symbol('KFSStatWatcherMaxRefCount'); | | 36 | +const kFSStatWatcherAddOrCleanRef = Symbol('kFSStatWatcherAddOrCleanRef'); | 34 | 37 |
| 35 | 38 | function emitStop(self) { | 36 | 39 | self.emit('stop'); | @@ -42,6 +45,8 @@ function StatWatcher(bigint) { | 42 | 45 | this._handle = null; | 43 | 46 | this[kOldStatus] = -1; | 44 | 47 | this[kUseBigint] = bigint; | | 48 | + this[KFSStatWatcherRefCount] = 1; | | 49 | + this[KFSStatWatcherMaxRefCount] = 1; | 45 | 50 | } | 46 | 51 | ObjectSetPrototypeOf(StatWatcher.prototype, EventEmitter.prototype); | 47 | 52 | ObjectSetPrototypeOf(StatWatcher, EventEmitter); | @@ -75,7 +80,7 @@ StatWatcher.prototype[kFSStatWatcherStart] = function(filename, | 75 | 80 | this._handle[owner_symbol] = this; | 76 | 81 | this._handle.onchange = onchange; | 77 | 82 | if (!persistent) | 78 | | - this._handle.unref(); | | 83 | + this.unref(); | 79 | 84 |
| 80 | 85 | // uv_fs_poll is a little more powerful than ev_stat but we curb it for | 81 | 86 | // the sake of backwards compatibility. | @@ -117,6 +122,41 @@ StatWatcher.prototype.stop = function() { | 117 | 122 | this._handle = null; | 118 | 123 | }; | 119 | 124 |
| | 125 | +// Clean up or add ref counters. | | 126 | +StatWatcher.prototype[kFSStatWatcherAddOrCleanRef] = function(operate) { | | 127 | + if (operate === 'add') { | | 128 | + // Add a Ref | | 129 | + this[KFSStatWatcherRefCount]++; | | 130 | + this[KFSStatWatcherMaxRefCount]++; | | 131 | + } else if (operate === 'clean') { | | 132 | + // Clean up a single | | 133 | + this[KFSStatWatcherMaxRefCount]--; | | 134 | + this.unref(); | | 135 | + } else if (operate === 'cleanAll') { | | 136 | + // Clean up all | | 137 | + this[KFSStatWatcherMaxRefCount] = 0; | | 138 | + this[KFSStatWatcherRefCount] = 0; | | 139 | + this._handle && this._handle.unref(); | | 140 | + } | | 141 | +}; | | 142 | + | | 143 | +StatWatcher.prototype.ref = function() { | | 144 | + // Avoid refCount calling ref multiple times causing unref to have no effect. | | 145 | + if (this[KFSStatWatcherRefCount] === this[KFSStatWatcherMaxRefCount]) | | 146 | + return this; | | 147 | + if (this._handle && this[KFSStatWatcherRefCount]++ === 0) | | 148 | + this._handle.ref(); | | 149 | + return this; | | 150 | +}; | | 151 | + | | 152 | +StatWatcher.prototype.unref = function() { | | 153 | + // Avoid refCount calling unref multiple times causing ref to have no effect. | | 154 | + if (this[KFSStatWatcherRefCount] === 0) return this; | | 155 | + if (this._handle && --this[KFSStatWatcherRefCount] === 0) | | 156 | + this._handle.unref(); | | 157 | + return this; | | 158 | +}; | | 159 | + | 120 | 160 |
| 121 | 161 | function FSWatcher() { | 122 | 162 | EventEmitter.call(this); | @@ -208,6 +248,16 @@ FSWatcher.prototype.close = function() { | 208 | 248 | process.nextTick(emitCloseNT, this); | 209 | 249 | }; | 210 | 250 |
| | 251 | +FSWatcher.prototype.ref = function() { | | 252 | + if (this._handle) this._handle.ref(); | | 253 | + return this; | | 254 | +}; | | 255 | + | | 256 | +FSWatcher.prototype.unref = function() { | | 257 | + if (this._handle) this._handle.unref(); | | 258 | + return this; | | 259 | +}; | | 260 | + | 211 | 261 | function emitCloseNT(self) { | 212 | 262 | self.emit('close'); | 213 | 263 | } | @@ -223,5 +273,6 @@ module.exports = { | 223 | 273 | FSWatcher, | 224 | 274 | StatWatcher, | 225 | 275 | kFSWatchStart, | 226 | | - kFSStatWatcherStart | | 276 | + kFSStatWatcherStart, | | 277 | + kFSStatWatcherAddOrCleanRef, | 227 | 278 | }; |
Collapse file Copy file name to clipboardExpand all lines: src/fs_event_wrap.cc +1-2Lines changed: 1 addition & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -101,9 +101,8 @@ void FSEventWrap::Initialize(Local<Object> target, | 101 | 101 | FSEventWrap::kInternalFieldCount); | 102 | 102 | t->SetClassName(fsevent_string); | 103 | 103 |
| 104 | | - t->Inherit(AsyncWrap::GetConstructorTemplate(env)); | | 104 | + t->Inherit(HandleWrap::GetConstructorTemplate(env)); | 105 | 105 | env->SetProtoMethod(t, "start", Start); | 106 | | - env->SetProtoMethod(t, "close", Close); | 107 | 106 |
| 108 | 107 | Local<FunctionTemplate> get_initialized_templ = | 109 | 108 | FunctionTemplate::New(env->isolate(), | |
Collapse file Copy file name to clipboard +20Lines changed: 20 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | + | | 3 | +const common = require('../common'); | | 4 | + | | 5 | +if (common.isIBMi) | | 6 | + common.skip('IBMi does not support `fs.watch()`'); | | 7 | + | | 8 | +const fs = require('fs'); | | 9 | + | | 10 | +const watcher = fs.watch(__filename, common.mustNotCall()); | | 11 | + | | 12 | +watcher.unref(); | | 13 | + | | 14 | +setTimeout( | | 15 | + common.mustCall(() => { | | 16 | + watcher.ref(); | | 17 | + watcher.unref(); | | 18 | + }), | | 19 | + common.platformTimeout(100) | | 20 | +); |
Collapse file Copy file name to clipboard +35Lines changed: 35 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | + | | 3 | +const common = require('../common'); | | 4 | + | | 5 | +const fs = require('fs'); | | 6 | +const assert = require('assert'); | | 7 | + | | 8 | +const uncalledListener = common.mustNotCall(); | | 9 | +const uncalledListener2 = common.mustNotCall(); | | 10 | +const watcher = fs.watchFile(__filename, uncalledListener); | | 11 | + | | 12 | +watcher.unref(); | | 13 | +watcher.unref(); | | 14 | +watcher.ref(); | | 15 | +watcher.unref(); | | 16 | +watcher.ref(); | | 17 | +watcher.ref(); | | 18 | +watcher.unref(); | | 19 | + | | 20 | +fs.unwatchFile(__filename, uncalledListener); | | 21 | + | | 22 | +// Watch the file with two different listeners. | | 23 | +fs.watchFile(__filename, uncalledListener); | | 24 | +const watcher2 = fs.watchFile(__filename, uncalledListener2); | | 25 | + | | 26 | +setTimeout( | | 27 | + common.mustCall(() => { | | 28 | + fs.unwatchFile(__filename, common.mustNotCall()); | | 29 | + assert.strictEqual(watcher2.listenerCount('change'), 2); | | 30 | + fs.unwatchFile(__filename, uncalledListener); | | 31 | + assert.strictEqual(watcher2.listenerCount('change'), 1); | | 32 | + watcher2.unref(); | | 33 | + }), | | 34 | + common.platformTimeout(100) | | 35 | +); |
Collapse file Copy file name to clipboardExpand all lines: tools/doc/type-parser.js +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -84,6 +84,7 @@ const customTypesMap = { | 84 | 84 | 'fs.FSWatcher': 'fs.html#fs_class_fs_fswatcher', | 85 | 85 | 'fs.ReadStream': 'fs.html#fs_class_fs_readstream', | 86 | 86 | 'fs.Stats': 'fs.html#fs_class_fs_stats', | | 87 | + 'fs.StatWatcher': 'fs.html#fs_class_fs_statwatcher', | 87 | 88 | 'fs.WriteStream': 'fs.html#fs_class_fs_writestream', | 88 | 89 |
| 89 | 90 | 'http.Agent': 'http.html#http_class_http_agent', | |
|