diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/w32inevt.c | 115 | ||||
| -rw-r--r-- | src/w32notify.c | 320 | ||||
| -rw-r--r-- | src/w32term.c | 116 | ||||
| -rw-r--r-- | src/w32term.h | 16 | ||||
| -rw-r--r-- | src/w32xfns.c | 28 |
5 files changed, 349 insertions, 246 deletions
diff --git a/src/w32inevt.c b/src/w32inevt.c index a33f82dc7db..2269d318051 100644 --- a/src/w32inevt.c +++ b/src/w32inevt.c | |||
| @@ -620,70 +620,89 @@ maybe_generate_resize_event (void) | |||
| 620 | int | 620 | int |
| 621 | handle_file_notifications (struct input_event *hold_quit) | 621 | handle_file_notifications (struct input_event *hold_quit) |
| 622 | { | 622 | { |
| 623 | BYTE *p = file_notifications; | 623 | struct notifications_set *ns = NULL; |
| 624 | FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p; | ||
| 625 | const DWORD min_size | ||
| 626 | = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t); | ||
| 627 | struct input_event inev; | ||
| 628 | int nevents = 0; | 624 | int nevents = 0; |
| 625 | int done = 0; | ||
| 629 | 626 | ||
| 630 | /* We cannot process notification before Emacs is fully initialized, | 627 | /* We cannot process notification before Emacs is fully initialized, |
| 631 | since we need the UTF-16LE coding-system to be set up. */ | 628 | since we need the UTF-16LE coding-system to be set up. */ |
| 632 | if (!initialized) | 629 | if (!initialized) |
| 633 | { | 630 | { |
| 634 | notification_buffer_in_use = 0; | ||
| 635 | return nevents; | 631 | return nevents; |
| 636 | } | 632 | } |
| 637 | 633 | ||
| 638 | enter_crit (); | 634 | while (!done) |
| 639 | if (notification_buffer_in_use) | ||
| 640 | { | 635 | { |
| 641 | DWORD info_size = notifications_size; | 636 | ns = NULL; |
| 642 | Lisp_Object cs = Qutf_16le; | ||
| 643 | Lisp_Object obj = w32_get_watch_object (notifications_desc); | ||
| 644 | |||
| 645 | /* notifications_size could be zero when the buffer of | ||
| 646 | notifications overflowed on the OS level, or when the | ||
| 647 | directory being watched was itself deleted. Do nothing in | ||
| 648 | that case. */ | ||
| 649 | if (info_size | ||
| 650 | && !NILP (obj) && CONSP (obj)) | ||
| 651 | { | ||
| 652 | Lisp_Object callback = XCDR (obj); | ||
| 653 | 637 | ||
| 654 | EVENT_INIT (inev); | 638 | /* Find out if there is a record available in the linked list of |
| 639 | notifications sets. If so, unlink te set from the linked list. | ||
| 640 | Use the critical section. */ | ||
| 641 | enter_crit (); | ||
| 642 | if (notifications_set_head->next != notifications_set_head) | ||
| 643 | { | ||
| 644 | ns = notifications_set_head->next; | ||
| 645 | ns->prev->next = ns->next; | ||
| 646 | ns->next->prev = ns->prev; | ||
| 647 | } | ||
| 648 | else | ||
| 649 | done = 1; | ||
| 650 | leave_crit(); | ||
| 655 | 651 | ||
| 656 | while (info_size >= min_size) | 652 | if (ns) |
| 653 | { | ||
| 654 | BYTE *p = ns->notifications; | ||
| 655 | FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p; | ||
| 656 | const DWORD min_size | ||
| 657 | = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t); | ||
| 658 | struct input_event inev; | ||
| 659 | DWORD info_size = ns->size; | ||
| 660 | Lisp_Object cs = Qutf_16le; | ||
| 661 | Lisp_Object obj = w32_get_watch_object (ns->desc); | ||
| 662 | |||
| 663 | /* notifications size could be zero when the buffer of | ||
| 664 | notifications overflowed on the OS level, or when the | ||
| 665 | directory being watched was itself deleted. Do nothing in | ||
| 666 | that case. */ | ||
| 667 | if (info_size | ||
| 668 | && !NILP (obj) && CONSP (obj)) | ||
| 657 | { | 669 | { |
| 658 | Lisp_Object utf_16_fn | 670 | Lisp_Object callback = XCDR (obj); |
| 659 | = make_unibyte_string ((char *)fni->FileName, | 671 | |
| 660 | fni->FileNameLength); | 672 | EVENT_INIT (inev); |
| 661 | /* Note: mule-conf is preloaded, so utf-16le must | 673 | |
| 662 | already be defined at this point. */ | 674 | while (info_size >= min_size) |
| 663 | Lisp_Object fname | 675 | { |
| 664 | = code_convert_string_norecord (utf_16_fn, cs, 0); | 676 | Lisp_Object utf_16_fn |
| 665 | Lisp_Object action = lispy_file_action (fni->Action); | 677 | = make_unibyte_string ((char *)fni->FileName, |
| 666 | 678 | fni->FileNameLength); | |
| 667 | inev.kind = FILE_NOTIFY_EVENT; | 679 | /* Note: mule-conf is preloaded, so utf-16le must |
| 668 | inev.timestamp = GetTickCount (); | 680 | already be defined at this point. */ |
| 669 | inev.modifiers = 0; | 681 | Lisp_Object fname |
| 670 | inev.frame_or_window = callback; | 682 | = code_convert_string_norecord (utf_16_fn, cs, 0); |
| 671 | inev.arg = Fcons (action, fname); | 683 | Lisp_Object action = lispy_file_action (fni->Action); |
| 672 | inev.arg = list3 (make_pointer_integer (notifications_desc), | 684 | |
| 673 | action, fname); | 685 | inev.kind = FILE_NOTIFY_EVENT; |
| 674 | kbd_buffer_store_event_hold (&inev, hold_quit); | 686 | inev.timestamp = GetTickCount (); |
| 675 | nevents++; | 687 | inev.modifiers = 0; |
| 676 | 688 | inev.frame_or_window = callback; | |
| 677 | if (!fni->NextEntryOffset) | 689 | inev.arg = Fcons (action, fname); |
| 678 | break; | 690 | inev.arg = list3 (make_pointer_integer (ns->desc), |
| 679 | p += fni->NextEntryOffset; | 691 | action, fname); |
| 680 | fni = (PFILE_NOTIFY_INFORMATION)p; | 692 | kbd_buffer_store_event_hold (&inev, hold_quit); |
| 681 | info_size -= fni->NextEntryOffset; | 693 | nevents++; |
| 694 | if (!fni->NextEntryOffset) | ||
| 695 | break; | ||
| 696 | p += fni->NextEntryOffset; | ||
| 697 | fni = (PFILE_NOTIFY_INFORMATION)p; | ||
| 698 | info_size -= fni->NextEntryOffset; | ||
| 699 | } | ||
| 682 | } | 700 | } |
| 701 | /* Free this notification set. */ | ||
| 702 | free (ns->notifications); | ||
| 703 | free (ns); | ||
| 683 | } | 704 | } |
| 684 | notification_buffer_in_use = 0; | ||
| 685 | } | 705 | } |
| 686 | leave_crit (); | ||
| 687 | return nevents; | 706 | return nevents; |
| 688 | } | 707 | } |
| 689 | #else /* !HAVE_W32NOTIFY */ | 708 | #else /* !HAVE_W32NOTIFY */ |
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 | ||
| 103 | struct notification { | 104 | struct 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. */ |
| 116 | volatile int notification_buffer_in_use; | 117 | struct notifications_set *notifications_set_head; |
| 117 | BYTE file_notifications[16384]; | ||
| 118 | DWORD notifications_size; | ||
| 119 | void *notifications_desc; | ||
| 120 | 118 | ||
| 121 | static Lisp_Object watch_list; | 119 | static 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. */ |
| 125 | static void | 123 | static void |
| 126 | send_notifications (BYTE *info, DWORD info_size, void *desc, | 124 | send_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 | |||
| 202 | watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info) | 179 | watch_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 | |||
| 243 | watch_worker (LPVOID arg) | 269 | watch_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. */ |
| 284 | static struct notification * | 311 | static 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 |
diff --git a/src/w32term.c b/src/w32term.c index 62ad4eb086b..8955ce26b4b 100644 --- a/src/w32term.c +++ b/src/w32term.c | |||
| @@ -3211,71 +3211,85 @@ static void | |||
| 3211 | queue_notifications (struct input_event *event, W32Msg *msg, struct frame *f, | 3211 | queue_notifications (struct input_event *event, W32Msg *msg, struct frame *f, |
| 3212 | int *evcount) | 3212 | int *evcount) |
| 3213 | { | 3213 | { |
| 3214 | BYTE *p = file_notifications; | 3214 | struct notifications_set *ns = NULL; |
| 3215 | FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p; | ||
| 3216 | const DWORD min_size | ||
| 3217 | = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t); | ||
| 3218 | Lisp_Object frame; | 3215 | Lisp_Object frame; |
| 3216 | int done = 0; | ||
| 3219 | 3217 | ||
| 3220 | /* We cannot process notification before Emacs is fully initialized, | 3218 | /* We cannot process notification before Emacs is fully initialized, |
| 3221 | since we need the UTF-16LE coding-system to be set up. */ | 3219 | since we need the UTF-16LE coding-system to be set up. */ |
| 3222 | if (!initialized) | 3220 | if (!initialized) |
| 3223 | { | 3221 | return; |
| 3224 | notification_buffer_in_use = 0; | ||
| 3225 | return; | ||
| 3226 | } | ||
| 3227 | 3222 | ||
| 3228 | XSETFRAME (frame, f); | 3223 | XSETFRAME (frame, f); |
| 3229 | 3224 | ||
| 3230 | enter_crit (); | 3225 | while (!done) |
| 3231 | if (notification_buffer_in_use) | ||
| 3232 | { | 3226 | { |
| 3233 | DWORD info_size = notifications_size; | 3227 | ns = NULL; |
| 3234 | Lisp_Object cs = Qutf_16le; | 3228 | |
| 3235 | Lisp_Object obj = w32_get_watch_object (notifications_desc); | 3229 | /* Find out if there is a record available in the linked list of |
| 3236 | 3230 | notifications sets. If so, unlink the set from the linked | |
| 3237 | /* notifications_size could be zero when the buffer of | 3231 | list. Use critical section. */ |
| 3238 | notifications overflowed on the OS level, or when the | 3232 | enter_crit (); |
| 3239 | directory being watched was itself deleted. Do nothing in | 3233 | if (notifications_set_head->next != notifications_set_head) |
| 3240 | that case. */ | ||
| 3241 | if (info_size | ||
| 3242 | && !NILP (obj) && CONSP (obj)) | ||
| 3243 | { | 3234 | { |
| 3244 | Lisp_Object callback = XCDR (obj); | 3235 | ns = notifications_set_head->next; |
| 3236 | ns->prev->next = ns->next; | ||
| 3237 | ns->next->prev = ns->prev; | ||
| 3238 | } | ||
| 3239 | else | ||
| 3240 | done = 1; | ||
| 3241 | leave_crit(); | ||
| 3245 | 3242 | ||
| 3246 | while (info_size >= min_size) | 3243 | if (ns) |
| 3244 | { | ||
| 3245 | BYTE *p = ns->notifications; | ||
| 3246 | FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p; | ||
| 3247 | const DWORD min_size | ||
| 3248 | = offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t); | ||
| 3249 | DWORD info_size = ns->size; | ||
| 3250 | Lisp_Object cs = Qutf_16le; | ||
| 3251 | Lisp_Object obj = w32_get_watch_object (ns->desc); | ||
| 3252 | |||
| 3253 | /* notifications size could be zero when the buffer of | ||
| 3254 | notifications overflowed on the OS level, or when the | ||
| 3255 | directory being watched was itself deleted. Do nothing in | ||
| 3256 | that case. */ | ||
| 3257 | if (info_size | ||
| 3258 | && !NILP (obj) && CONSP (obj)) | ||
| 3247 | { | 3259 | { |
| 3248 | Lisp_Object utf_16_fn | 3260 | Lisp_Object callback = XCDR (obj); |
| 3249 | = make_unibyte_string ((char *)fni->FileName, | 3261 | |
| 3250 | fni->FileNameLength); | 3262 | while (info_size >= min_size) |
| 3251 | /* Note: mule-conf is preloaded, so utf-16le must | 3263 | { |
| 3252 | already be defined at this point. */ | 3264 | Lisp_Object utf_16_fn |
| 3253 | Lisp_Object fname | 3265 | = make_unibyte_string ((char *)fni->FileName, |
| 3254 | = code_convert_string_norecord (utf_16_fn, cs, 0); | 3266 | fni->FileNameLength); |
| 3255 | Lisp_Object action = lispy_file_action (fni->Action); | 3267 | /* Note: mule-conf is preloaded, so utf-16le must |
| 3256 | 3268 | already be defined at this point. */ | |
| 3257 | event->kind = FILE_NOTIFY_EVENT; | 3269 | Lisp_Object fname |
| 3258 | event->timestamp = msg->msg.time; | 3270 | = code_convert_string_norecord (utf_16_fn, cs, 0); |
| 3259 | event->modifiers = 0; | 3271 | Lisp_Object action = lispy_file_action (fni->Action); |
| 3260 | event->frame_or_window = callback; | 3272 | |
| 3261 | event->arg = list3 (make_pointer_integer (notifications_desc), | 3273 | event->kind = FILE_NOTIFY_EVENT; |
| 3262 | action, fname); | 3274 | event->timestamp = msg->msg.time; |
| 3263 | kbd_buffer_store_event (event); | 3275 | event->modifiers = 0; |
| 3264 | (*evcount)++; | 3276 | event->frame_or_window = callback; |
| 3265 | 3277 | event->arg = list3 (make_pointer_integer (ns->desc), | |
| 3266 | if (!fni->NextEntryOffset) | 3278 | action, fname); |
| 3267 | break; | 3279 | kbd_buffer_store_event (event); |
| 3268 | p += fni->NextEntryOffset; | 3280 | (*evcount)++; |
| 3269 | fni = (PFILE_NOTIFY_INFORMATION)p; | 3281 | if (!fni->NextEntryOffset) |
| 3270 | info_size -= fni->NextEntryOffset; | 3282 | break; |
| 3283 | p += fni->NextEntryOffset; | ||
| 3284 | fni = (PFILE_NOTIFY_INFORMATION)p; | ||
| 3285 | info_size -= fni->NextEntryOffset; | ||
| 3286 | } | ||
| 3271 | } | 3287 | } |
| 3288 | /* Free this notifications set. */ | ||
| 3289 | xfree (ns->notifications); | ||
| 3290 | xfree (ns); | ||
| 3272 | } | 3291 | } |
| 3273 | notification_buffer_in_use = 0; | ||
| 3274 | } | 3292 | } |
| 3275 | else | ||
| 3276 | DebPrint (("We were promised notifications, but in-use flag is zero!\n")); | ||
| 3277 | leave_crit (); | ||
| 3278 | |||
| 3279 | /* We've stuffed all the events ourselves, so w32_read_socket shouldn't. */ | 3293 | /* We've stuffed all the events ourselves, so w32_read_socket shouldn't. */ |
| 3280 | event->kind = NO_EVENT; | 3294 | event->kind = NO_EVENT; |
| 3281 | } | 3295 | } |
| @@ -6949,6 +6963,8 @@ w32_init_main_thread (void) | |||
| 6949 | DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), | 6963 | DuplicateHandle (GetCurrentProcess (), GetCurrentThread (), |
| 6950 | GetCurrentProcess (), &hMainThread, 0, TRUE, | 6964 | GetCurrentProcess (), &hMainThread, 0, TRUE, |
| 6951 | DUPLICATE_SAME_ACCESS); | 6965 | DUPLICATE_SAME_ACCESS); |
| 6966 | |||
| 6967 | |||
| 6952 | } | 6968 | } |
| 6953 | 6969 | ||
| 6954 | DWORD WINAPI w32_msg_worker (void * arg); | 6970 | DWORD WINAPI w32_msg_worker (void * arg); |
diff --git a/src/w32term.h b/src/w32term.h index d809be3c03d..8585c8190db 100644 --- a/src/w32term.h +++ b/src/w32term.h | |||
| @@ -727,10 +727,18 @@ extern void x_delete_display (struct w32_display_info *dpyinfo); | |||
| 727 | 727 | ||
| 728 | extern void x_query_color (struct frame *, XColor *); | 728 | extern void x_query_color (struct frame *, XColor *); |
| 729 | 729 | ||
| 730 | extern volatile int notification_buffer_in_use; | 730 | #define FILE_NOTIFICATIONS_SIZE 16384 |
| 731 | extern BYTE file_notifications[16384]; | 731 | /* Notifications come in sets. We use a doubly linked list with a |
| 732 | extern DWORD notifications_size; | 732 | sentinel to communicate those sets from the watching threads to the |
| 733 | extern void *notifications_desc; | 733 | main thread. */ |
| 734 | struct notifications_set { | ||
| 735 | LPBYTE notifications; | ||
| 736 | DWORD size; | ||
| 737 | void *desc; | ||
| 738 | struct notifications_set *next; | ||
| 739 | struct notifications_set *prev; | ||
| 740 | }; | ||
| 741 | extern struct notifications_set *notifications_set_head; | ||
| 734 | extern Lisp_Object w32_get_watch_object (void *); | 742 | extern Lisp_Object w32_get_watch_object (void *); |
| 735 | extern Lisp_Object lispy_file_action (DWORD); | 743 | extern Lisp_Object lispy_file_action (DWORD); |
| 736 | extern int handle_file_notifications (struct input_event *); | 744 | extern int handle_file_notifications (struct input_event *); |
diff --git a/src/w32xfns.c b/src/w32xfns.c index 04bf5ce733e..9b633c4c56a 100644 --- a/src/w32xfns.c +++ b/src/w32xfns.c | |||
| @@ -48,6 +48,19 @@ init_crit (void) | |||
| 48 | when the input queue is empty, so make it a manual reset event. */ | 48 | when the input queue is empty, so make it a manual reset event. */ |
| 49 | input_available = CreateEvent (NULL, TRUE, FALSE, NULL); | 49 | input_available = CreateEvent (NULL, TRUE, FALSE, NULL); |
| 50 | 50 | ||
| 51 | /* Initialize the linked list of notifications sets that will be | ||
| 52 | used to communicate between the watching worker threads and the | ||
| 53 | main thread. */ | ||
| 54 | notifications_set_head = malloc (sizeof(struct notifications_set)); | ||
| 55 | if (notifications_set_head) | ||
| 56 | { | ||
| 57 | memset (notifications_set_head, 0, sizeof(struct notifications_set)); | ||
| 58 | notifications_set_head->next | ||
| 59 | = notifications_set_head->prev = notifications_set_head; | ||
| 60 | } | ||
| 61 | else | ||
| 62 | DebPrint(("Out of memory: can't initialize notifications sets.")); | ||
| 63 | |||
| 51 | #ifdef WINDOWSNT | 64 | #ifdef WINDOWSNT |
| 52 | keyboard_handle = input_available; | 65 | keyboard_handle = input_available; |
| 53 | #endif /* WINDOWSNT */ | 66 | #endif /* WINDOWSNT */ |
| @@ -76,6 +89,21 @@ delete_crit (void) | |||
| 76 | CloseHandle (interrupt_handle); | 89 | CloseHandle (interrupt_handle); |
| 77 | interrupt_handle = NULL; | 90 | interrupt_handle = NULL; |
| 78 | } | 91 | } |
| 92 | |||
| 93 | if (notifications_set_head) | ||
| 94 | { | ||
| 95 | /* Free any remaining notifications set that could be left over. */ | ||
| 96 | while (notifications_set_head->next != notifications_set_head) | ||
| 97 | { | ||
| 98 | struct notifications_set *ns = notifications_set_head->next; | ||
| 99 | notifications_set_head->next = ns->next; | ||
| 100 | ns->next->prev = notifications_set_head; | ||
| 101 | if (ns->notifications) | ||
| 102 | free (ns->notifications); | ||
| 103 | free (ns); | ||
| 104 | } | ||
| 105 | } | ||
| 106 | free (notifications_set_head); | ||
| 79 | } | 107 | } |
| 80 | 108 | ||
| 81 | void | 109 | void |