]> code.delx.au - gnu-emacs/blob - src/w32notify.c
Merge from trunk after 24.3 was moved to emacs-24 branch.
[gnu-emacs] / src / w32notify.c
1 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
2 Copyright (C) 2012 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
9 (at 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 /* Design overview:
20
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.
28
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.)
38
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).
47
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.
56
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,
60 defined on subr.el.
61
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.
65
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
77 forcibly. */
78
79 #include <stddef.h>
80 #include <errno.h>
81
82 /* must include CRT headers *before* config.h */
83 #include <config.h>
84
85 #include <windows.h>
86
87 #include "lisp.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 */
91 #include "coding.h"
92 #include "keyboard.h"
93 #include "frame.h" /* needed by termhooks.h */
94 #include "termhooks.h" /* for FILE_NOTIFY_EVENT */
95
96 #define DIRWATCH_SIGNATURE 0x01233210
97
98 struct notification {
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 */
107 unsigned signature;
108 };
109
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;
115
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;
119
120 /* Signal to the main thread that we have file notifications for it to
121 process. */
122 static void
123 send_notifications (BYTE *info, DWORD info_size, void *desc,
124 volatile int *terminate)
125 {
126 int done = 0;
127 FRAME_PTR f = SELECTED_FRAME ();
128
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.
136
137 FIXME: is there a better way of dealing with this? */
138 while (!done && !*terminate)
139 {
140 enter_crit ();
141 if (!notification_buffer_in_use)
142 {
143 if (info_size)
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))
159 || (FRAME_W32_P (f)
160 && PostMessage (FRAME_W32_WINDOW (f),
161 WM_EMACS_FILENOTIFY, 0, 0)))
162 notification_buffer_in_use = 1;
163 done = 1;
164 }
165 leave_crit ();
166 if (!done)
167 Sleep (5);
168 }
169 }
170
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.) */
176 VOID CALLBACK
177 watch_end (ULONG_PTR arg)
178 {
179 HANDLE hdir = (HANDLE)arg;
180
181 if (hdir && hdir != INVALID_HANDLE_VALUE)
182 {
183 CancelIo (hdir);
184 CloseHandle (hdir);
185 }
186 }
187
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. */
192 VOID CALLBACK
193 watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
194 {
195 struct notification *dirwatch;
196
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? */
201 if (!io_info)
202 return;
203
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
208 use it yourself." */
209 dirwatch = (struct notification *)io_info->hEvent;
210 if (status == ERROR_OPERATION_ABORTED)
211 {
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 nor the memory consumed
218 by its buffers; this is done by the main thread in
219 remove_watch. Calling malloc/free from a thread other than
220 the main thread is a no-no. */
221 dirwatch->dir = NULL;
222 dirwatch->terminate = 1;
223 }
224 else
225 {
226 /* Tell the main thread we have notifications for it. */
227 send_notifications (dirwatch->buf, bytes_ret, dirwatch,
228 &dirwatch->terminate);
229 }
230 }
231
232 /* Worker routine for the watch thread. */
233 static DWORD WINAPI
234 watch_worker (LPVOID arg)
235 {
236 struct notification *dirwatch = (struct notification *)arg;
237
238 do {
239 BOOL status;
240 DWORD sleep_result;
241 DWORD bytes_ret = 0;
242
243 if (dirwatch->dir)
244 {
245 status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384,
246 dirwatch->subtree, dirwatch->filter,
247 &bytes_ret,
248 dirwatch->io_info, watch_completion);
249 if (!status)
250 {
251 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
252 /* We cannot remove the dirwatch object from watch_list,
253 because we are in a separate thread. For the same
254 reason, we also cannot free memory consumed by the
255 buffers allocated for the dirwatch object. So we close
256 the directory handle, but do not free the object itself
257 or its buffers. We also don't touch the signature.
258 This way, remove_watch can still identify the object,
259 remove it, and free its memory. */
260 CloseHandle (dirwatch->dir);
261 dirwatch->dir = NULL;
262 return 1;
263 }
264 }
265 /* Sleep indefinitely until awoken by the I/O completion, which
266 could be either a change notification or a cancellation of the
267 watch. */
268 sleep_result = SleepEx (INFINITE, TRUE);
269 } while (!dirwatch->terminate);
270
271 return 0;
272 }
273
274 /* Launch a thread to watch changes to FILE in a directory open on
275 handle HDIR. */
276 static struct notification *
277 start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
278 {
279 struct notification *dirwatch = xzalloc (sizeof (struct notification));
280 HANDLE thr;
281
282 dirwatch->signature = DIRWATCH_SIGNATURE;
283 dirwatch->buf = xmalloc (16384);
284 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
285 /* Stash a pointer to dirwatch structure for use by the completion
286 routine. According to MSDN documentation of ReadDirectoryChangesW:
287 "The hEvent member of the OVERLAPPED structure is not used by the
288 system, so you can use it yourself." */
289 dirwatch->io_info->hEvent = dirwatch;
290 dirwatch->subtree = subdirs;
291 dirwatch->filter = flags;
292 dirwatch->watchee = xstrdup (file);
293 dirwatch->terminate = 0;
294 dirwatch->dir = hdir;
295
296 /* See w32proc.c where it calls CreateThread for the story behind
297 the 2nd and 5th argument in the call to CreateThread. */
298 dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
299 0x00010000, NULL);
300
301 if (!dirwatch->thr)
302 {
303 xfree (dirwatch->buf);
304 xfree (dirwatch->io_info);
305 xfree (dirwatch->watchee);
306 xfree (dirwatch);
307 dirwatch = NULL;
308 }
309 return dirwatch;
310 }
311
312 /* Called from the main thread to start watching FILE in PARENT_DIR,
313 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
314 PARENT_DIR as well. Value is a pointer to 'struct notification'
315 used by the thread that watches the changes. */
316 static struct notification *
317 add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
318 {
319 HANDLE hdir;
320 struct notification *dirwatch = NULL;
321
322 if (!file || !*file)
323 return NULL;
324
325 hdir = CreateFile (parent_dir,
326 FILE_LIST_DIRECTORY,
327 /* FILE_SHARE_DELETE doesn't preclude other
328 processes from deleting files inside
329 parent_dir. */
330 FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
331 NULL, OPEN_EXISTING,
332 FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED,
333 NULL);
334 if (hdir == INVALID_HANDLE_VALUE)
335 return NULL;
336
337 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
338 CloseHandle (hdir);
339
340 return dirwatch;
341 }
342
343 /* Stop watching a directory specified by a pointer to its dirwatch object. */
344 static int
345 remove_watch (struct notification *dirwatch)
346 {
347 if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
348 {
349 int i;
350 BOOL status;
351 DWORD exit_code, err;
352
353 /* Only the thread that issued the outstanding I/O call can call
354 CancelIo on it. (CancelIoEx is available only since Vista.)
355 So we need to queue an APC for the worker thread telling it
356 to terminate. */
357 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
358 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
359 /* We also set the terminate flag, for when the thread is
360 waiting on the critical section that never gets acquired.
361 FIXME: is there a cleaner method? Using SleepEx there is a
362 no-no, as that will lead to recursive APC invocations and
363 stack overflow. */
364 dirwatch->terminate = 1;
365 /* Wait for the thread to exit. FIXME: is there a better method
366 that is not overly complex? */
367 for (i = 0; i < 50; i++)
368 {
369 if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
370 && exit_code == STILL_ACTIVE))
371 break;
372 Sleep (10);
373 }
374 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
375 || exit_code == STILL_ACTIVE)
376 {
377 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
378 {
379 TerminateThread (dirwatch->thr, 0);
380 if (dirwatch->dir)
381 CloseHandle (dirwatch->dir);
382 }
383 }
384
385 /* Clean up. */
386 if (dirwatch->thr)
387 {
388 CloseHandle (dirwatch->thr);
389 dirwatch->thr = NULL;
390 }
391 xfree (dirwatch->buf);
392 xfree (dirwatch->io_info);
393 xfree (dirwatch->watchee);
394 xfree (dirwatch);
395
396 return 0;
397 }
398 else
399 {
400 DebPrint (("Unknown dirwatch object!\n"));
401 return -1;
402 }
403 }
404
405 static DWORD
406 filter_list_to_flags (Lisp_Object filter_list)
407 {
408 DWORD flags = 0;
409
410 if (NILP (filter_list))
411 return flags;
412
413 if (!NILP (Fmember (Qfile_name, filter_list)))
414 flags |= FILE_NOTIFY_CHANGE_FILE_NAME;
415 if (!NILP (Fmember (Qdirectory_name, filter_list)))
416 flags |= FILE_NOTIFY_CHANGE_DIR_NAME;
417 if (!NILP (Fmember (Qattributes, filter_list)))
418 flags |= FILE_NOTIFY_CHANGE_ATTRIBUTES;
419 if (!NILP (Fmember (Qsize, filter_list)))
420 flags |= FILE_NOTIFY_CHANGE_SIZE;
421 if (!NILP (Fmember (Qlast_write_time, filter_list)))
422 flags |= FILE_NOTIFY_CHANGE_LAST_WRITE;
423 if (!NILP (Fmember (Qlast_access_time, filter_list)))
424 flags |= FILE_NOTIFY_CHANGE_LAST_ACCESS;
425 if (!NILP (Fmember (Qcreation_time, filter_list)))
426 flags |= FILE_NOTIFY_CHANGE_CREATION;
427 if (!NILP (Fmember (Qsecurity_desc, filter_list)))
428 flags |= FILE_NOTIFY_CHANGE_SECURITY;
429
430 return flags;
431 }
432
433 DEFUN ("w32notify-add-watch", Fw32notify_add_watch,
434 Sw32notify_add_watch, 3, 3, 0,
435 doc: /* Add a watch for filesystem events pertaining to FILE.
436
437 This arranges for filesystem events pertaining to FILE to be reported
438 to Emacs. Use `w32notify-rm-watch' to cancel the watch.
439
440 Value is a descriptor for the added watch, or nil if the file
441 cannot be watched.
442
443 FILTER is a list of conditions for reporting an event. It can include
444 the following symbols:
445
446 'file-name' -- report file creation, deletion, or renaming
447 'directory-name' -- report directory creation, deletion, or renaming
448 'attributes' -- report changes in attributes
449 'size' -- report changes in file-size
450 'last-write-time' -- report changes in last-write time
451 'last-access-time' -- report changes in last-access time
452 'creation-time' -- report changes in creation time
453 'security-desc' -- report changes in security descriptor
454
455 If FILE is a directory, and FILTER includes 'subtree', then all the
456 subdirectories will also be watched and changes in them reported.
457
458 When any event happens that satisfies the conditions specified by
459 FILTER, Emacs will call the CALLBACK function passing it a single
460 argument EVENT, which is of the form
461
462 (DESCRIPTOR ACTION FILE)
463
464 DESCRIPTOR is the same object as the one returned by this function.
465 ACTION is the description of the event. It could be any one of the
466 following:
467
468 'added' -- FILE was added
469 'removed' -- FILE was deleted
470 'modified' -- FILE's contents or its attributes were modified
471 'renamed-from' -- a file was renamed whose old name was FILE
472 'renamed-to' -- a file was renamed and its new name is FILE
473
474 FILE is the name of the file whose event is being reported. */)
475 (Lisp_Object file, Lisp_Object filter, Lisp_Object callback)
476 {
477 Lisp_Object encoded_file, watch_object, watch_descriptor;
478 char parent_dir[MAX_PATH], *basename;
479 size_t fn_len;
480 DWORD flags;
481 BOOL subdirs = FALSE;
482 struct notification *dirwatch = NULL;
483 Lisp_Object lisp_errstr;
484 char *errstr;
485
486 CHECK_LIST (filter);
487
488 /* The underlying features are available only since XP. */
489 if (os_subtype == OS_9X
490 || (w32_major_version == 5 && w32_major_version < 1))
491 {
492 errno = ENOSYS;
493 report_file_error ("Watching filesystem events is not supported",
494 Qnil);
495 }
496
497 /* We need a full absolute file name of FILE, and we need to remove
498 any trailing slashes from it, so that GetFullPathName below gets
499 the basename part correctly. */
500 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
501 encoded_file = ENCODE_FILE (file);
502
503 fn_len = GetFullPathName (SDATA (encoded_file), MAX_PATH, parent_dir,
504 &basename);
505 if (!fn_len)
506 {
507 errstr = w32_strerror (0);
508 errno = EINVAL;
509 if (!NILP (Vlocale_coding_system))
510 lisp_errstr
511 = code_convert_string_norecord (build_unibyte_string (errstr),
512 Vlocale_coding_system, 0);
513 else
514 lisp_errstr = build_string (errstr);
515 report_file_error ("GetFullPathName failed",
516 Fcons (lisp_errstr, Fcons (file, Qnil)));
517 }
518 /* We need the parent directory without the slash that follows it.
519 If BASENAME is NULL, the argument was the root directory on its
520 drive. */
521 if (basename)
522 basename[-1] = '\0';
523 else
524 subdirs = TRUE;
525
526 if (!NILP (Fmember (Qsubtree, filter)))
527 subdirs = TRUE;
528
529 flags = filter_list_to_flags (filter);
530
531 dirwatch = add_watch (parent_dir, basename, subdirs, flags);
532 if (!dirwatch)
533 {
534 DWORD err = GetLastError ();
535
536 errno = EINVAL;
537 if (err)
538 {
539 errstr = w32_strerror (err);
540 if (!NILP (Vlocale_coding_system))
541 lisp_errstr
542 = code_convert_string_norecord (build_unibyte_string (errstr),
543 Vlocale_coding_system, 0);
544 else
545 lisp_errstr = build_string (errstr);
546 report_file_error ("Cannot watch file",
547 Fcons (lisp_errstr, Fcons (file, Qnil)));
548 }
549 else
550 report_file_error ("Cannot watch file", Fcons (file, Qnil));
551 }
552 /* Store watch object in watch list. */
553 watch_descriptor = XIL ((EMACS_INT)dirwatch);
554 watch_object = Fcons (watch_descriptor, callback);
555 watch_list = Fcons (watch_object, watch_list);
556
557 return watch_descriptor;
558 }
559
560 DEFUN ("w32notify-rm-watch", Fw32notify_rm_watch,
561 Sw32notify_rm_watch, 1, 1, 0,
562 doc: /* Remove an existing watch specified by its WATCH-DESCRIPTOR.
563
564 WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
565 (Lisp_Object watch_descriptor)
566 {
567 Lisp_Object watch_object;
568 struct notification *dirwatch;
569 int status = -1;
570
571 /* Remove the watch object from watch list. Do this before freeing
572 the object, do that even if we fail to free it, watch_list is
573 kept free of junk. */
574 watch_object = Fassoc (watch_descriptor, watch_list);
575 if (!NILP (watch_object))
576 {
577 watch_list = Fdelete (watch_object, watch_list);
578 dirwatch = (struct notification *)XLI (watch_descriptor);
579 if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
580 status = remove_watch (dirwatch);
581 }
582
583 if (status == -1)
584 report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
585 Qnil));
586
587 return Qnil;
588 }
589
590 Lisp_Object
591 w32_get_watch_object (void *desc)
592 {
593 Lisp_Object descriptor = XIL ((EMACS_INT)desc);
594
595 /* This is called from the input queue handling code, inside a
596 critical section, so we cannot possibly QUIT if watch_list is not
597 in the right condition. */
598 return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
599 }
600
601 void
602 globals_of_w32notify (void)
603 {
604 watch_list = Qnil;
605 }
606
607 void
608 syms_of_w32notify (void)
609 {
610 DEFSYM (Qfile_name, "file-name");
611 DEFSYM (Qdirectory_name, "directory-name");
612 DEFSYM (Qattributes, "attributes");
613 DEFSYM (Qsize, "size");
614 DEFSYM (Qlast_write_time, "last-write-time");
615 DEFSYM (Qlast_access_time, "last-access-time");
616 DEFSYM (Qcreation_time, "creation-time");
617 DEFSYM (Qsecurity_desc, "security-desc");
618 DEFSYM (Qsubtree, "subtree");
619
620 defsubr (&Sw32notify_add_watch);
621 defsubr (&Sw32notify_rm_watch);
622
623 staticpro (&watch_list);
624
625 Fprovide (intern_c_string ("w32notify"), Qnil);
626 }