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