]> code.delx.au - gnu-emacs/blob - src/w32notify.c
54d9bcc189a19c0624bed58e345e1f802d52048b
[gnu-emacs] / src / w32notify.c
1 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012-2016 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
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 (at
9 your option) any later version.
10
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.
15
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/>. */
18
19 /* Written by Eli Zaretskii <eliz@gnu.org>.
20
21 Design overview:
22
23 For each watch request, we launch a separate worker thread. The
24 worker thread runs the watch_worker function, which issues an
25 asynchronous call to ReadDirectoryChangesW, and then calls
26 WaitForSingleObjectEx to wait that an event be signaled
27 to terminate the thread.
28 Waiting with WaitForSingleObjectEx puts the thread in an
29 "alertable" state, so it wakes up when either (a) the call to
30 ReadDirectoryChangesW completes, or (b) the main thread instructs
31 the worker thread to terminate by signaling an event, see below.
32
33 When the ReadDirectoryChangesW call completes, its completion
34 routine watch_completion is automatically called. watch_completion
35 stashes the received file events in a linked list used to
36 communicate them to the main thread (using a critical section, so
37 that several threads could alter the same linked list), posts a
38 special message, WM_EMACS_FILENOTIFY, to the Emacs's message queue,
39 and returns. That causes the WaitForSingleObjectEx function call
40 inside watch_worker to return, but the thread won't terminate until
41 the event telling to do so will be signaled. The completion
42 routine issued another call to ReadDirectoryChangesW as quickly as
43 possible. (Except when it does not, see below.)
44
45 In a GUI session, the WM_EMACS_FILENOTIFY message posted to the
46 message queue gets dispatched to the main Emacs window procedure,
47 which queues it for processing by w32_read_socket. When
48 w32_read_socket sees this message, it accesses the linked list with file
49 notifications (using a critical section), extracts the information,
50 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs
51 them into the input event queue to be processed by keyboard.c input
52 machinery (read_char via a call to kbd_buffer_get_event).
53
54 In a non-GUI session, we send the WM_EMACS_FILENOTIFY message to
55 the main (a.k.a. "Lisp") thread instead, since there are no window
56 procedures in console programs. That message wakes up
57 MsgWaitForMultipleObjects inside sys_select, which then signals to
58 its caller that some keyboard input is available. This causes
59 w32_console_read_socket to be called, which accesses the linked list
60 with file notifications and stuffs them into the input event queue
61 for keyboard.c to process.
62
63 When the FILE_NOTIFY_EVENT event is processed by keyboard.c's
64 kbd_buffer_get_event, it is converted to a Lispy event that can be
65 bound to a command. The default binding is file-notify-handle-event,
66 defined on subr.el.
67
68 Routines w32_read_socket or w32_console_read_socket process notifications
69 sets as long as some are available.
70
71 When the watch is removed by a call to w32notify-rm-watch, the main
72 thread requests that the worker thread terminates by signaling the
73 appropriate event and queuing an APC for the worker thread. The
74 APC specifies the watch_end function to be called. watch_end calls
75 CancelIo on the outstanding ReadDirectoryChangesW call. When
76 watch_end returns, the watch_completion function is called one last
77 time with the ERROR_OPERATION_ABORTED status, which causes it to
78 clean up and set a flag telling watch_worker to exit without
79 issuing another ReadDirectoryChangesW call. Since watch_worker is
80 the thread procedure of the worker thread, exiting it causes the
81 thread to exit. The main thread waits for some time for the worker
82 thread to exit, and if it doesn't, terminates it forcibly. */
83
84 #include <stddef.h>
85 #include <errno.h>
86
87 /* must include CRT headers *before* config.h */
88 #include <config.h>
89
90 #include <windows.h>
91
92 #include "lisp.h"
93 #include "w32term.h" /* for enter_crit/leave_crit and WM_EMACS_FILENOTIFY */
94 #include "w32common.h" /* for OS version data */
95 #include "w32.h" /* for w32_strerror */
96 #include "coding.h"
97 #include "keyboard.h"
98 #include "frame.h" /* needed by termhooks.h */
99 #include "termhooks.h" /* for FILE_NOTIFY_EVENT */
100
101 #define DIRWATCH_BUFFER_SIZE 16384
102 #define DIRWATCH_SIGNATURE 0x01233210
103
104 struct notification {
105 BYTE *buf; /* buffer for ReadDirectoryChangesW */
106 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
107 BOOL subtree; /* whether to watch subdirectories */
108 DWORD filter; /* bit mask for events to watch */
109 char *watchee; /* the file we are interested in, UTF-8 encoded */
110 HANDLE dir; /* handle to the watched directory */
111 HANDLE thr; /* handle to the thread that watches */
112 HANDLE terminate; /* event signaling the thread to terminate */
113 unsigned signature;
114 };
115
116 /* Used for communicating notifications to the main thread. */
117 struct notifications_set *notifications_set_head;
118
119 static Lisp_Object watch_list;
120
121 /* Signal to the main thread that we have file notifications for it to
122 process. */
123 static void
124 send_notifications (struct notifications_set *ns)
125 {
126 int done = 0;
127 struct frame *f = SELECTED_FRAME ();
128
129 /* We add the current notification set to the linked list. Use the
130 critical section to make sure only one thread will access the
131 linked list. */
132 enter_crit ();
133 ns->next = notifications_set_head;
134 ns->prev = notifications_set_head->prev;
135 ns->prev->next = ns;
136 notifications_set_head->prev = ns;
137 leave_crit();
138
139 /* If PostMessage fails, the message queue is full. If that
140 happens, the last thing they will worry about is file
141 notifications. So we effectively discard the notification in
142 that case. */
143 if (FRAME_TERMCAP_P (f))
144 /* We send the message to the main (a.k.a. "Lisp") thread, where
145 it will wake up MsgWaitForMultipleObjects inside sys_select,
146 causing it to report that there's some keyboard input
147 available. This will in turn cause w32_console_read_socket to
148 be called, which will pick up the file notifications. */
149 PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0);
150 else if (FRAME_W32_P (f))
151 PostMessage (FRAME_W32_WINDOW (f),
152 WM_EMACS_FILENOTIFY, 0, 0);
153 /* When we are running in batch mode, there's no one to send a
154 message, so we just signal the data is available and hope
155 sys_select will be called soon and will read the data. */
156 else if (FRAME_INITIAL_P (f) && noninteractive)
157 ;
158 }
159
160 /* An APC routine to cancel outstanding directory watch. Invoked by
161 the main thread via QueueUserAPC. This is needed because only the
162 thread that issued the ReadDirectoryChangesW call can call CancelIo
163 to cancel that. (CancelIoEx is only available since Vista, so we
164 cannot use it on XP.) */
165 VOID CALLBACK
166 watch_end (ULONG_PTR arg)
167 {
168 HANDLE hdir = (HANDLE)arg;
169
170 if (hdir && hdir != INVALID_HANDLE_VALUE)
171 CancelIo (hdir);
172 }
173
174 /* A completion routine (a.k.a. "APC function") for handling events
175 read by ReadDirectoryChangesW. Called by the OS when the thread
176 which issued the asynchronous ReadDirectoryChangesW call is in the
177 "alertable state", i.e. waiting inside SleepEx call. */
178 VOID CALLBACK
179 watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
180 {
181 struct notification *dirwatch;
182 DWORD _bytes;
183 struct notifications_set *ns = NULL;
184 BOOL terminate = FALSE;
185
186 /* Who knows what happened? Perhaps the OVERLAPPED structure was
187 freed by someone already? In any case, we cannot do anything
188 with this request, so just punt and skip it. FIXME: should we
189 raise the 'terminate' flag in this case? */
190 if (!io_info)
191 {
192 DebPrint(("watch_completion: io_info is null.\n"));
193 return;
194 }
195
196 /* We have a pointer to our dirwatch structure conveniently stashed
197 away in the hEvent member of the OVERLAPPED struct. According to
198 MSDN documentation of ReadDirectoryChangesW: "The hEvent member
199 of the OVERLAPPED structure is not used by the system, so you can
200 use it yourself." */
201 dirwatch = (struct notification *)io_info->hEvent;
202
203 if (status == ERROR_OPERATION_ABORTED)
204 {
205 /* We've been called because the main thread told us to issue
206 CancelIo on the directory we watch, and watch_end did so.
207 We must exit, without issuing another call to
208 ReadDirectoryChangesW. */
209 return;
210 }
211
212 /* We allocate a new set of notifications to be linked to the linked
213 list of notifications set. This will be processed by Emacs event
214 loop in the main thread. We need to duplicate the notifications
215 buffer, but not the dirwatch structure. */
216
217 /* Implementation note: In general, allocating memory in non-main
218 threads is a no-no in Emacs. We certainly cannot call xmalloc
219 and friends, because it can longjmp when allocation fails, which
220 will crash Emacs because the jmp_buf is set up to a location on
221 the main thread's stack. However, we can call 'malloc' directly,
222 since that is redirected to HeapAlloc that uses our private heap,
223 see w32heap.c, and that is thread-safe. */
224 ns = malloc (sizeof(struct notifications_set));
225 if (ns)
226 {
227 memset (ns, 0, sizeof(struct notifications_set));
228 ns->notifications = malloc (bytes_ret);
229 if (ns->notifications)
230 {
231 memcpy (ns->notifications, dirwatch->buf, bytes_ret);
232 ns->size = bytes_ret;
233 ns->desc = dirwatch;
234 }
235 else
236 {
237 free (ns);
238 ns = NULL;
239 }
240 }
241 if (ns == NULL)
242 DebPrint(("Out of memory. Notifications lost."));
243
244 /* Calling ReadDirectoryChangesW quickly to watch again for new
245 notifications. */
246 if (!ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
247 DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
248 dirwatch->filter, &_bytes, dirwatch->io_info,
249 watch_completion))
250 {
251 DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ()));
252 /* If this call fails, it means that the directory is not
253 watchable any more. We need to terminate the worker thread.
254 Still, we will wait until the current notifications have been
255 sent to the main thread. */
256 terminate = TRUE;
257 }
258
259 if (ns)
260 send_notifications(ns);
261
262 /* If we were asked to terminate the thread, then fire the event. */
263 if (terminate)
264 SetEvent(dirwatch->terminate);
265 }
266
267 /* Worker routine for the watch thread. */
268 static DWORD WINAPI
269 watch_worker (LPVOID arg)
270 {
271 struct notification *dirwatch = (struct notification *)arg;
272 BOOL bErr;
273 DWORD _bytes = 0;
274 DWORD status;
275
276 if (dirwatch->dir)
277 {
278 bErr = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf,
279 DIRWATCH_BUFFER_SIZE, dirwatch->subtree,
280 dirwatch->filter, &_bytes,
281 dirwatch->io_info, watch_completion);
282 if (!bErr)
283 {
284 DebPrint (("ReadDirectoryChangesW: %lu\n", GetLastError ()));
285 /* We cannot remove the dirwatch object from watch_list,
286 because we are in a separate thread. For the same
287 reason, we also cannot free memory consumed by the
288 buffers allocated for the dirwatch object. So we close
289 the directory handle, but do not free the object itself
290 or its buffers. We also don't touch the signature. This
291 way, remove_watch can still identify the object, remove
292 it, and free its memory. */
293 CloseHandle (dirwatch->dir);
294 dirwatch->dir = NULL;
295 return 1;
296 }
297 }
298
299 do {
300 status = WaitForSingleObjectEx(dirwatch->terminate, INFINITE, TRUE);
301 } while (status == WAIT_IO_COMPLETION);
302
303 /* The thread is about to terminate, so we clean up the dir handle. */
304 CloseHandle (dirwatch->dir);
305 dirwatch->dir = NULL;
306
307 return 0;
308 }
309 /* Launch a thread to watch changes to FILE in a directory open on
310 handle HDIR. */
311 static struct notification *
312 start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
313 {
314 struct notification *dirwatch = xzalloc (sizeof (struct notification));
315
316 dirwatch->signature = DIRWATCH_SIGNATURE;
317 dirwatch->buf = xmalloc (DIRWATCH_BUFFER_SIZE);
318 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
319 /* Stash a pointer to dirwatch structure for use by the completion
320 routine. According to MSDN documentation of ReadDirectoryChangesW:
321 "The hEvent member of the OVERLAPPED structure is not used by the
322 system, so you can use it yourself." */
323 dirwatch->io_info->hEvent = dirwatch;
324 dirwatch->subtree = subdirs;
325 dirwatch->filter = flags;
326 dirwatch->watchee = xstrdup (file);
327
328 dirwatch->terminate = CreateEvent(NULL, FALSE, FALSE, NULL);
329
330 dirwatch->dir = hdir;
331
332 /* See w32proc.c where it calls CreateThread for the story behind
333 the 2nd and 5th argument in the call to CreateThread. */
334 dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
335 0x00010000, NULL);
336
337 if (!dirwatch->thr)
338 {
339 CloseHandle(dirwatch->terminate);
340 xfree (dirwatch->buf);
341 xfree (dirwatch->io_info);
342 xfree (dirwatch->watchee);
343 xfree (dirwatch);
344 }
345 return dirwatch;
346 }
347
348 /* Called from the main thread to start watching FILE in PARENT_DIR,
349 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
350 PARENT_DIR as well. Value is a pointer to 'struct notification'
351 used by the thread that watches the changes. */
352 static struct notification *
353 add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
354 {
355 HANDLE hdir;
356 struct notification *dirwatch = NULL;
357
358 if (!file)
359 return NULL;
360
361 if (w32_unicode_filenames)
362 {
363 wchar_t dir_w[MAX_PATH], file_w[MAX_PATH];
364
365 filename_to_utf16 (parent_dir, dir_w);
366 if (*file)
367 filename_to_utf16 (file, file_w);
368 else
369 file_w[0] = 0;
370
371 hdir = CreateFileW (dir_w,
372 FILE_LIST_DIRECTORY,
373 /* FILE_SHARE_DELETE doesn't preclude other
374 processes from deleting files inside
375 parent_dir. */
376 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
377 NULL, OPEN_EXISTING,
378 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
379 NULL);
380 }
381 else
382 {
383 char dir_a[MAX_PATH], file_a[MAX_PATH];
384
385 filename_to_ansi (parent_dir, dir_a);
386 if (*file)
387 filename_to_ansi (file, file_a);
388 else
389 file_a[0] = '\0';
390
391 hdir = CreateFileA (dir_a,
392 FILE_LIST_DIRECTORY,
393 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
394 NULL, OPEN_EXISTING,
395 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
396 NULL);
397 }
398 if (hdir == INVALID_HANDLE_VALUE)
399 return NULL;
400
401 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
402 {
403 CloseHandle (hdir);
404 dirwatch->dir = NULL;
405 }
406
407 return dirwatch;
408 }
409
410 /* Stop watching a directory specified by a pointer to its dirwatch object. */
411 static int
412 remove_watch (struct notification *dirwatch)
413 {
414 if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
415 {
416 int i;
417 BOOL status;
418 DWORD exit_code = 0, err;
419
420 /* Only the thread that issued the outstanding I/O call can call
421 CancelIo on it. (CancelIoEx is available only since Vista.)
422 So we need to queue an APC for the worker thread telling it
423 to terminate. */
424 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
425 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
426
427 /* We also signal the thread that it can terminate. */
428 SetEvent(dirwatch->terminate);
429
430 /* Wait for the thread to exit. FIXME: is there a better method
431 that is not overly complex? */
432 for (i = 0; i < 50; i++)
433 {
434 if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
435 && exit_code == STILL_ACTIVE))
436 break;
437 Sleep (10);
438 }
439
440 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
441 || exit_code == STILL_ACTIVE)
442 {
443 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
444 {
445 DebPrint(("Forcing thread termination.\n"));
446 TerminateThread (dirwatch->thr, 0);
447 if (dirwatch->dir)
448 CloseHandle (dirwatch->dir);
449 }
450 }
451
452 /* Clean up. */
453 if (dirwatch->thr)
454 {
455 CloseHandle (dirwatch->thr);
456 dirwatch->thr = NULL;
457 }
458 CloseHandle(dirwatch->terminate);
459 xfree (dirwatch->buf);
460 xfree (dirwatch->io_info);
461 xfree (dirwatch->watchee);
462 xfree (dirwatch);
463 return 0;
464 }
465 else
466 {
467 DebPrint (("Unknown dirwatch object!\n"));
468 return -1;
469 }
470 }
471
472 static DWORD
473 filter_list_to_flags (Lisp_Object filter_list)
474 {
475 DWORD flags = 0;
476
477 if (NILP (filter_list))
478 return flags;
479
480 if (!NILP (Fmember (Qfile_name, filter_list)))
481 flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
482 if (!NILP (Fmember (Qdirectory_name, filter_list)))
483 flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
484 if (!NILP (Fmember (Qattributes, filter_list)))
485 flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
486 if (!NILP (Fmember (Qsize, filter_list)))
487 flags |= FILE_NOTIFY_CHANGE_SIZE;
488 if (!NILP (Fmember (Qlast_write_time, filter_list)))
489 flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
490 if (!NILP (Fmember (Qlast_access_time, filter_list)))
491 flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
492 if (!NILP (Fmember (Qcreation_time, filter_list)))
493 flags |= FILE_NOTIFY_CHANGE_CREATION;
494 if (!NILP (Fmember (Qsecurity_desc, filter_list)))
495 flags |= FILE_NOTIFY_CHANGE_SECURITY;
496
497 return flags;
498 }
499
500 DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
501 Sw32notify_add_watch, 3, 3, 0,
502 doc: /* Add a watch for filesystem events pertaining to FILE.
503
504 This arranges for filesystem events pertaining to FILE to be reported
505 to Emacs. Use `w32notify-rm-watch' to cancel the watch.
506
507 Value is a descriptor for the added watch. If the file cannot be
508 watched for some reason, this function signals a `file-error' error.
509
510 FILTER is a list of conditions for reporting an event. It can include
511 the following symbols:
512
513 'file-name' -- report file creation, deletion, or renaming
514 'directory-name' -- report directory creation, deletion, or renaming
515 'attributes' -- report changes in attributes
516 'size' -- report changes in file-size
517 'last-write-time' -- report changes in last-write time
518 'last-access-time' -- report changes in last-access time
519 'creation-time' -- report changes in creation time
520 'security-desc' -- report changes in security descriptor
521
522 If FILE is a directory, and FILTER includes 'subtree', then all the
523 subdirectories will also be watched and changes in them reported.
524
525 When any event happens that satisfies the conditions specified by
526 FILTER, Emacs will call the CALLBACK function passing it a single
527 argument EVENT, which is of the form
528
529 (DESCRIPTOR ACTION FILE)
530
531 DESCRIPTOR is the same object as the one returned by this function.
532 ACTION is the description of the event. It could be any one of the
533 following:
534
535 'added' -- FILE was added
536 'removed' -- FILE was deleted
537 'modified' -- FILE's contents or its attributes were modified
538 'renamed-from' -- a file was renamed whose old name was FILE
539 'renamed-to' -- a file was renamed and its new name is FILE
540
541 FILE is the name of the file whose event is being reported.
542
543 Note that some networked filesystems, such as Samba-mounted Unix
544 volumes, might not send notifications about file changes. In these
545 cases, this function will return a valid descriptor, but notifications
546 will never come in. Volumes shared from remote Windows machines do
547 generate notifications correctly, though. */)
548 (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
549 {
550 Lisp_Object dirfn, basefn, watch_object, watch_descriptor;
551 DWORD flags;
552 BOOL subdirs = FALSE;
553 struct notification *dirwatch = NULL;
554 Lisp_Object lisp_errstr;
555 char *errstr;
556
557 CHECK_LIST (filter);
558
559 /* The underlying features are available only since XP. */
560 if (os_subtype == OS_9X
561 || (w32_major_version == 5 && w32_minor_version < 1))
562 {
563 errno = ENOSYS;
564 report_file_notify_error ("Watching filesystem events is not supported",
565 Qnil);
566 }
567
568 /* filenotify.el always passes us a directory, either the parent
569 directory of a file to be watched, or the directory to be
570 watched. */
571 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
572 if (NILP (Ffile_directory_p (file)))
573 {
574 /* This should only happen if we are called directly, not via
575 filenotify.el. If BASEFN is empty, the argument was the root
576 directory on its drive. */
577 dirfn = ENCODE_FILE (Ffile_name_directory (file));
578 basefn = ENCODE_FILE (Ffile_name_nondirectory (file));
579 if (*SDATA (basefn) == '\0')
580 subdirs = TRUE;
581 }
582 else
583 {
584 dirfn = ENCODE_FILE (file);
585 basefn = Qnil;
586 }
587
588 if (!NILP (Fmember (Qsubtree, filter)))
589 subdirs = TRUE;
590
591 flags = filter_list_to_flags (filter);
592
593 dirwatch = add_watch (SSDATA (dirfn), NILP (basefn) ? "" : SSDATA (basefn),
594 subdirs, flags);
595 if (!dirwatch)
596 {
597 DWORD err = GetLastError ();
598
599 errno = EINVAL;
600 if (err)
601 {
602 errstr = w32_strerror (err);
603 if (!NILP (Vlocale_coding_system))
604 lisp_errstr
605 = code_convert_string_norecord (build_unibyte_string (errstr),
606 Vlocale_coding_system, 0);
607 else
608 lisp_errstr = build_string (errstr);
609 report_file_notify_error ("Cannot watch file",
610 Fcons (lisp_errstr, Fcons (file, Qnil)));
611 }
612 else
613 report_file_notify_error ("Cannot watch file", Fcons (file, Qnil));
614 }
615 /* Store watch object in watch list. */
616 watch_descriptor = make_pointer_integer (dirwatch);
617 watch_object = Fcons (watch_descriptor, callback);
618 watch_list = Fcons (watch_object, watch_list);
619
620 return watch_descriptor;
621 }
622
623 DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
624 Sw32notify_rm_watch, 1, 1, 0,
625 doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
626
627 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
628 (Lisp_Object watch_descriptor)
629 {
630 Lisp_Object watch_object;
631 struct notification *dirwatch;
632 int status = -1;
633
634 /* Remove the watch object from watch list. Do this before freeing
635 the object, do that even if we fail to free it, watch_list is
636 kept free of junk. */
637 watch_object = Fassoc (watch_descriptor, watch_list);
638 if (!NILP (watch_object))
639 {
640 watch_list = Fdelete (watch_object, watch_list);
641 dirwatch = (struct notification *)XINTPTR (watch_descriptor);
642 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
643 status = remove_watch (dirwatch);
644 }
645
646 if (status == -1)
647 report_file_notify_error ("Invalid watch descriptor",
648 Fcons (watch_descriptor, Qnil));
649
650 return Qnil;
651 }
652
653 Lisp_Object
654 w32_get_watch_object (void *desc)
655 {
656 Lisp_Object descriptor = make_pointer_integer (desc);
657
658 /* This is called from the input queue handling code, inside a
659 critical section, so we cannot possibly QUIT if watch_list is not
660 in the right condition. */
661 return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
662 }
663
664 DEFUN ("w32notify-valid-p", Fw32notify_valid_p, Sw32notify_valid_p, 1, 1, 0,
665 doc: /* "Check a watch specified by its WATCH-DESCRIPTOR for validity.
666
667 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.
668
669 A watch can become invalid if the directory it watches is deleted, or if
670 the watcher thread exits abnormally for any other reason. Removing the
671 watch by calling `w32notify-rm-watch' also makes it invalid. */)
672 (Lisp_Object watch_descriptor)
673 {
674 Lisp_Object watch_object = Fassoc (watch_descriptor, watch_list);
675
676 if (!NILP (watch_object))
677 {
678 struct notification *dirwatch =
679 (struct notification *)XINTPTR (watch_descriptor);
680 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification))
681 && dirwatch->dir != NULL)
682 return Qt;
683 }
684
685 return Qnil;
686 }
687
688 void
689 globals_of_w32notify (void)
690 {
691 watch_list = Qnil;
692 }
693
694 void
695 syms_of_w32notify (void)
696 {
697 DEFSYM (Qfile_name, "file-name");
698 DEFSYM (Qdirectory_name, "directory-name");
699 DEFSYM (Qattributes, "attributes");
700 DEFSYM (Qlast_write_time, "last-write-time");
701 DEFSYM (Qlast_access_time, "last-access-time");
702 DEFSYM (Qcreation_time, "creation-time");
703 DEFSYM (Qsecurity_desc, "security-desc");
704 DEFSYM (Qsubtree, "subtree");
705
706 defsubr (&Sw32notify_add_watch);
707 defsubr (&Sw32notify_rm_watch);
708 defsubr (&Sw32notify_valid_p);
709
710 staticpro (&watch_list);
711
712 Fprovide (intern_c_string ("w32notify"), Qnil);
713 }