aboutsummaryrefslogtreecommitdiffstats
path: root/src/w32notify.c
diff options
context:
space:
mode:
authorEli Zaretskii2012-10-14 16:38:33 +0200
committerEli Zaretskii2012-10-14 16:38:33 +0200
commit37a4dabe8aaa51e15cf63e43710ee4b8f35d434b (patch)
treef2e68e1b5822f726e9b1d12c96b732bff607fad3 /src/w32notify.c
parentdd8c2f5adeba029790a007ec829e18442a4ade36 (diff)
downloademacs-37a4dabe8aaa51e15cf63e43710ee4b8f35d434b.tar.gz
emacs-37a4dabe8aaa51e15cf63e43710ee4b8f35d434b.zip
More than one watch is now supported.
Need to fix the issue with descriptor, now a pointer converted to Lisp integer.
Diffstat (limited to 'src/w32notify.c')
-rw-r--r--src/w32notify.c158
1 files changed, 87 insertions, 71 deletions
diff --git a/src/w32notify.c b/src/w32notify.c
index bdfdf3472a0..244b0b872bf 100644
--- a/src/w32notify.c
+++ b/src/w32notify.c
@@ -93,6 +93,8 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
93#include "frame.h" /* needed by termhooks.h */ 93#include "frame.h" /* needed by termhooks.h */
94#include "termhooks.h" /* for FILE_NOTIFY_EVENT */ 94#include "termhooks.h" /* for FILE_NOTIFY_EVENT */
95 95
96#define DIRWATCH_SIGNATURE 0x01233210
97
96struct notification { 98struct notification {
97 BYTE *buf; /* buffer for ReadDirectoryChangesW */ 99 BYTE *buf; /* buffer for ReadDirectoryChangesW */
98 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */ 100 OVERLAPPED *io_info; /* the OVERLAPPED structure for async I/O */
@@ -102,17 +104,14 @@ struct notification {
102 HANDLE dir; /* handle to the watched directory */ 104 HANDLE dir; /* handle to the watched directory */
103 HANDLE thr; /* handle to the thread that watches */ 105 HANDLE thr; /* handle to the thread that watches */
104 int terminate; /* if non-zero, request for the thread to terminate */ 106 int terminate; /* if non-zero, request for the thread to terminate */
107 unsigned signature;
105}; 108};
106 109
107/* FIXME: this needs to be changed to support more that one request at
108 a time. */
109static struct notification dirwatch;
110
111/* Used for communicating notifications to the main thread. */ 110/* Used for communicating notifications to the main thread. */
112int notification_buffer_in_use; 111int notification_buffer_in_use;
113BYTE file_notifications[16384]; 112BYTE file_notifications[16384];
114DWORD notifications_size; 113DWORD notifications_size;
115HANDLE notifications_desc; 114void *notifications_desc;
116 115
117static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize; 116static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize;
118static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time; 117static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time;
@@ -121,12 +120,11 @@ static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;
121/* Signal to the main thread that we have file notifications for it to 120/* Signal to the main thread that we have file notifications for it to
122 process. */ 121 process. */
123static void 122static void
124send_notifications (BYTE *info, DWORD info_size, HANDLE hdir, int *terminate) 123send_notifications (BYTE *info, DWORD info_size, void *desc, int *terminate)
125{ 124{
126 int done = 0; 125 int done = 0;
127 FRAME_PTR f = SELECTED_FRAME (); 126 FRAME_PTR f = SELECTED_FRAME ();
128 127
129
130 /* A single buffer is used to communicate all notifications to the 128 /* A single buffer is used to communicate all notifications to the
131 main thread. Since both the main thread and several watcher 129 main thread. Since both the main thread and several watcher
132 threads could be active at the same time, we use a critical area 130 threads could be active at the same time, we use a critical area
@@ -144,7 +142,7 @@ send_notifications (BYTE *info, DWORD info_size, HANDLE hdir, int *terminate)
144 if (info_size) 142 if (info_size)
145 memcpy (file_notifications, info, info_size); 143 memcpy (file_notifications, info, info_size);
146 notifications_size = info_size; 144 notifications_size = info_size;
147 notifications_desc = hdir; 145 notifications_desc = desc;
148 /* If PostMessage fails, the message queue is full. If that 146 /* If PostMessage fails, the message queue is full. If that
149 happens, the last thing they will worry about is file 147 happens, the last thing they will worry about is file
150 notifications. So we effectively discard the 148 notifications. So we effectively discard the
@@ -214,7 +212,9 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
214 CancelIo on the directory we watch, and watch_end did so. 212 CancelIo on the directory we watch, and watch_end did so.
215 The directory handle is already closed. We should clean up 213 The directory handle is already closed. We should clean up
216 and exit, signalling to the thread worker routine not to 214 and exit, signalling to the thread worker routine not to
217 issue another call to ReadDirectoryChangesW. */ 215 issue another call to ReadDirectoryChangesW. Note that we
216 don't free the dirwatch object itself; this is done by the
217 main thread in remove_watch. */
218 xfree (dirwatch->buf); 218 xfree (dirwatch->buf);
219 dirwatch->buf = NULL; 219 dirwatch->buf = NULL;
220 xfree (dirwatch->io_info); 220 xfree (dirwatch->io_info);
@@ -227,7 +227,7 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
227 else 227 else
228 { 228 {
229 /* Tell the main thread we have notifications for it. */ 229 /* Tell the main thread we have notifications for it. */
230 send_notifications (dirwatch->buf, bytes_ret, dirwatch->dir, 230 send_notifications (dirwatch->buf, bytes_ret, dirwatch,
231 &dirwatch->terminate); 231 &dirwatch->terminate);
232 } 232 }
233} 233}
@@ -252,6 +252,12 @@ watch_worker (LPVOID arg)
252 if (!status) 252 if (!status)
253 { 253 {
254 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ())); 254 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
255 /* We cannot remove the dirwatch object from watch_list,
256 because we are in a separate thread. So we free and
257 zero out all the pointers in the object, but do not
258 free the object itself. We also don't touch the
259 signature. This way, remove_watch can still identify
260 the object, remove it, and free its memory. */
255 xfree (dirwatch->buf); 261 xfree (dirwatch->buf);
256 dirwatch->buf = NULL; 262 dirwatch->buf = NULL;
257 xfree (dirwatch->io_info); 263 xfree (dirwatch->io_info);
@@ -274,50 +280,51 @@ watch_worker (LPVOID arg)
274 280
275/* Launch a thread to watch changes to FILE in a directory open on 281/* Launch a thread to watch changes to FILE in a directory open on
276 handle HDIR. */ 282 handle HDIR. */
277static int 283static struct notification *
278start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags) 284start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
279{ 285{
280 dirwatch.buf = xmalloc (16384); 286 struct notification *dirwatch = xzalloc (sizeof (struct notification));
281 dirwatch.io_info = xzalloc (sizeof(OVERLAPPED)); 287 HANDLE thr;
288
289 dirwatch->signature = DIRWATCH_SIGNATURE;
290 dirwatch->buf = xmalloc (16384);
291 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
282 /* Stash a pointer to dirwatch structure for use by the completion 292 /* Stash a pointer to dirwatch structure for use by the completion
283 routine. According to MSDN documentation of ReadDirectoryChangesW: 293 routine. According to MSDN documentation of ReadDirectoryChangesW:
284 "The hEvent member of the OVERLAPPED structure is not used by the 294 "The hEvent member of the OVERLAPPED structure is not used by the
285 system, so you can use it yourself." */ 295 system, so you can use it yourself." */
286 dirwatch.io_info->hEvent = &dirwatch; 296 dirwatch->io_info->hEvent = dirwatch;
287 dirwatch.subtree = subdirs; 297 dirwatch->subtree = subdirs;
288 dirwatch.filter = flags; 298 dirwatch->filter = flags;
289 dirwatch.watchee = xstrdup (file); 299 dirwatch->watchee = xstrdup (file);
290 dirwatch.terminate = 0; 300 dirwatch->terminate = 0;
291 dirwatch.dir = hdir; 301 dirwatch->dir = hdir;
292 302
293 /* See w32proc.c where it calls CreateThread for the story behind 303 /* See w32proc.c where it calls CreateThread for the story behind
294 the 2nd and 5th argument in the call to CreateThread. */ 304 the 2nd and 5th argument in the call to CreateThread. */
295 dirwatch.thr = CreateThread (NULL, 64 * 1024, watch_worker, 305 dirwatch->thr = CreateThread (NULL, 64 * 1024, watch_worker, (void *)dirwatch,
296 (void *)&dirwatch, 0x00010000, NULL); 306 0x00010000, NULL);
297 307
298 if (!dirwatch.thr) 308 if (!dirwatch->thr)
299 { 309 {
300 dirwatch.terminate = 1; 310 xfree (dirwatch->buf);
301 xfree (dirwatch.buf); 311 xfree (dirwatch->io_info);
302 dirwatch.buf = NULL; 312 xfree (dirwatch->watchee);
303 xfree (dirwatch.io_info); 313 xfree (dirwatch);
304 dirwatch.io_info = NULL; 314 dirwatch = NULL;
305 xfree (dirwatch.watchee);
306 dirwatch.watchee = NULL;
307 dirwatch.dir = NULL;
308 return -1;
309 } 315 }
310 return 0; 316 return dirwatch;
311} 317}
312 318
313/* Called from the main thread to start watching FILE in PARENT_DIR, 319/* Called from the main thread to start watching FILE in PARENT_DIR,
314 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of 320 subject to FLAGS. If SUBDIRS is TRUE, watch the subdirectories of
315 PARENT_DIR as well. Value is the handle on which the directory is 321 PARENT_DIR as well. Value is a pointer to 'struct notification'
316 open. */ 322 used by the thread that watches the changes. */
317static HANDLE * 323static struct notification *
318add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags) 324add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
319{ 325{
320 HANDLE hdir; 326 HANDLE hdir;
327 struct notification *dirwatch = NULL;
321 328
322 if (!file || !*file) 329 if (!file || !*file)
323 return NULL; 330 return NULL;
@@ -334,18 +341,17 @@ add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
334 if (hdir == INVALID_HANDLE_VALUE) 341 if (hdir == INVALID_HANDLE_VALUE)
335 return NULL; 342 return NULL;
336 343
337 if (start_watching (file, hdir, subdirs, flags) == 0) 344 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
338 return hdir; 345 CloseHandle (hdir);
339 346
340 CloseHandle (hdir); 347 return dirwatch;
341 return NULL;
342} 348}
343 349
344/* Stop watching a directory specified by its handle HDIR. */ 350/* Stop watching a directory specified by a pointer to its dirwatch object. */
345static int 351static int
346remove_watch (HANDLE hdir) 352remove_watch (struct notification *dirwatch)
347{ 353{
348 if (hdir == dirwatch.dir) 354 if (dirwatch && dirwatch->signature == DIRWATCH_SIGNATURE)
349 { 355 {
350 int i; 356 int i;
351 BOOL status; 357 BOOL status;
@@ -355,19 +361,19 @@ remove_watch (HANDLE hdir)
355 CancelIo on it. (CancelIoEx is available only since Vista.) 361 CancelIo on it. (CancelIoEx is available only since Vista.)
356 So we need to queue an APC for the worker thread telling it 362 So we need to queue an APC for the worker thread telling it
357 to terminate. */ 363 to terminate. */
358 if (!QueueUserAPC (watch_end, dirwatch.thr, (ULONG_PTR)dirwatch.dir)) 364 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
359 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ())); 365 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
360 /* We also set the terminate flag, for when the thread is 366 /* We also set the terminate flag, for when the thread is
361 waiting on the critical section that never gets acquired. 367 waiting on the critical section that never gets acquired.
362 FIXME: is there a cleaner method? Using SleepEx there is a 368 FIXME: is there a cleaner method? Using SleepEx there is a
363 no-no, as that will lead to recursive APC invocations and 369 no-no, as that will lead to recursive APC invocations and
364 stack overflow. */ 370 stack overflow. */
365 dirwatch.terminate = 1; 371 dirwatch->terminate = 1;
366 /* Wait for the thread to exit. FIXME: is there a better method 372 /* Wait for the thread to exit. FIXME: is there a better method
367 that is not overly complex? */ 373 that is not overly complex? */
368 for (i = 0; i < 50; i++) 374 for (i = 0; i < 50; i++)
369 { 375 {
370 if (!((status = GetExitCodeThread (dirwatch.thr, &exit_code)) 376 if (!((status = GetExitCodeThread (dirwatch->thr, &exit_code))
371 && exit_code == STILL_ACTIVE)) 377 && exit_code == STILL_ACTIVE))
372 break; 378 break;
373 Sleep (10); 379 Sleep (10);
@@ -376,25 +382,29 @@ remove_watch (HANDLE hdir)
376 || exit_code == STILL_ACTIVE) 382 || exit_code == STILL_ACTIVE)
377 { 383 {
378 if (!(status == FALSE && err == ERROR_INVALID_HANDLE)) 384 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
379 TerminateThread (dirwatch.thr, 0); 385 {
386 TerminateThread (dirwatch->thr, 0);
387 if (dirwatch->dir)
388 CloseHandle (dirwatch->dir);
389 }
380 } 390 }
381 391
382 /* Clean up. */ 392 /* Clean up. */
383 if (dirwatch.thr) 393 if (dirwatch->thr)
384 { 394 {
385 CloseHandle (dirwatch.thr); 395 CloseHandle (dirwatch->thr);
386 dirwatch.thr = NULL; 396 dirwatch->thr = NULL;
387 } 397 }
388 return 0; 398 xfree (dirwatch->buf);
389 } 399 xfree (dirwatch->io_info);
390 else if (!dirwatch.dir) 400 xfree (dirwatch->watchee);
391 { 401 xfree (dirwatch);
392 DebPrint (("Directory handle already closed!\n")); 402
393 return 0; 403 return 0;
394 } 404 }
395 else 405 else
396 { 406 {
397 DebPrint (("Unknown directory handle!\n")); 407 DebPrint (("Unknown dirwatch object!\n"));
398 return -1; 408 return -1;
399 } 409 }
400} 410}
@@ -474,9 +484,9 @@ FILE is the name of the file whose event is being reported. */)
474 Lisp_Object encoded_file, watch_object, watch_descriptor; 484 Lisp_Object encoded_file, watch_object, watch_descriptor;
475 char parent_dir[MAX_PATH], *basename; 485 char parent_dir[MAX_PATH], *basename;
476 size_t fn_len; 486 size_t fn_len;
477 HANDLE hdir;
478 DWORD flags; 487 DWORD flags;
479 BOOL subdirs = FALSE; 488 BOOL subdirs = FALSE;
489 struct notification *dirwatch = NULL;
480 Lisp_Object lisp_errstr; 490 Lisp_Object lisp_errstr;
481 char *errstr; 491 char *errstr;
482 492
@@ -491,10 +501,7 @@ FILE is the name of the file whose event is being reported. */)
491 Qnil); 501 Qnil);
492 } 502 }
493 503
494 if (dirwatch.dir) 504 /* We need a full absolute file name of FILE, and we need to remove
495 error ("File watch already active");
496
497 /* We needa full absolute file name of FILE, and we need to remove
498 any trailing slashes from it, so that GetFullPathName below gets 505 any trailing slashes from it, so that GetFullPathName below gets
499 the basename part correctly. */ 506 the basename part correctly. */
500 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil)); 507 file = Fdirectory_file_name (Fexpand_file_name (file, Qnil));
@@ -528,8 +535,8 @@ FILE is the name of the file whose event is being reported. */)
528 535
529 flags = filter_list_to_flags (filter); 536 flags = filter_list_to_flags (filter);
530 537
531 hdir = add_watch (parent_dir, basename, subdirs, flags); 538 dirwatch = add_watch (parent_dir, basename, subdirs, flags);
532 if (!hdir) 539 if (!dirwatch)
533 { 540 {
534 DWORD err = GetLastError (); 541 DWORD err = GetLastError ();
535 542
@@ -550,7 +557,7 @@ FILE is the name of the file whose event is being reported. */)
550 report_file_error ("Cannot watch file", Fcons (file, Qnil)); 557 report_file_error ("Cannot watch file", Fcons (file, Qnil));
551 } 558 }
552 /* Store watch object in watch list. */ 559 /* Store watch object in watch list. */
553 watch_descriptor = make_number (hdir); 560 watch_descriptor = make_number (dirwatch);
554 watch_object = Fcons (watch_descriptor, callback); 561 watch_object = Fcons (watch_descriptor, callback);
555 watch_list = Fcons (watch_object, watch_list); 562 watch_list = Fcons (watch_object, watch_list);
556 563
@@ -565,24 +572,33 @@ WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'. */)
565 (Lisp_Object watch_descriptor) 572 (Lisp_Object watch_descriptor)
566{ 573{
567 Lisp_Object watch_object; 574 Lisp_Object watch_object;
568 HANDLE hdir = (HANDLE)XINT (watch_descriptor); 575 struct notification *dirwatch =
576 (struct notification *)XINT (watch_descriptor);
569 577
570 if (remove_watch (hdir) == -1) 578 /* Remove the watch object from watch list. Do this before freeing
571 report_file_error ("Could not remove watch", Fcons (watch_descriptor, 579 the object, do that even if we fail to free it, watch_list is
572 Qnil)); 580 kept free of junk. */
573
574 /* Remove watch descriptor from watch list. */
575 watch_object = Fassoc (watch_descriptor, watch_list); 581 watch_object = Fassoc (watch_descriptor, watch_list);
576 if (!NILP (watch_object)) 582 if (!NILP (watch_object))
577 watch_list = Fdelete (watch_object, watch_list); 583 watch_list = Fdelete (watch_object, watch_list);
578 584
585 if (remove_watch (dirwatch) == -1)
586 report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
587 Qnil));
588
579 return Qnil; 589 return Qnil;
580} 590}
581 591
582Lisp_Object 592Lisp_Object
583get_watch_object (Lisp_Object desc) 593w32_get_watch_object (Lisp_Object desc)
594{
595 return NILP (watch_list) ? Qnil : assoc_no_quit (desc, watch_list);
596}
597
598void
599globals_of_w32notify (void)
584{ 600{
585 return Fassoc (desc, watch_list); 601 watch_list = Qnil;
586} 602}
587 603
588void 604void