ext/libuv/src/unix/linux-inotify.c in libuv-2.0.6 vs ext/libuv/src/unix/linux-inotify.c in libuv-2.0.8
- old
+ new
@@ -33,10 +33,11 @@
#include <unistd.h>
struct watcher_list {
RB_ENTRY(watcher_list) entry;
QUEUE watchers;
+ int iterating;
char* path;
int wd;
};
struct watcher_root {
@@ -111,10 +112,19 @@
struct watcher_list w;
w.wd = wd;
return RB_FIND(watcher_root, CAST(&loop->inotify_watchers), &w);
}
+static void maybe_free_watcher_list(struct watcher_list* w, uv_loop_t* loop) {
+ /* if the watcher_list->watchers is being iterated over, we can't free it. */
+ if ((!w->iterating) && QUEUE_EMPTY(&w->watchers)) {
+ /* No watchers left for this path. Clean up. */
+ RB_REMOVE(watcher_root, CAST(&loop->inotify_watchers), w);
+ uv__inotify_rm_watch(loop->inotify_fd, w->wd);
+ uv__free(w);
+ }
+}
static void uv__inotify_read(uv_loop_t* loop,
uv__io_t* dummy,
unsigned int events) {
const struct uv__inotify_event* e;
@@ -158,20 +168,35 @@
* for modifications. Repurpose the filename for API compatibility.
* I'm not convinced this is a good thing, maybe it should go.
*/
path = e->len ? (const char*) (e + 1) : uv__basename_r(w->path);
+ /* We're about to iterate over the queue and call user's callbacks.
+ * What can go wrong?
+ * A callback could call uv_fs_event_stop()
+ * and the queue can change under our feet.
+ * So, we use QUEUE_MOVE() trick to safely iterate over the queue.
+ * And we don't free the watcher_list until we're done iterating.
+ *
+ * First,
+ * tell uv_fs_event_stop() (that could be called from a user's callback)
+ * not to free watcher_list.
+ */
+ w->iterating = 1;
QUEUE_MOVE(&w->watchers, &queue);
while (!QUEUE_EMPTY(&queue)) {
q = QUEUE_HEAD(&queue);
h = QUEUE_DATA(q, uv_fs_event_t, watchers);
QUEUE_REMOVE(q);
QUEUE_INSERT_TAIL(&w->watchers, q);
h->cb(h, path, events, 0);
}
+ /* done iterating, time to (maybe) free empty watcher_list */
+ w->iterating = 0;
+ maybe_free_watcher_list(w, loop);
}
}
}
@@ -219,10 +244,11 @@
return -ENOMEM;
w->wd = wd;
w->path = strcpy((char*)(w + 1), path);
QUEUE_INIT(&w->watchers);
+ w->iterating = 0;
RB_INSERT(watcher_root, CAST(&handle->loop->inotify_watchers), w);
no_insert:
uv__handle_start(handle);
QUEUE_INSERT_TAIL(&w->watchers, &handle->watchers);
@@ -246,15 +272,10 @@
handle->wd = -1;
handle->path = NULL;
uv__handle_stop(handle);
QUEUE_REMOVE(&handle->watchers);
- if (QUEUE_EMPTY(&w->watchers)) {
- /* No watchers left for this path. Clean up. */
- RB_REMOVE(watcher_root, CAST(&handle->loop->inotify_watchers), w);
- uv__inotify_rm_watch(handle->loop->inotify_fd, w->wd);
- uv__free(w);
- }
+ maybe_free_watcher_list(w, handle->loop);
return 0;
}