/* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
- Copyright (C) 2012 Free Software Foundation, Inc.
+ Copyright (C) 2012-2013 Free Software Foundation, Inc.
This file is part of GNU Emacs.
You should have received a copy of the GNU General Public License
along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
-/* Design overview:
+/* Written by Eli Zaretskii <eliz@gnu.org>.
+
+ Design overview:
For each watch request, we launch a separate worker thread. The
worker thread runs the watch_worker function, which issues an
- asynchronous call to ReadDirectoryChangesW, and then waits for that
- call to complete in SleepEx. Waiting in SleepEx puts the thread in
- an alertable state, so it wakes up when either (a) the call to
- ReadDirectoryChangesW completes, or (b) the main thread instructs
- the worker thread to terminate by sending it an APC, see below.
+ asynchronous call to ReadDirectoryChangesW, and then waits in
+ SleepEx for that call to complete. Waiting in SleepEx puts the
+ thread in an "alertable" state, so it wakes up when either (a) the
+ call to ReadDirectoryChangesW completes, or (b) the main thread
+ instructs the worker thread to terminate by sending it an APC, see
+ below.
When the ReadDirectoryChangesW call completes, its completion
routine watch_completion is automatically called. watch_completion
bound to a command. The default binding is w32notify-handle-event,
defined on subr.el.
- After w32_read_socket or w32_console_read_socket is done processing
- the notifications, it resets a flag signaling to all watch worker
- threads that the notifications buffer is available for more input.
+ After w32_read_socket or w32_console_read_socket are done
+ processing the notifications, they reset a flag signaling to all
+ watch worker threads that the notifications buffer is available for
+ more input.
When the watch is removed by a call to w32notify-rm-watch, the main
thread requests that the worker thread terminates by queuing an APC
watch_completion function is called one last time with the
ERROR_OPERATION_ABORTED status, which causes it to clean up and set
a flag telling watch_worker to exit without issuing another
- ReadDirectoryChangesW call. The main thread waits for some time
- for the worker thread to exit, and if it doesn't, terminates it
- forcibly. */
+ ReadDirectoryChangesW call. Since watch_worker is the thread
+ procedure of the worker thread, exiting it causes the thread to
+ exit. The main thread waits for some time for the worker thread to
+ exit, and if it doesn't, terminates it forcibly. */
#include <stddef.h>
#include <errno.h>
char *watchee; /* the file we are interested in */
HANDLE dir; /* handle to the watched directory */
HANDLE thr; /* handle to the thread that watches */
- int terminate; /* if non-zero, request for the thread to terminate */
+ volatile int terminate; /* if non-zero, request for the thread to terminate */
unsigned signature;
};
/* Used for communicating notifications to the main thread. */
-int notification_buffer_in_use;
+volatile int notification_buffer_in_use;
BYTE file_notifications[16384];
DWORD notifications_size;
void *notifications_desc;
/* Signal to the main thread that we have file notifications for it to
process. */
static void
-send_notifications (BYTE *info, DWORD info_size, void *desc, int *terminate)
+send_notifications (BYTE *info, DWORD info_size, void *desc,
+ volatile int *terminate)
{
int done = 0;
FRAME_PTR f = SELECTED_FRAME ();
}
}
-/* A completion routine (a.k.a. APC function) for handling events read
- by ReadDirectoryChangesW. Called by the OS when the thread which
- issued the asynchronous ReadDirectoryChangesW call is in the
+/* A completion routine (a.k.a. "APC function") for handling events
+ read by ReadDirectoryChangesW. Called by the OS when the thread
+ which issued the asynchronous ReadDirectoryChangesW call is in the
"alertable state", i.e. waiting inside SleepEx call. */
VOID CALLBACK
watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
/* We've been called because the main thread told us to issue
CancelIo on the directory we watch, and watch_end did so.
The directory handle is already closed. We should clean up
- and exit, signalling to the thread worker routine not to
+ and exit, signaling to the thread worker routine not to
issue another call to ReadDirectoryChangesW. Note that we
- don't free the dirwatch object itself; this is done by the
- main thread in remove_watch. */
- xfree (dirwatch->buf);
- dirwatch->buf = NULL;
- xfree (dirwatch->io_info);
- dirwatch->io_info = NULL;
- xfree (dirwatch->watchee);
- dirwatch->watchee = NULL;
+ don't free the dirwatch object itself nor the memory consumed
+ by its buffers; this is done by the main thread in
+ remove_watch. Calling malloc/free from a thread other than
+ the main thread is a no-no. */
dirwatch->dir = NULL;
dirwatch->terminate = 1;
}
{
DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
/* We cannot remove the dirwatch object from watch_list,
- because we are in a separate thread. So we free and
- zero out all the pointers in the object, but do not
- free the object itself. We also don't touch the
- signature. This way, remove_watch can still identify
- the object, remove it, and free its memory. */
- xfree (dirwatch->buf);
- dirwatch->buf = NULL;
- xfree (dirwatch->io_info);
- dirwatch->io_info = NULL;
+ because we are in a separate thread. For the same
+ reason, we also cannot free memory consumed by the
+ buffers allocated for the dirwatch object. So we close
+ the directory handle, but do not free the object itself
+ or its buffers. We also don't touch the signature.
+ This way, remove_watch can still identify the object,
+ remove it, and free its memory. */
CloseHandle (dirwatch->dir);
dirwatch->dir = NULL;
- xfree (dirwatch->watchee);
- dirwatch->watchee = NULL;
return 1;
}
}
report_file_error ("Cannot watch file", Fcons (file, Qnil));
}
/* Store watch object in watch list. */
- watch_descriptor = make_number (dirwatch);
+ watch_descriptor = XIL ((EMACS_INT)dirwatch);
watch_object = Fcons (watch_descriptor, callback);
watch_list = Fcons (watch_object, watch_list);
(Lisp_Object watch_descriptor)
{
Lisp_Object watch_object;
- struct notification *dirwatch =
- (struct notification *)XINT (watch_descriptor);
+ struct notification *dirwatch;
+ int status = -1;
/* Remove the watch object from watch list. Do this before freeing
the object, do that even if we fail to free it, watch_list is
kept free of junk. */
watch_object = Fassoc (watch_descriptor, watch_list);
if (!NILP (watch_object))
- watch_list = Fdelete (watch_object, watch_list);
+ {
+ watch_list = Fdelete (watch_object, watch_list);
+ dirwatch = (struct notification *)XLI (watch_descriptor);
+ if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
+ status = remove_watch (dirwatch);
+ }
- if (remove_watch (dirwatch) == -1)
+ if (status == -1)
report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
Qnil));
}
Lisp_Object
-w32_get_watch_object (Lisp_Object desc)
+w32_get_watch_object (void *desc)
{
- return NILP (watch_list) ? Qnil : assoc_no_quit (desc, watch_list);
+ Lisp_Object descriptor = XIL ((EMACS_INT)desc);
+
+ /* This is called from the input queue handling code, inside a
+ critical section, so we cannot possibly QUIT if watch_list is not
+ in the right condition. */
+ return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
}
void