aboutsummaryrefslogtreecommitdiffstats
path: root/src/w32notify.c
diff options
context:
space:
mode:
authorFabrice Popineau2016-03-19 14:44:53 +0200
committerEli Zaretskii2016-03-19 14:44:53 +0200
commit326fff41fa9f674d80be00b5c97c44f8043bbace (patch)
tree15f5e2fb315130ce923970d16a96c8e951914857 /src/w32notify.c
parent2fbdb1bb4c878f8ae17bd69d1b4ff51c47497e41 (diff)
downloademacs-326fff41fa9f674d80be00b5c97c44f8043bbace.tar.gz
emacs-326fff41fa9f674d80be00b5c97c44f8043bbace.zip
Improve w32notify notifications
* src/w32notify.c (DIRWATCH_BUFFER_SIZE): New macro. (struct notification): 'terminate' is now a HANDLE. (send_notifications): Argument is now a pointer to a notification. Don't loop waiting for the notification to be acknowledged by the main thread; instead, just add the notification to the linked list of notifications waiting to be acknowledged. (watch_end): Don't close the directory handle. (watch_completion): Allocate a new notification structure to be added to the notifications set. Call ReadDirectoryChangesW immediately after adding the new notification, and before sending a message to the main thread about them. (watch_worker): Don't loop calling ReadDirectoryChangesW; instead, call it just once -- it will be called again in watch_completion. Loop waiting for the main thread's indication to terminate. (start_watching): Create the event to be used to indicate to the worker thread that its should terminate. (remove_watch): Indicate to the worker thread that it should terminate. * src/w32term.c (queue_notifications): Loop over all the notifications in the linked list, processing all of them in one go. * src/w32inevt.c (handle_file_notifications): Loop over all the notifications in the linked list. * src/w32xfns.c (init_crit): Initialize the linked list of file notifications. (delete_crit): Free the linked list of file notifications, including any unprocessed notifications left in it. * src/w32term.h (struct notifications_se): New struct. * test/lisp/filenotify-tests.el (file-notify-test02-events) (file-notify-test05-dir-validity): Add read-event calls to facilitate event recognition by the main thread in batch mode.
Diffstat (limited to 'src/w32notify.c')
-rw-r--r--src/w32notify.c320
1 files changed, 176 insertions, 144 deletions
diff --git a/src/w32notify.c b/src/w32notify.c
index 586c2062f62..54d9bcc189a 100644
--- a/src/w32notify.c
+++ b/src/w32notify.c
@@ -22,27 +22,30 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
22 22
23 For each watch request, we launch a separate worker thread. The 23 For each watch request, we launch a separate worker thread. The
24 worker thread runs the watch_worker function, which issues an 24 worker thread runs the watch_worker function, which issues an
25 asynchronous call to ReadDirectoryChangesW, and then waits in 25 asynchronous call to ReadDirectoryChangesW, and then calls
26 SleepEx for that call to complete. Waiting in SleepEx puts the 26 WaitForSingleObjectEx to wait that an event be signaled
27 thread in an "alertable" state, so it wakes up when either (a) the 27 to terminate the thread.
28 call to ReadDirectoryChangesW completes, or (b) the main thread 28 Waiting with WaitForSingleObjectEx puts the thread in an
29 instructs the worker thread to terminate by sending it an APC, see 29 "alertable" state, so it wakes up when either (a) the call to
30 below. 30 ReadDirectoryChangesW completes, or (b) the main thread instructs
31 the worker thread to terminate by signaling an event, see below.
31 32
32 When the ReadDirectoryChangesW call completes, its completion 33 When the ReadDirectoryChangesW call completes, its completion
33 routine watch_completion is automatically called. watch_completion 34 routine watch_completion is automatically called. watch_completion
34 stashes the received file events in a buffer used to communicate 35 stashes the received file events in a linked list used to
35 them to the main thread (using a critical section, so that several 36 communicate them to the main thread (using a critical section, so
36 threads could use the same buffer), posts a special message, 37 that several threads could alter the same linked list), posts a
37 WM_EMACS_FILENOTIFY, to the Emacs's message queue, and returns. 38 special message, WM_EMACS_FILENOTIFY, to the Emacs's message queue,
38 That causes the SleepEx function call inside watch_worker to 39 and returns. That causes the WaitForSingleObjectEx function call
39 return, and watch_worker then issues another call to 40 inside watch_worker to return, but the thread won't terminate until
40 ReadDirectoryChangesW. (Except when it does not, see below.) 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.)
41 44
42 In a GUI session, the WM_EMACS_FILENOTIFY message posted to the 45 In a GUI session, the WM_EMACS_FILENOTIFY message posted to the
43 message queue gets dispatched to the main Emacs window procedure, 46 message queue gets dispatched to the main Emacs window procedure,
44 which queues it for processing by w32_read_socket. When 47 which queues it for processing by w32_read_socket. When
45 w32_read_socket sees this message, it accesses the buffer with file 48 w32_read_socket sees this message, it accesses the linked list with file
46 notifications (using a critical section), extracts the information, 49 notifications (using a critical section), extracts the information,
47 converts it to a series of FILE_NOTIFY_EVENT events, and stuffs 50 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 51 them into the input event queue to be processed by keyboard.c input
@@ -53,7 +56,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
53 procedures in console programs. That message wakes up 56 procedures in console programs. That message wakes up
54 MsgWaitForMultipleObjects inside sys_select, which then signals to 57 MsgWaitForMultipleObjects inside sys_select, which then signals to
55 its caller that some keyboard input is available. This causes 58 its caller that some keyboard input is available. This causes
56 w32_console_read_socket to be called, which accesses the buffer 59 w32_console_read_socket to be called, which accesses the linked list
57 with file notifications and stuffs them into the input event queue 60 with file notifications and stuffs them into the input event queue
58 for keyboard.c to process. 61 for keyboard.c to process.
59 62
@@ -62,24 +65,21 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
62 bound to a command. The default binding is file-notify-handle-event, 65 bound to a command. The default binding is file-notify-handle-event,
63 defined on subr.el. 66 defined on subr.el.
64 67
65 After w32_read_socket or w32_console_read_socket are done 68 Routines w32_read_socket or w32_console_read_socket process notifications
66 processing the notifications, they reset a flag signaling to all 69 sets as long as some are available.
67 watch worker threads that the notifications buffer is available for
68 more input.
69 70
70 When the watch is removed by a call to w32notify-rm-watch, the main 71 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 thread requests that the worker thread terminates by signaling the
72 for the worker thread. The APC specifies the watch_end function to 73 appropriate event and queuing an APC for the worker thread. The
73 be called. watch_end calls CancelIo on the outstanding 74 APC specifies the watch_end function to be called. watch_end calls
74 ReadDirectoryChangesW call and closes the handle on which the 75 CancelIo on the outstanding ReadDirectoryChangesW call. When
75 watched directory was open. When watch_end returns, the 76 watch_end returns, the watch_completion function is called one last
76 watch_completion function is called one last time with the 77 time with the ERROR_OPERATION_ABORTED status, which causes it to
77 ERROR_OPERATION_ABORTED status, which causes it to clean up and set 78 clean up and set a flag telling watch_worker to exit without
78 a flag telling watch_worker to exit without issuing another 79 issuing another ReadDirectoryChangesW call. Since watch_worker is
79 ReadDirectoryChangesW call. Since watch_worker is the thread 80 the thread procedure of the worker thread, exiting it causes the
80 procedure of the worker thread, exiting it causes the thread to 81 thread to exit. The main thread waits for some time for the worker
81 exit. The main thread waits for some time for the worker thread to 82 thread to exit, and if it doesn't, terminates it forcibly. */
82 exit, and if it doesn't, terminates it forcibly. */
83 83
84#include <stddef.h> 84#include <stddef.h>
85#include <errno.h> 85#include <errno.h>
@@ -98,6 +98,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
98#include "frame.h" /* needed by termhooks.h */ 98#include "frame.h" /* needed by termhooks.h */
99#include "termhooks.h" /* for FILE_NOTIFY_EVENT */ 99#include "termhooks.h" /* for FILE_NOTIFY_EVENT */
100 100
101#define DIRWATCH_BUFFER_SIZE 16384
101#define DIRWATCH_SIGNATURE 0x01233210 102#define DIRWATCH_SIGNATURE 0x01233210
102 103
103struct notification { 104struct notification {
@@ -108,73 +109,52 @@ struct notification {
108 char *watchee; /* the file we are interested in, UTF-8 encoded */ 109 char *watchee; /* the file we are interested in, UTF-8 encoded */
109 HANDLE dir; /* handle to the watched directory */ 110 HANDLE dir; /* handle to the watched directory */
110 HANDLE thr; /* handle to the thread that watches */ 111 HANDLE thr; /* handle to the thread that watches */
111 volatile int terminate; /* if non-zero, request for the thread to terminate */ 112 HANDLE terminate; /* event signaling the thread to terminate */
112 unsigned signature; 113 unsigned signature;
113}; 114};
114 115
115/* Used for communicating notifications to the main thread. */ 116/* Used for communicating notifications to the main thread. */
116volatile int notification_buffer_in_use; 117struct notifications_set *notifications_set_head;
117BYTE file_notifications[16384];
118DWORD notifications_size;
119void *notifications_desc;
120 118
121static Lisp_Object watch_list; 119static Lisp_Object watch_list;
122 120
123/* Signal to the main thread that we have file notifications for it to 121/* Signal to the main thread that we have file notifications for it to
124 process. */ 122 process. */
125static void 123static void
126send_notifications (BYTE *info, DWORD info_size, void *desc, 124send_notifications (struct notifications_set *ns)
127 volatile int *terminate)
128{ 125{
129 int done = 0; 126 int done = 0;
130 struct frame *f = SELECTED_FRAME (); 127 struct frame *f = SELECTED_FRAME ();
131 128
132 /* A single buffer is used to communicate all notifications to the 129 /* We add the current notification set to the linked list. Use the
133 main thread. Since both the main thread and several watcher 130 critical section to make sure only one thread will access the
134 threads could be active at the same time, we use a critical area 131 linked list. */
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 (); 132 enter_crit ();
144 if (!notification_buffer_in_use) 133 ns->next = notifications_set_head;
145 { 134 ns->prev = notifications_set_head->prev;
146 if (info_size) 135 ns->prev->next = ns;
147 memcpy (file_notifications, info, 136 notifications_set_head->prev = ns;
148 min (info_size, sizeof (file_notifications))); 137 leave_crit();
149 notifications_size = min (info_size, sizeof (file_notifications)); 138
150 notifications_desc = desc; 139 /* If PostMessage fails, the message queue is full. If that
151 /* If PostMessage fails, the message queue is full. If that 140 happens, the last thing they will worry about is file
152 happens, the last thing they will worry about is file 141 notifications. So we effectively discard the notification in
153 notifications. So we effectively discard the 142 that case. */
154 notification in that case. */ 143 if (FRAME_TERMCAP_P (f))
155 if ((FRAME_TERMCAP_P (f) 144 /* We send the message to the main (a.k.a. "Lisp") thread, where
156 /* We send the message to the main (a.k.a. "Lisp") 145 it will wake up MsgWaitForMultipleObjects inside sys_select,
157 thread, where it will wake up MsgWaitForMultipleObjects 146 causing it to report that there's some keyboard input
158 inside sys_select, causing it to report that there's 147 available. This will in turn cause w32_console_read_socket to
159 some keyboard input available. This will in turn cause 148 be called, which will pick up the file notifications. */
160 w32_console_read_socket to be called, which will pick 149 PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0);
161 up the file notifications. */ 150 else if (FRAME_W32_P (f))
162 && PostThreadMessage (dwMainThreadId, WM_EMACS_FILENOTIFY, 0, 0)) 151 PostMessage (FRAME_W32_WINDOW (f),
163 || (FRAME_W32_P (f) 152 WM_EMACS_FILENOTIFY, 0, 0);
164 && PostMessage (FRAME_W32_WINDOW (f), 153 /* When we are running in batch mode, there's no one to send a
165 WM_EMACS_FILENOTIFY, 0, 0)) 154 message, so we just signal the data is available and hope
166 /* When we are running in batch mode, there's no one to 155 sys_select will be called soon and will read the data. */
167 send a message, so we just signal the data is 156 else if (FRAME_INITIAL_P (f) && noninteractive)
168 available and hope sys_select will be called soon and 157 ;
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} 158}
179 159
180/* An APC routine to cancel outstanding directory watch. Invoked by 160/* An APC routine to cancel outstanding directory watch. Invoked by
@@ -188,10 +168,7 @@ watch_end (ULONG_PTR arg)
188 HANDLE hdir = (HANDLE)arg; 168 HANDLE hdir = (HANDLE)arg;
189 169
190 if (hdir && hdir != INVALID_HANDLE_VALUE) 170 if (hdir && hdir != INVALID_HANDLE_VALUE)
191 { 171 CancelIo (hdir);
192 CancelIo (hdir);
193 CloseHandle (hdir);
194 }
195} 172}
196 173
197/* A completion routine (a.k.a. "APC function") for handling events 174/* A completion routine (a.k.a. "APC function") for handling events
@@ -202,13 +179,19 @@ VOID CALLBACK
202watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info) 179watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
203{ 180{
204 struct notification *dirwatch; 181 struct notification *dirwatch;
182 DWORD _bytes;
183 struct notifications_set *ns = NULL;
184 BOOL terminate = FALSE;
205 185
206 /* Who knows what happened? Perhaps the OVERLAPPED structure was 186 /* Who knows what happened? Perhaps the OVERLAPPED structure was
207 freed by someone already? In any case, we cannot do anything 187 freed by someone already? In any case, we cannot do anything
208 with this request, so just punt and skip it. FIXME: should we 188 with this request, so just punt and skip it. FIXME: should we
209 raise the 'terminate' flag in this case? */ 189 raise the 'terminate' flag in this case? */
210 if (!io_info) 190 if (!io_info)
211 return; 191 {
192 DebPrint(("watch_completion: io_info is null.\n"));
193 return;
194 }
212 195
213 /* We have a pointer to our dirwatch structure conveniently stashed 196 /* We have a pointer to our dirwatch structure conveniently stashed
214 away in the hEvent member of the OVERLAPPED struct. According to 197 away in the hEvent member of the OVERLAPPED struct. According to
@@ -216,26 +199,69 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
216 of the OVERLAPPED structure is not used by the system, so you can 199 of the OVERLAPPED structure is not used by the system, so you can
217 use it yourself." */ 200 use it yourself." */
218 dirwatch = (struct notification *)io_info->hEvent; 201 dirwatch = (struct notification *)io_info->hEvent;
202
219 if (status == ERROR_OPERATION_ABORTED) 203 if (status == ERROR_OPERATION_ABORTED)
220 { 204 {
221 /* We've been called because the main thread told us to issue 205 /* We've been called because the main thread told us to issue
222 CancelIo on the directory we watch, and watch_end did so. 206 CancelIo on the directory we watch, and watch_end did so.
223 The directory handle is already closed. We should clean up 207 We must exit, without issuing another call to
224 and exit, signaling to the thread worker routine not to 208 ReadDirectoryChangesW. */
225 issue another call to ReadDirectoryChangesW. Note that we 209 return;
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 } 210 }
233 else 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))
234 { 250 {
235 /* Tell the main thread we have notifications for it. */ 251 DebPrint (("ReadDirectoryChangesW error: %lu\n", GetLastError ()));
236 send_notifications (dirwatch->buf, bytes_ret, dirwatch, 252 /* If this call fails, it means that the directory is not
237 &dirwatch->terminate); 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;
238 } 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);
239} 265}
240 266
241/* Worker routine for the watch thread. */ 267/* Worker routine for the watch thread. */
@@ -243,42 +269,43 @@ static DWORD WINAPI
243watch_worker (LPVOID arg) 269watch_worker (LPVOID arg)
244{ 270{
245 struct notification *dirwatch = (struct notification *)arg; 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 }
246 298
247 do { 299 do {
248 BOOL status; 300 status = WaitForSingleObjectEx(dirwatch->terminate, INFINITE, TRUE);
249 DWORD bytes_ret = 0; 301 } while (status == WAIT_IO_COMPLETION);
250 302
251 if (dirwatch->dir) 303 /* The thread is about to terminate, so we clean up the dir handle. */
252 { 304 CloseHandle (dirwatch->dir);
253 status = ReadDirectoryChangesW (dirwatch->dir, dirwatch->buf, 16384, 305 dirwatch->dir = NULL;
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 306
279 return 0; 307 return 0;
280} 308}
281
282/* Launch a thread to watch changes to FILE in a directory open on 309/* Launch a thread to watch changes to FILE in a directory open on
283 handle HDIR. */ 310 handle HDIR. */
284static struct notification * 311static struct notification *
@@ -287,7 +314,7 @@ start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
287 struct notification *dirwatch = xzalloc (sizeof (struct notification)); 314 struct notification *dirwatch = xzalloc (sizeof (struct notification));
288 315
289 dirwatch->signature = DIRWATCH_SIGNATURE; 316 dirwatch->signature = DIRWATCH_SIGNATURE;
290 dirwatch->buf = xmalloc (16384); 317 dirwatch->buf = xmalloc (DIRWATCH_BUFFER_SIZE);
291 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED)); 318 dirwatch->io_info = xzalloc (sizeof(OVERLAPPED));
292 /* Stash a pointer to dirwatch structure for use by the completion 319 /* Stash a pointer to dirwatch structure for use by the completion
293 routine. According to MSDN documentation of ReadDirectoryChangesW: 320 routine. According to MSDN documentation of ReadDirectoryChangesW:
@@ -297,7 +324,9 @@ start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
297 dirwatch->subtree = subdirs; 324 dirwatch->subtree = subdirs;
298 dirwatch->filter = flags; 325 dirwatch->filter = flags;
299 dirwatch->watchee = xstrdup (file); 326 dirwatch->watchee = xstrdup (file);
300 dirwatch->terminate = 0; 327
328 dirwatch->terminate = CreateEvent(NULL, FALSE, FALSE, NULL);
329
301 dirwatch->dir = hdir; 330 dirwatch->dir = hdir;
302 331
303 /* See w32proc.c where it calls CreateThread for the story behind 332 /* See w32proc.c where it calls CreateThread for the story behind
@@ -307,11 +336,11 @@ start_watching (const char *file, HANDLE hdir, BOOL subdirs, DWORD flags)
307 336
308 if (!dirwatch->thr) 337 if (!dirwatch->thr)
309 { 338 {
339 CloseHandle(dirwatch->terminate);
310 xfree (dirwatch->buf); 340 xfree (dirwatch->buf);
311 xfree (dirwatch->io_info); 341 xfree (dirwatch->io_info);
312 xfree (dirwatch->watchee); 342 xfree (dirwatch->watchee);
313 xfree (dirwatch); 343 xfree (dirwatch);
314 dirwatch = NULL;
315 } 344 }
316 return dirwatch; 345 return dirwatch;
317} 346}
@@ -370,7 +399,10 @@ add_watch (const char *parent_dir, const char *file, BOOL subdirs, DWORD flags)
370 return NULL; 399 return NULL;
371 400
372 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL) 401 if ((dirwatch = start_watching (file, hdir, subdirs, flags)) == NULL)
373 CloseHandle (hdir); 402 {
403 CloseHandle (hdir);
404 dirwatch->dir = NULL;
405 }
374 406
375 return dirwatch; 407 return dirwatch;
376} 408}
@@ -383,7 +415,7 @@ remove_watch (struct notification *dirwatch)
383 { 415 {
384 int i; 416 int i;
385 BOOL status; 417 BOOL status;
386 DWORD exit_code, err; 418 DWORD exit_code = 0, err;
387 419
388 /* Only the thread that issued the outstanding I/O call can call 420 /* Only the thread that issued the outstanding I/O call can call
389 CancelIo on it. (CancelIoEx is available only since Vista.) 421 CancelIo on it. (CancelIoEx is available only since Vista.)
@@ -391,12 +423,10 @@ remove_watch (struct notification *dirwatch)
391 to terminate. */ 423 to terminate. */
392 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir)) 424 if (!QueueUserAPC (watch_end, dirwatch->thr, (ULONG_PTR)dirwatch->dir))
393 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ())); 425 DebPrint (("QueueUserAPC failed (%lu)!\n", GetLastError ()));
394 /* We also set the terminate flag, for when the thread is 426
395 waiting on the critical section that never gets acquired. 427 /* We also signal the thread that it can terminate. */
396 FIXME: is there a cleaner method? Using SleepEx there is a 428 SetEvent(dirwatch->terminate);
397 no-no, as that will lead to recursive APC invocations and 429
398 stack overflow. */
399 dirwatch->terminate = 1;
400 /* Wait for the thread to exit. FIXME: is there a better method 430 /* Wait for the thread to exit. FIXME: is there a better method
401 that is not overly complex? */ 431 that is not overly complex? */
402 for (i = 0; i < 50; i++) 432 for (i = 0; i < 50; i++)
@@ -406,11 +436,13 @@ remove_watch (struct notification *dirwatch)
406 break; 436 break;
407 Sleep (10); 437 Sleep (10);
408 } 438 }
439
409 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE) 440 if ((status == FALSE && (err = GetLastError ()) == ERROR_INVALID_HANDLE)
410 || exit_code == STILL_ACTIVE) 441 || exit_code == STILL_ACTIVE)
411 { 442 {
412 if (!(status == FALSE && err == ERROR_INVALID_HANDLE)) 443 if (!(status == FALSE && err == ERROR_INVALID_HANDLE))
413 { 444 {
445 DebPrint(("Forcing thread termination.\n"));
414 TerminateThread (dirwatch->thr, 0); 446 TerminateThread (dirwatch->thr, 0);
415 if (dirwatch->dir) 447 if (dirwatch->dir)
416 CloseHandle (dirwatch->dir); 448 CloseHandle (dirwatch->dir);
@@ -423,11 +455,11 @@ remove_watch (struct notification *dirwatch)
423 CloseHandle (dirwatch->thr); 455 CloseHandle (dirwatch->thr);
424 dirwatch->thr = NULL; 456 dirwatch->thr = NULL;
425 } 457 }
458 CloseHandle(dirwatch->terminate);
426 xfree (dirwatch->buf); 459 xfree (dirwatch->buf);
427 xfree (dirwatch->io_info); 460 xfree (dirwatch->io_info);
428 xfree (dirwatch->watchee); 461 xfree (dirwatch->watchee);
429 xfree (dirwatch); 462 xfree (dirwatch);
430
431 return 0; 463 return 0;
432 } 464 }
433 else 465 else