1 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
21 For each watch request, we launch a separate worker thread. The
22 worker thread runs the watch_worker function, which issues an
23 asynchronous call to ReadDirectoryChangesW, and then waits for that
24 call to complete in SleepEx. Waiting in SleepEx puts the thread in
25 an alertable state, so it wakes up when either (a) the call to
26 ReadDirectoryChangesW completes, or (b) the main thread instructs
27 the worker thread to terminate by sending it an APC, see below.
29 When the ReadDirectoryChangesW call completes, its completion
30 routine watch_completion is automatically called. watch_completion
31 stashes the received file events in a buffer used to communicate
32 them to the main thread (using a critical section, so that several
33 threads could use the same buffer), posts a special message,
34 WM_EMACS_FILENOTIFY, to the Emacs's message queue, and returns.
35 That causes the SleepEx function call inside watch_worker to
36 return, and watch_worker then issues another call to
37 ReadDirectoryChangesW. (Except when it does not, see below.)
39 In a GUI session, The WM_EMACS_FILENOTIFY message, posted to the
40 message queue gets dispatched to the main Emacs window procedure,
41 which queues it for processing by w32_read_socket. When
42 w32_read_socket sees this message, it accesses the buffer with file
43 notifications (using a critical section), extracts the information,
44 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
45 them into the input event queue to be processed by keyboard.c input
46 machinery (read_char via a call to kbd_buffer_get_event).
48 In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
49 the main (a.k.a. "Lisp") thread instead, since there are no window
50 procedures in console programs. That message wakes up
51 MsgWaitForMultipleObjects inside sys_select, which then signals to
52 its caller that some keyboard input is available. This causes
53 w32_console_read_socket to be called, which accesses the buffer
54 with file notifications and stuffs them into the input event queue
55 for keyboard.c to process.
57 When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
58 kbd_buffer_get_event, it is converted to a Lispy event that can be
59 bound to a command. The default binding is w32notify-handle-event,
62 After w32_read_socket or w32_console_read_socket is done processing
63 the notifications, it resets a flag signaling to all watch worker
64 threads that the notifications buffer is available for more input.
66 When the watch is removed by a call to w32notify-rm-watch, the main
67 thread requests that the worker thread terminates by queuing an APC
68 for the worker thread. The APC specifies the watch_end function to
69 be called. watch_end calls CancelIo on the outstanding
70 ReadDirectoryChangesW call and closes the handle on which the
71 watched directory was open. When watch_end returns, the
72 watch_completion function is called one last time with the
73 ERROR_OPERATION_ABORTED status, which causes it to clean up and set
74 a flag telling watch_worker to exit without issuing another
75 ReadDirectoryChangesW call. The main thread waits for some time
76 for the worker thread to exit, and if it doesn't, terminates it
82 /* must include CRT headers *before* config.h */
88 #include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
89 #include "w32common.h" /* for OS version data */
90 #include "w32.h" /* for w32_strerror */
93 #include "frame.h" /* needed by termhooks.h */
94 #include "termhooks.h" /* for FILE_NOTIFY_EVENT */
96 #define DIRWATCH_SIGNATURE 0x01233210
99 BYTE
*buf
; /* buffer for ReadDirectoryChangesW */
100 OVERLAPPED
*io_info
; /* the OVERLAPPED structure for async I/O */
101 BOOL subtree
; /* whether to watch subdirectories */
102 DWORD filter
; /* bit mask for events to watch */
103 char *watchee
; /* the file we are interested in */
104 HANDLE dir
; /* handle to the watched directory */
105 HANDLE thr
; /* handle to the thread that watches */
106 volatile int terminate
; /* if non-zero, request for the thread to terminate */
110 /* Used for communicating notifications to the main thread. */
111 volatile int notification_buffer_in_use
;
112 BYTE file_notifications
[16384];
113 DWORD notifications_size
;
114 void *notifications_desc
;
116 static Lisp_Object Qfile_name
, Qdirectory_name
, Qattributes
, Qsize
;
117 static Lisp_Object Qlast_write_time
, Qlast_access_time
, Qcreation_time
;
118 static Lisp_Object Qsecurity_desc
, Qsubtree
, watch_list
;
120 /* Signal to the main thread that we have file notifications for it to
123 send_notifications (BYTE
*info
, DWORD info_size
, void *desc
,
124 volatile int *terminate
)
127 FRAME_PTR f
= SELECTED_FRAME ();
129 /* A single buffer is used to communicate all notifications to the
130 main thread. Since both the main thread and several watcher
131 threads could be active at the same time, we use a critical area
132 and an "in-use" flag to synchronize them. A watcher thread can
133 only put its notifications in the buffer if it acquires the
134 critical area and finds the "in-use" flag reset. The main thread
135 resets the flag after it is done processing notifications.
137 FIXME: is there a better way of dealing with this? */
138 while (!done
&& !*terminate
)
141 if (!notification_buffer_in_use
)
144 memcpy (file_notifications
, info
, info_size
);
145 notifications_size
= info_size
;
146 notifications_desc
= desc
;
147 /* If PostMessage fails, the message queue is full. If that
148 happens, the last thing they will worry about is file
149 notifications. So we effectively discard the
150 notification in that case. */
151 if ((FRAME_TERMCAP_P (f
)
152 /* We send the message to the main (a.k.a. "Lisp")
153 thread, where it will wake up MsgWaitForMultipleObjects
154 inside sys_select, causing it to report that there's
155 some keyboard input available. This will in turn cause
156 w32_console_read_socket to be called, which will pick
157 up the file notifications. */
158 && PostThreadMessage (dwMainThreadId
, WM_EMACS_FILENOTIFY
, 0, 0))
160 && PostMessage (FRAME_W32_WINDOW (f
),
161 WM_EMACS_FILENOTIFY
, 0, 0)))
162 notification_buffer_in_use
= 1;
171 /* An APC routine to cancel outstanding directory watch. Invoked by
172 the main thread via QueueUserAPC. This is needed because only the
173 thread that issued the ReadDirectoryChangesW call can call CancelIo
174 to cancel that. (CancelIoEx is only available since Vista, so we
175 cannot use it on XP.) */
177 watch_end (ULONG_PTR arg
)
179 HANDLE hdir
= (HANDLE
)arg
;
181 if (hdir
&& hdir
!= INVALID_HANDLE_VALUE
)
188 /* A completion routine (a.k.a. APC function) for handling events read
189 by ReadDirectoryChangesW. Called by the OS when the thread which
190 issued the asynchronous ReadDirectoryChangesW call is in the
191 "alertable state", i.e. waiting inside SleepEx call. */
193 watch_completion (DWORD status
, DWORD bytes_ret
, OVERLAPPED
*io_info
)
195 struct notification
*dirwatch
;
197 /* Who knows what happened? Perhaps the OVERLAPPED structure was
198 freed by someone already? In any case, we cannot do anything
199 with this request, so just punt and skip it. FIXME: should we
200 raise the 'terminate' flag in this case? */
204 /* We have a pointer to our dirwatch structure conveniently stashed
205 away in the hEvent member of the OVERLAPPED struct. According to
206 MSDN documentation of ReadDirectoryChangesW: "The hEvent member
207 of the OVERLAPPED structure is not used by the system, so you can
209 dirwatch
= (struct notification
*)io_info
->hEvent
;
210 if (status
== ERROR_OPERATION_ABORTED
)
212 /* We've been called because the main thread told us to issue
213 CancelIo on the directory we watch, and watch_end did so.
214 The directory handle is already closed. We should clean up
215 and exit, signalling to the thread worker routine not to
216 issue another call to ReadDirectoryChangesW. Note that we
217 don't free the dirwatch object itself; this is done by the
218 main thread in remove_watch. */
219 xfree (dirwatch
->buf
);
220 dirwatch
->buf
= NULL
;
221 xfree (dirwatch
->io_info
);
222 dirwatch
->io_info
= NULL
;
223 xfree (dirwatch
->watchee
);
224 dirwatch
->watchee
= NULL
;
225 dirwatch
->dir
= NULL
;
226 dirwatch
->terminate
= 1;
230 /* Tell the main thread we have notifications for it. */
231 send_notifications (dirwatch
->buf
, bytes_ret
, dirwatch
,
232 &dirwatch
->terminate
);
236 /* Worker routine for the watch thread. */
238 watch_worker (LPVOID arg
)
240 struct notification
*dirwatch
= (struct notification
*)arg
;
249 status
= ReadDirectoryChangesW (dirwatch
->dir
, dirwatch
->buf
, 16384,
250 dirwatch
->subtree
, dirwatch
->filter
,
252 dirwatch
->io_info
, watch_completion
);
255 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
256 /* We cannot remove the dirwatch object from watch_list,
257 because we are in a separate thread. So we free and
258 zero out all the pointers in the object, but do not
259 free the object itself. We also don't touch the
260 signature. This way, remove_watch can still identify
261 the object, remove it, and free its memory. */
262 xfree (dirwatch
->buf
);
263 dirwatch
->buf
= NULL
;
264 xfree (dirwatch
->io_info
);
265 dirwatch
->io_info
= NULL
;
266 CloseHandle (dirwatch
->dir
);
267 dirwatch
->dir
= NULL
;
268 xfree (dirwatch
->watchee
);
269 dirwatch
->watchee
= NULL
;
273 /* Sleep indefinitely until awoken by the I/O completion, which
274 could be either a change notification or a cancellation of the
276 sleep_result
= SleepEx (INFINITE
, TRUE
);
277 } while (!dirwatch
->terminate
);
282 /* Launch a thread to watch changes to FILE in a directory open on
284 static struct notification
*
285 start_watching (const char *file
, HANDLE hdir
, BOOL subdirs
, DWORD flags
)
287 struct notification
*dirwatch
= xzalloc (sizeof (struct notification
));
290 dirwatch
->signature
= DIRWATCH_SIGNATURE
;
291 dirwatch
->buf
= xmalloc (16384);
292 dirwatch
->io_info
= xzalloc (sizeof(OVERLAPPED
));
293 /* Stash a pointer to dirwatch structure for use by the completion
294 routine. According to MSDN documentation of ReadDirectoryChangesW:
295 "The hEvent member of the OVERLAPPED structure is not used by the
296 system, so you can use it yourself." */
297 dirwatch
->io_info
->hEvent
= dirwatch
;
298 dirwatch
->subtree
= subdirs
;
299 dirwatch
->filter
= flags
;
300 dirwatch
->watchee
= xstrdup (file
);
301 dirwatch
->terminate
= 0;
302 dirwatch
->dir
= hdir
;
304 /* See w32proc.c where it calls CreateThread for the story behind
305 the 2nd and 5th argument in the call to CreateThread. */
306 dirwatch
->thr
= CreateThread (NULL
, 64 * 1024, watch_worker
, (void *)dirwatch
,
311 xfree (dirwatch
->buf
);
312 xfree (dirwatch
->io_info
);
313 xfree (dirwatch
->watchee
);
320 /* Called from the main thread to start watching FILE in PARENT_DIR,
321 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
322 PARENT_DIR as well. Value is a pointer to 'struct notification'
323 used by the thread that watches the changes. */
324 static struct notification
*
325 add_watch (const char *parent_dir
, const char *file
, BOOL subdirs
, DWORD flags
)
328 struct notification
*dirwatch
= NULL
;
333 hdir
= CreateFile (parent_dir
,
335 /* FILE_SHARE_DELETE doesn't preclude other
336 processes from deleting files inside
338 FILE_SHARE_READ
| FILE_SHARE_WRITE
| FILE_SHARE_DELETE
,
340 FILE_FLAG_BACKUP_SEMANTICS
| FILE_FLAG_OVERLAPPED
,
342 if (hdir
== INVALID_HANDLE_VALUE
)
345 if ((dirwatch
= start_watching (file
, hdir
, subdirs
, flags
)) == NULL
)
351 /* Stop watching a directory specified by a pointer to its dirwatch object. */
353 remove_watch (struct notification
*dirwatch
)
355 if (dirwatch
&& dirwatch
->signature
== DIRWATCH_SIGNATURE
)
359 DWORD exit_code
, err
;
361 /* Only the thread that issued the outstanding I/O call can call
362 CancelIo on it. (CancelIoEx is available only since Vista.)
363 So we need to queue an APC for the worker thread telling it
365 if (!QueueUserAPC (watch_end
, dirwatch
->thr
, (ULONG_PTR
)dirwatch
->dir
))
366 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
367 /* We also set the terminate flag, for when the thread is
368 waiting on the critical section that never gets acquired.
369 FIXME: is there a cleaner method? Using SleepEx there is a
370 no-no, as that will lead to recursive APC invocations and
372 dirwatch
->terminate
= 1;
373 /* Wait for the thread to exit. FIXME: is there a better method
374 that is not overly complex? */
375 for (i
= 0; i
< 50; i
++)
377 if (!((status
= GetExitCodeThread (dirwatch
->thr
, &exit_code
))
378 && exit_code
== STILL_ACTIVE
))
382 if ((status
== FALSE
&& (err
= GetLastError ()) == ERROR_INVALID_HANDLE
)
383 || exit_code
== STILL_ACTIVE
)
385 if (!(status
== FALSE
&& err
== ERROR_INVALID_HANDLE
))
387 TerminateThread (dirwatch
->thr
, 0);
389 CloseHandle (dirwatch
->dir
);
396 CloseHandle (dirwatch
->thr
);
397 dirwatch
->thr
= NULL
;
399 xfree (dirwatch
->buf
);
400 xfree (dirwatch
->io_info
);
401 xfree (dirwatch
->watchee
);
408 DebPrint (("Unknown dirwatch object!\n"));
414 filter_list_to_flags (Lisp_Object filter_list
)
418 if (NILP (filter_list
))
421 if (!NILP (Fmember (Qfile_name
, filter_list
)))
422 flags
|= FILE_NOTIFY_CHANGE_FILE_NAME
;
423 if (!NILP (Fmember (Qdirectory_name
, filter_list
)))
424 flags
|= FILE_NOTIFY_CHANGE_DIR_NAME
;
425 if (!NILP (Fmember (Qattributes
, filter_list
)))
426 flags
|= FILE_NOTIFY_CHANGE_ATTRIBUTES
;
427 if (!NILP (Fmember (Qsize
, filter_list
)))
428 flags
|= FILE_NOTIFY_CHANGE_SIZE
;
429 if (!NILP (Fmember (Qlast_write_time
, filter_list
)))
430 flags
|= FILE_NOTIFY_CHANGE_LAST_WRITE
;
431 if (!NILP (Fmember (Qlast_access_time
, filter_list
)))
432 flags
|= FILE_NOTIFY_CHANGE_LAST_ACCESS
;
433 if (!NILP (Fmember (Qcreation_time
, filter_list
)))
434 flags
|= FILE_NOTIFY_CHANGE_CREATION
;
435 if (!NILP (Fmember (Qsecurity_desc
, filter_list
)))
436 flags
|= FILE_NOTIFY_CHANGE_SECURITY
;
441 DEFUN ("w32notify-add-watch", Fw32notify_add_watch
,
442 Sw32notify_add_watch
, 3, 3, 0,
443 doc
: /* Add a watch for filesystem events pertaining to FILE.
445 This arranges for filesystem events pertaining to FILE to be reported
446 to Emacs. Use `w32notify-rm-watch' to cancel the watch.
448 Value is a descriptor for the added watch, or nil if the file
451 FILTER is a list of conditions for reporting an event. It can include
452 the following symbols:
454 'file-name' -- report file creation, deletion, or renaming
455 'directory-name' -- report directory creation, deletion, or renaming
456 'attributes' -- report changes in attributes
457 'size' -- report changes in file-size
458 'last-write-time' -- report changes in last-write time
459 'last-access-time' -- report changes in last-access time
460 'creation-time' -- report changes in creation time
461 'security-desc' -- report changes in security descriptor
463 If FILE is a directory, and FILTER includes 'subtree', then all the
464 subdirectories will also be watched and changes in them reported.
466 When any event happens that satisfies the conditions specified by
467 FILTER, Emacs will call the CALLBACK function passing it a single
468 argument EVENT, which is of the form
470 (DESCRIPTOR ACTION FILE)
472 DESCRIPTOR is the same object as the one returned by this function.
473 ACTION is the description of the event. It could be any one of the
476 'added' -- FILE was added
477 'removed' -- FILE was deleted
478 'modified' -- FILE's contents or its attributes were modified
479 'renamed-from' -- a file was renamed whose old name was FILE
480 'renamed-to' -- a file was renamed and its new name is FILE
482 FILE is the name of the file whose event is being reported. */)
483 (Lisp_Object file
, Lisp_Object filter
, Lisp_Object callback
)
485 Lisp_Object encoded_file
, watch_object
, watch_descriptor
;
486 char parent_dir
[MAX_PATH
], *basename
;
489 BOOL subdirs
= FALSE
;
490 struct notification
*dirwatch
= NULL
;
491 Lisp_Object lisp_errstr
;
496 /* The underlying features are available only since XP. */
497 if (os_subtype
== OS_9X
498 || (w32_major_version
== 5 && w32_major_version
< 1))
501 report_file_error ("Watching filesystem events is not supported",
505 /* We need a full absolute file name of FILE, and we need to remove
506 any trailing slashes from it, so that GetFullPathName below gets
507 the basename part correctly. */
508 file
= Fdirectory_file_name (Fexpand_file_name (file
, Qnil
));
509 encoded_file
= ENCODE_FILE (file
);
511 fn_len
= GetFullPathName (SDATA (encoded_file
), MAX_PATH
, parent_dir
,
515 errstr
= w32_strerror (0);
517 if (!NILP (Vlocale_coding_system
))
519 = code_convert_string_norecord (build_unibyte_string (errstr
),
520 Vlocale_coding_system
, 0);
522 lisp_errstr
= build_string (errstr
);
523 report_file_error ("GetFullPathName failed",
524 Fcons (lisp_errstr
, Fcons (file
, Qnil
)));
526 /* We need the parent directory without the slash that follows it.
527 If BASENAME is NULL, the argument was the root directory on its
534 if (!NILP (Fmember (Qsubtree
, filter
)))
537 flags
= filter_list_to_flags (filter
);
539 dirwatch
= add_watch (parent_dir
, basename
, subdirs
, flags
);
542 DWORD err
= GetLastError ();
547 errstr
= w32_strerror (err
);
548 if (!NILP (Vlocale_coding_system
))
550 = code_convert_string_norecord (build_unibyte_string (errstr
),
551 Vlocale_coding_system
, 0);
553 lisp_errstr
= build_string (errstr
);
554 report_file_error ("Cannot watch file",
555 Fcons (lisp_errstr
, Fcons (file
, Qnil
)));
558 report_file_error ("Cannot watch file", Fcons (file
, Qnil
));
560 /* Store watch object in watch list. */
561 watch_descriptor
= XIL ((EMACS_INT
)dirwatch
);
562 watch_object
= Fcons (watch_descriptor
, callback
);
563 watch_list
= Fcons (watch_object
, watch_list
);
565 return watch_descriptor
;
568 DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch
,
569 Sw32notify_rm_watch
, 1, 1, 0,
570 doc
: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
572 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
573 (Lisp_Object watch_descriptor
)
575 Lisp_Object watch_object
;
576 struct notification
*dirwatch
;
579 /* Remove the watch object from watch list. Do this before freeing
580 the object, do that even if we fail to free it, watch_list is
581 kept free of junk. */
582 watch_object
= Fassoc (watch_descriptor
, watch_list
);
583 if (!NILP (watch_object
))
585 watch_list
= Fdelete (watch_object
, watch_list
);
586 dirwatch
= (struct notification
*)XLI (watch_descriptor
);
587 if (w32_valid_pointer_p (dirwatch
, sizeof(struct notification
)))
588 status
= remove_watch (dirwatch
);
592 report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor
,
599 w32_get_watch_object (void *desc
)
601 Lisp_Object descriptor
= XIL ((EMACS_INT
)desc
);
603 /* This is called from the input queue handling code, so we cannot
604 possibly QUIT if watch_list is not in the right condition. */
605 return NILP (watch_list
) ? Qnil
: assoc_no_quit (descriptor
, watch_list
);
609 globals_of_w32notify (void)
615 syms_of_w32notify (void)
617 DEFSYM (Qfile_name
, "file-name");
618 DEFSYM (Qdirectory_name
, "directory-name");
619 DEFSYM (Qattributes
, "attributes");
620 DEFSYM (Qsize
, "size");
621 DEFSYM (Qlast_write_time
, "last-write-time");
622 DEFSYM (Qlast_access_time
, "last-access-time");
623 DEFSYM (Qcreation_time
, "creation-time");
624 DEFSYM (Qsecurity_desc
, "security-desc");
625 DEFSYM (Qsubtree
, "subtree");
627 defsubr (&Sw32notify_add_watch
);
628 defsubr (&Sw32notify_rm_watch
);
630 staticpro (&watch_list
);
632 Fprovide (intern_c_string ("w32notify"), Qnil
);