Skip to content

Commit f0ce984

Browse files
committed
fs: make fs.watchFile() work on windows
1 parent d98a857 commit f0ce984

File tree

8 files changed

+52
-63
lines changed

8 files changed

+52
-63
lines changed

lib/fs.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -876,7 +876,13 @@ function StatWatcher() {
876876
var self = this;
877877
this._handle = new binding.StatWatcher();
878878

879-
this._handle.onchange = function(current, previous) {
879+
// uv_fs_poll is a little more powerful than ev_stat but we curb it for
880+
// the sake of backwards compatibility
881+
var oldStatus = -1;
882+
883+
this._handle.onchange = function(current, previous, newStatus) {
884+
if (oldStatus == -1 && newStatus == -1) return;
885+
oldStatus = newStatus;
880886
self.emit('change', current, previous);
881887
};
882888

@@ -905,10 +911,6 @@ function inStatWatchers(filename) {
905911

906912

907913
fs.watchFile = function(filename) {
908-
if (isWindows) {
909-
throw new Error('use fs.watch api instead');
910-
}
911-
912914
var stat;
913915
var options;
914916
var listener;

node.gyp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@
8282
'src/node_main.cc',
8383
'src/node_os.cc',
8484
'src/node_script.cc',
85+
'src/node_stat_watcher.cc',
8586
'src/node_string.cc',
8687
'src/node_zlib.cc',
8788
'src/pipe_wrap.cc',
@@ -208,7 +209,6 @@
208209
'defines': [ '__POSIX__' ],
209210
'sources': [
210211
'src/node_signal_watcher.cc',
211-
'src/node_stat_watcher.cc',
212212
'src/node_io_watcher.cc',
213213
]
214214
}],

src/node.cc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,6 @@ typedef int mode_t;
6969
#include "node_http_parser.h"
7070
#ifdef __POSIX__
7171
# include "node_signal_watcher.h"
72-
# include "node_stat_watcher.h"
7372
#endif
7473
#include "node_constants.h"
7574
#include "node_javascript.h"

src/node_file.cc

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
#include "node.h"
2323
#include "node_file.h"
2424
#include "node_buffer.h"
25-
#ifdef __POSIX__
26-
# include "node_stat_watcher.h"
27-
#endif
25+
#include "node_stat_watcher.h"
2826
#include "req_wrap.h"
2927

3028
#include <fcntl.h>
@@ -984,9 +982,7 @@ void InitFs(Handle<Object> target) {
984982

985983
oncomplete_sym = NODE_PSYMBOL("oncomplete");
986984

987-
#ifdef __POSIX__
988985
StatWatcher::Initialize(target);
989-
#endif
990986
}
991987

992988
} // end namespace node

src/node_stat_watcher.cc

Lines changed: 36 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@
2525
#include <string.h>
2626
#include <stdlib.h>
2727

28+
// Poll interval in milliseconds. 5007 is what libev used to use. It's a little
29+
// on the slow side but let's stick with it for now, keep behavioral changes to
30+
// a minimum.
31+
#define DEFAULT_POLL_INTERVAL 5007
32+
2833
namespace node {
2934

3035
using namespace v8;
@@ -33,6 +38,7 @@ Persistent<FunctionTemplate> StatWatcher::constructor_template;
3338
static Persistent<String> onchange_sym;
3439
static Persistent<String> onstop_sym;
3540

41+
3642
void StatWatcher::Initialize(Handle<Object> target) {
3743
HandleScope scope;
3844

@@ -48,18 +54,24 @@ void StatWatcher::Initialize(Handle<Object> target) {
4854
}
4955

5056

51-
void StatWatcher::Callback(EV_P_ ev_stat *watcher, int revents) {
52-
assert(revents == EV_STAT);
53-
StatWatcher *handler = static_cast<StatWatcher*>(watcher->data);
54-
assert(watcher == &handler->watcher_);
57+
void StatWatcher::Callback(uv_fs_poll_t* handle,
58+
int status,
59+
const uv_statbuf_t* prev,
60+
const uv_statbuf_t* curr) {
61+
StatWatcher* wrap = container_of(handle, StatWatcher, watcher_);
62+
assert(handle == &wrap->watcher_);
5563
HandleScope scope;
56-
Local<Value> argv[2];
57-
argv[0] = BuildStatsObject(&watcher->attr);
58-
argv[1] = BuildStatsObject(&watcher->prev);
64+
Local<Value> argv[3];
65+
argv[0] = BuildStatsObject(curr);
66+
argv[1] = BuildStatsObject(prev);
67+
argv[2] = Integer::New(status);
68+
if (status == -1) {
69+
SetErrno(uv_last_error(wrap->watcher_.loop));
70+
}
5971
if (onchange_sym.IsEmpty()) {
6072
onchange_sym = NODE_PSYMBOL("onchange");
6173
}
62-
MakeCallback(handler->handle_, onchange_sym, ARRAY_SIZE(argv), argv);
74+
MakeCallback(wrap->handle_, onchange_sym, ARRAY_SIZE(argv), argv);
6375
}
6476

6577

@@ -69,7 +81,7 @@ Handle<Value> StatWatcher::New(const Arguments& args) {
6981
}
7082

7183
HandleScope scope;
72-
StatWatcher *s = new StatWatcher();
84+
StatWatcher* s = new StatWatcher();
7385
s->Wrap(args.Holder());
7486
return args.This();
7587
}
@@ -82,52 +94,44 @@ Handle<Value> StatWatcher::Start(const Arguments& args) {
8294
return ThrowException(Exception::TypeError(String::New("Bad arguments")));
8395
}
8496

85-
StatWatcher *handler = ObjectWrap::Unwrap<StatWatcher>(args.Holder());
97+
StatWatcher* wrap = ObjectWrap::Unwrap<StatWatcher>(args.Holder());
8698
String::Utf8Value path(args[0]);
8799

88-
assert(handler->path_ == NULL);
89-
handler->path_ = strdup(*path);
90-
91-
ev_tstamp interval = 0.;
92-
if (args[2]->IsInt32()) {
93-
interval = NODE_V8_UNIXTIME(args[2]);
100+
uint32_t interval = DEFAULT_POLL_INTERVAL;
101+
if (args[2]->IsUint32()) {
102+
interval = args[2]->Uint32Value();
94103
}
95104

96-
ev_stat_set(&handler->watcher_, handler->path_, interval);
97-
ev_stat_start(EV_DEFAULT_UC_ &handler->watcher_);
105+
uv_fs_poll_start(&wrap->watcher_, Callback, *path, interval);
98106

99-
handler->persistent_ = args[1]->IsTrue();
107+
wrap->persistent_ = args[1]->IsTrue();
100108

101-
if (!handler->persistent_) {
102-
ev_unref(EV_DEFAULT_UC);
109+
if (!wrap->persistent_) {
110+
uv_unref(reinterpret_cast<uv_handle_t*>(&wrap->watcher_));
103111
}
104112

105-
handler->Ref();
113+
wrap->Ref();
106114

107115
return Undefined();
108116
}
109117

110118

111119
Handle<Value> StatWatcher::Stop(const Arguments& args) {
112120
HandleScope scope;
113-
StatWatcher *handler = ObjectWrap::Unwrap<StatWatcher>(args.Holder());
121+
StatWatcher* wrap = ObjectWrap::Unwrap<StatWatcher>(args.Holder());
114122
if (onstop_sym.IsEmpty()) {
115123
onstop_sym = NODE_PSYMBOL("onstop");
116124
}
117-
MakeCallback(handler->handle_, onstop_sym, 0, NULL);
118-
handler->Stop();
125+
MakeCallback(wrap->handle_, onstop_sym, 0, NULL);
126+
wrap->Stop();
119127
return Undefined();
120128
}
121129

122130

123131
void StatWatcher::Stop () {
124-
if (watcher_.active) {
125-
if (!persistent_) ev_ref(EV_DEFAULT_UC);
126-
ev_stat_stop(EV_DEFAULT_UC_ &watcher_);
127-
free(path_);
128-
path_ = NULL;
129-
Unref();
130-
}
132+
if (!uv_is_active(reinterpret_cast<uv_handle_t*>(&watcher_))) return;
133+
uv_fs_poll_stop(&watcher_);
134+
Unref();
131135
}
132136

133137

src/node_stat_watcher.h

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
#define NODE_STAT_WATCHER_H_
2424

2525
#include "node.h"
26-
#include "uv-private/ev.h"
26+
#include "uv.h"
2727

2828
namespace node {
2929

@@ -36,28 +36,27 @@ class StatWatcher : ObjectWrap {
3636

3737
StatWatcher() : ObjectWrap() {
3838
persistent_ = false;
39-
path_ = NULL;
40-
ev_init(&watcher_, StatWatcher::Callback);
41-
watcher_.data = this;
39+
uv_fs_poll_init(uv_default_loop(), &watcher_);
4240
}
4341

4442
~StatWatcher() {
4543
Stop();
46-
assert(path_ == NULL);
4744
}
4845

4946
static v8::Handle<v8::Value> New(const v8::Arguments& args);
5047
static v8::Handle<v8::Value> Start(const v8::Arguments& args);
5148
static v8::Handle<v8::Value> Stop(const v8::Arguments& args);
5249

5350
private:
54-
static void Callback(EV_P_ ev_stat *watcher, int revents);
51+
static void Callback(uv_fs_poll_t* handle,
52+
int status,
53+
const uv_statbuf_t* prev,
54+
const uv_statbuf_t* curr);
5555

5656
void Stop();
5757

58-
ev_stat watcher_;
58+
uv_fs_poll_t watcher_;
5959
bool persistent_;
60-
char *path_;
6160
};
6261

6362
} // namespace node

test/pummel/test-fs-watch-file.js

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,6 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22-
// fs.watchFile is not available on Windows
23-
if (process.platform === 'win32') {
24-
process.exit(0);
25-
}
26-
27-
2822
var common = require('../common');
2923
var assert = require('assert');
3024
var path = require('path');

test/pummel/test-watch-file.js

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22-
// fs.watchFile is not available on Windows
23-
if (process.platform === 'win32') {
24-
process.exit(0);
25-
}
26-
2722
var common = require('../common');
2823
var assert = require('assert');
2924

0 commit comments

Comments
 (0)