@@ -61,7 +61,6 @@ class FSEventsWatcher {
61
61
int write_fd,
62
62
bool recursive)
63
63
: watcher_(watcher),
64
- ready_ (false ),
65
64
base_path_length_ (strlen(base_path)),
66
65
path_ref_(CFStringCreateWithCString(NULL ,
67
66
base_path,
@@ -74,9 +73,14 @@ class FSEventsWatcher {
74
73
}
75
74
76
75
~Node () {
77
- Stop ();
76
+ // This is invoked outside of [Callback] execution because
77
+ // [context.release] callback is invoked when [FSEventStream] is
78
+ // deallocated, the same [FSEventStream] that [Callback] gets a reference
79
+ // to during its execution. [Callback] holding a reference prevents stream
80
+ // from deallocation.
78
81
close (write_fd_);
79
82
CFRelease (path_ref_);
83
+ watcher_ = nullptr ; // this is to catch access-after-free in Callback
80
84
}
81
85
82
86
void set_ref (FSEventStreamRef ref) { ref_ = ref; }
@@ -85,6 +89,9 @@ class FSEventsWatcher {
85
89
FSEventStreamContext context;
86
90
memset (&context, 0 , sizeof (context));
87
91
context.info = reinterpret_cast <void *>(this );
92
+ context.release = [](const void * info) {
93
+ delete static_cast <const Node*>(info);
94
+ };
88
95
CFArrayRef array = CFArrayCreate (
89
96
NULL , reinterpret_cast <const void **>(&path_ref_), 1 , NULL );
90
97
FSEventStreamRef ref = FSEventStreamCreate (
@@ -93,7 +100,6 @@ class FSEventsWatcher {
93
100
CFRelease (array);
94
101
95
102
set_ref (ref);
96
- ready_.store (true , std::memory_order_release);
97
103
98
104
FSEventStreamScheduleWithRunLoop (ref_, watcher_->run_loop_ ,
99
105
kCFRunLoopDefaultMode );
@@ -103,23 +109,19 @@ class FSEventsWatcher {
103
109
}
104
110
105
111
void Stop () {
106
- ASSERT (ready_);
107
112
FSEventStreamStop (ref_);
108
113
FSEventStreamInvalidate (ref_);
109
114
FSEventStreamRelease (ref_);
110
- ready_.store (false , std::memory_order_release);
111
115
}
112
116
113
117
FSEventsWatcher* watcher () const { return watcher_; }
114
- bool ready () const { return ready_.load (std::memory_order_acquire); }
115
118
intptr_t base_path_length () const { return base_path_length_; }
116
119
int read_fd () const { return read_fd_; }
117
120
int write_fd () const { return write_fd_; }
118
121
bool recursive () const { return recursive_; }
119
122
120
123
private:
121
124
FSEventsWatcher* watcher_;
122
- std::atomic<bool > ready_;
123
125
intptr_t base_path_length_;
124
126
CFStringRef path_ref_;
125
127
int read_fd_;
@@ -218,12 +220,15 @@ class FSEventsWatcher {
218
220
void * event_paths,
219
221
const FSEventStreamEventFlags event_flags[],
220
222
const FSEventStreamEventId event_ids[]) {
221
- Node* node = reinterpret_cast <Node*>(client);
223
+ if (FileSystemWatcher::delayed_filewatch_callback ()) {
224
+ // Used in tests to highlight race between callback invocation
225
+ // and unwatching the file path, Node destruction
226
+ TimerUtils::Sleep (1000 /* ms */ );
227
+ }
228
+ Node* node = static_cast <Node*>(client);
229
+ RELEASE_ASSERT (node->watcher () != nullptr );
222
230
ASSERT (Thread::Compare (node->watcher ()->threadId_ ,
223
231
Thread::GetCurrentThreadId ()));
224
- if (!node->ready ()) {
225
- return ;
226
- }
227
232
for (size_t i = 0 ; i < num_events; i++) {
228
233
char * path = reinterpret_cast <char **>(event_paths)[i];
229
234
FSEvent event;
@@ -274,7 +279,7 @@ intptr_t FileSystemWatcher::WatchPath(intptr_t id,
274
279
275
280
void FileSystemWatcher::UnwatchPath (intptr_t id, intptr_t path_id) {
276
281
USE (id);
277
- delete reinterpret_cast <FSEventsWatcher::Node*>(path_id);
282
+ reinterpret_cast <FSEventsWatcher::Node*>(path_id)-> Stop ( );
278
283
}
279
284
280
285
intptr_t FileSystemWatcher::GetSocketId (intptr_t id, intptr_t path_id) {
0 commit comments