diff options
| author | Eli Zaretskii | 2012-10-07 15:41:15 +0200 |
|---|---|---|
| committer | Eli Zaretskii | 2012-10-07 15:41:15 +0200 |
| commit | c5c91b847a41ec7e09dee582816d702c8a9adcdd (patch) | |
| tree | c96da676a40396ee6e0aaaa969fd01f984791559 /src/w32notify.c | |
| parent | 25d99a950debf417295e68a7a7d519b4ddf3e546 (diff) | |
| download | emacs-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.c | 102 |
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. | |||
| 16 | You should have received a copy of the GNU General Public License | 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/>. */ | 17 | along 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; | |||
| 58 | static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time; | 106 | static Lisp_Object Qlast_write_time, Qlast_access_time, Qcreation_time; |
| 59 | static Lisp_Object Qsecurity_desc, Qsubtree, watch_list; | 107 | static Lisp_Object Qsecurity_desc, Qsubtree, watch_list; |
| 60 | 108 | ||
| 61 | #if 0 | ||
| 62 | /* FIXME: Debugging code, should be removed eventually. */ | ||
| 63 | const wchar_t * | ||
| 64 | format_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 | |||
| 74 | void | ||
| 75 | parse_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. */ |
| 108 | static void | 111 | static 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 | ||