aboutsummaryrefslogtreecommitdiffstats
path: root/src/w32notify.c
diff options
context:
space:
mode:
authorEli Zaretskii2012-10-07 15:41:15 +0200
committerEli Zaretskii2012-10-07 15:41:15 +0200
commitc5c91b847a41ec7e09dee582816d702c8a9adcdd (patch)
treec96da676a40396ee6e0aaaa969fd01f984791559 /src/w32notify.c
parent25d99a950debf417295e68a7a7d519b4ddf3e546 (diff)
downloademacs-c5c91b847a41ec7e09dee582816d702c8a9adcdd.tar.gz
emacs-c5c91b847a41ec7e09dee582816d702c8a9adcdd.zip
Final version that supports only one watch at a time.
Diffstat (limited to 'src/w32notify.c')
-rw-r--r--src/w32notify.c102
1 files changed, 49 insertions, 53 deletions
diff --git a/src/w32notify.c b/src/w32notify.c
index 0dc4d27d33f..81199b2a0a1 100644
--- a/src/w32notify.c
+++ b/src/w32notify.c
@@ -16,6 +16,54 @@ GNU General Public License for more details.
16You should have received a copy of the GNU General Public License 16You should have received a copy of the GNU General Public License
17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ 17along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
18 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 The WM_EMACS_FILENOTIFY message, posted to the message queue gets
40 dispatched to the main Emacs window procedure, which queues it for
41 processing by w32_read_socket. When w32_read_socket sees this
42 message, it accesses the buffer with file notifications (using a
43 critical section), extracts the information, converts it to a
44 series of FILE_NOTIFY_EVENT events, and stuffs them into the input
45 event queue to be processed by keyboard.c input machinery
46 (read_char via a call to kbd_buffer_get_event). When the
47 FILE_NOTIFY_EVENT event is processed by kbd_buffer_get_event, it is
48 converted to a Lispy event that can be bound to a command. The
49 default binding is w32notify-handle-event, defined on subr.el.
50
51 After w32_read_socket is done processing the notifications, it
52 resets a flag signaling to all watch worker threads that the
53 notifications buffer is available for more input.
54
55 When the watch is removed by a call to w32notify-rm-watch, the main
56 thread requests that the worker thread terminates by queuing an APC
57 for the worker thread. The APC specifies the watch_end function to
58 be called. watch_end calls CancelIo on the outstanding
59 ReadDirectoryChangesW call and closes the handle on which the
60 watched directory was open. When watch_end returns, the
61 watch_completion function is called one last time with the
62 ERROR_OPERATION_ABORTED status, which causes it to clean up and set
63 a flag telling watch_worker to exit without issuing another
64 ReadDirectoryChangesW call. The main thread waits for the worker
65 thread to exit, and if it doesn't, terminate it forcibly. */
66
19#include <stddef.h> 67#include <stddef.h>
20#include <errno.h> 68#include <errno.h>
21 69
@@ -58,51 +106,6 @@ static Lisp_Object Qfile_name, Qdirectory_name, Qattributes, Qsize;
58static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time; 106static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time;
59static Lisp_Object Qsecurity_desc, Qsubtree, watch_list; 107static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;
60 108
61#if 0
62/* FIXME: Debugging code, should be removed eventually. */
63const wchar_t *
64format_file_action (DWORD action)
65{
66 static const wchar_t *action_str[] =
67 { L"???", L"Added", L"Removed", L"Modified", L"Renamed from", L"Renamed to" };
68
69 if (action >= sizeof(action_str)/sizeof(action_str[0]))
70 action = 0;
71 return action_str[action];
72}
73
74void
75parse_notifications (BYTE *info, DWORD info_size)
76{
77 BYTE *p = info;
78 FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
79 const DWORD min_size
80 = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
81
82 if (!info_size)
83 {
84 printf ("No info in notifications!\n");
85 return;
86 }
87
88 while (info_size >= min_size)
89 {
90 wchar_t *fn = alloca (fni->FileNameLength + sizeof(wchar_t));
91 const wchar_t *action_str;
92
93 memcpy (fn, fni->FileName, fni->FileNameLength);
94 fn[fni->FileNameLength/sizeof(wchar_t)] = 0;
95 action_str = format_file_action (fni->Action);
96 wprintf (L"%s: %s\n", action_str, fn);
97 if (!fni->NextEntryOffset)
98 break;
99 p += fni->NextEntryOffset;
100 fni = (PFILE_NOTIFY_INFORMATION)p;
101 info_size -= fni->NextEntryOffset;
102 }
103}
104#endif /* debugging code */
105
106/* Signal to the main thread that we have file notifications for it to 109/* Signal to the main thread that we have file notifications for it to
107 process. */ 110 process. */
108static void 111static void
@@ -141,7 +144,6 @@ send_notifications (BYTE *info, DWORD info_size, HANDLE hdir, int *terminate)
141 if (PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0)) 144 if (PostMessage (FRAME_W32_WINDOW (f), WM_EMACS_FILENOTIFY, 0, 0))
142 notification_buffer_in_use = 1; 145 notification_buffer_in_use = 1;
143 done = 1; 146 done = 1;
144 DebPrint (("Announced notifications of %lu bytes\n", info_size));
145 } 147 }
146 leave_crit (); 148 leave_crit ();
147 if (!done) 149 if (!done)
@@ -205,9 +207,6 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
205 } 207 }
206 else 208 else
207 { 209 {
208#if 0 /* debugging code */
209 parse_notifications (dirwatch->buf, bytes_ret);
210#endif
211 /* Tell the main thread we have notifications for it. */ 210 /* Tell the main thread we have notifications for it. */
212 send_notifications (dirwatch->buf, bytes_ret, dirwatch->dir, 211 send_notifications (dirwatch->buf, bytes_ret, dirwatch->dir,
213 &dirwatch->terminate); 212 &dirwatch->terminate);
@@ -233,7 +232,7 @@ watch_worker (LPVOID arg)
233 dirwatch->io_info, watch_completion); 232 dirwatch->io_info, watch_completion);
234 if (!status) 233 if (!status)
235 { 234 {
236 DebPrint (("watch_worker(1): %lu\n", GetLastError ())); 235 DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
237 xfree (dirwatch->buf); 236 xfree (dirwatch->buf);
238 dirwatch->buf = NULL; 237 dirwatch->buf = NULL;
239 xfree (dirwatch->io_info); 238 xfree (dirwatch->io_info);
@@ -249,11 +248,8 @@ watch_worker (LPVOID arg)
249 could be either a change notification or a cancellation of the 248 could be either a change notification or a cancellation of the
250 watch. */ 249 watch. */
251 sleep_result = SleepEx (INFINITE, TRUE); 250 sleep_result = SleepEx (INFINITE, TRUE);
252 if (dirwatch->terminate)
253 DebPrint (("watch_worker: exiting by request\n"));
254 } while (!dirwatch->terminate); 251 } while (!dirwatch->terminate);
255 252
256 DebPrint (("watch_worker(2): %lu\n", GetLastError ()));
257 return 0; 253 return 0;
258} 254}
259 255