Commit 326fff41 authored by Fabrice Popineau's avatar Fabrice Popineau Committed by Eli Zaretskii

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.
parent 2fbdb1bb
......@@ -620,70 +620,89 @@ maybe_generate_resize_event (void)
int
handle_file_notifications (struct input_event *hold_quit)
{
BYTE *p = file_notifications;
FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
const DWORD min_size
= offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
struct input_event inev;
struct notifications_set *ns = NULL;
int nevents = 0;
int done = 0;
/* We cannot process notification before Emacs is fully initialized,
since we need the UTF-16LE coding-system to be set up. */
if (!initialized)
{
notification_buffer_in_use = 0;
return nevents;
}
enter_crit ();
if (notification_buffer_in_use)
while (!done)
{
DWORD info_size = notifications_size;
Lisp_Object cs = Qutf_16le;
Lisp_Object obj = w32_get_watch_object (notifications_desc);
/* notifications_size could be zero when the buffer of
notifications overflowed on the OS level, or when the
directory being watched was itself deleted. Do nothing in
that case. */
if (info_size
&& !NILP (obj) && CONSP (obj))
{
Lisp_Object callback = XCDR (obj);
ns = NULL;
EVENT_INIT (inev);
/* Find out if there is a record available in the linked list of
notifications sets. If so, unlink te set from the linked list.
Use the critical section. */
enter_crit ();
if (notifications_set_head->next != notifications_set_head)
{
ns = notifications_set_head->next;
ns->prev->next = ns->next;
ns->next->prev = ns->prev;
}
else
done = 1;
leave_crit();
while (info_size >= min_size)
if (ns)
{
BYTE *p = ns->notifications;
FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
const DWORD min_size
= offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
struct input_event inev;
DWORD info_size = ns->size;
Lisp_Object cs = Qutf_16le;
Lisp_Object obj = w32_get_watch_object (ns->desc);
/* notifications size could be zero when the buffer of
notifications overflowed on the OS level, or when the
directory being watched was itself deleted. Do nothing in
that case. */
if (info_size
&& !NILP (obj) && CONSP (obj))
{
Lisp_Object utf_16_fn
= make_unibyte_string ((char *)fni->FileName,
fni->FileNameLength);
/* Note: mule-conf is preloaded, so utf-16le must
already be defined at this point. */
Lisp_Object fname
= code_convert_string_norecord (utf_16_fn, cs, 0);
Lisp_Object action = lispy_file_action (fni->Action);
inev.kind = FILE_NOTIFY_EVENT;
inev.timestamp = GetTickCount ();
inev.modifiers = 0;
inev.frame_or_window = callback;
inev.arg = Fcons (action, fname);
inev.arg = list3 (make_pointer_integer (notifications_desc),
action, fname);
kbd_buffer_store_event_hold (&inev, hold_quit);
nevents++;
if (!fni->NextEntryOffset)
break;
p += fni->NextEntryOffset;
fni = (PFILE_NOTIFY_INFORMATION)p;
info_size -= fni->NextEntryOffset;
Lisp_Object callback = XCDR (obj);
EVENT_INIT (inev);
while (info_size >= min_size)
{
Lisp_Object utf_16_fn
= make_unibyte_string ((char *)fni->FileName,
fni->FileNameLength);
/* Note: mule-conf is preloaded, so utf-16le must
already be defined at this point. */
Lisp_Object fname
= code_convert_string_norecord (utf_16_fn, cs, 0);
Lisp_Object action = lispy_file_action (fni->Action);
inev.kind = FILE_NOTIFY_EVENT;
inev.timestamp = GetTickCount ();
inev.modifiers = 0;
inev.frame_or_window = callback;
inev.arg = Fcons (action, fname);
inev.arg = list3 (make_pointer_integer (ns->desc),
action, fname);
kbd_buffer_store_event_hold (&inev, hold_quit);
nevents++;
if (!fni->NextEntryOffset)
break;
p += fni->NextEntryOffset;
fni = (PFILE_NOTIFY_INFORMATION)p;
info_size -= fni->NextEntryOffset;
}
}
/* Free this notification set. */
free (ns->notifications);
free (ns);
}
notification_buffer_in_use = 0;
}
leave_crit ();
return nevents;
}
#else /* !HAVE_W32NOTIFY */
......
This diff is collapsed.
......@@ -3211,71 +3211,85 @@ static void
queue_notifications (struct input_event *event, W32Msg *msg, struct frame *f,
int *evcount)
{
BYTE *p = file_notifications;
FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
const DWORD min_size
= offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
struct notifications_set *ns = NULL;
Lisp_Object frame;
int done = 0;
/* We cannot process notification before Emacs is fully initialized,
since we need the UTF-16LE coding-system to be set up. */
if (!initialized)
{
notification_buffer_in_use = 0;
return;
}
return;
XSETFRAME (frame, f);
enter_crit ();
if (notification_buffer_in_use)
while (!done)
{
DWORD info_size = notifications_size;
Lisp_Object cs = Qutf_16le;
Lisp_Object obj = w32_get_watch_object (notifications_desc);
/* notifications_size could be zero when the buffer of
notifications overflowed on the OS level, or when the
directory being watched was itself deleted. Do nothing in
that case. */
if (info_size
&& !NILP (obj) && CONSP (obj))
ns = NULL;
/* Find out if there is a record available in the linked list of
notifications sets. If so, unlink the set from the linked
list. Use critical section. */
enter_crit ();
if (notifications_set_head->next != notifications_set_head)
{
Lisp_Object callback = XCDR (obj);
ns = notifications_set_head->next;
ns->prev->next = ns->next;
ns->next->prev = ns->prev;
}
else
done = 1;
leave_crit();
while (info_size >= min_size)
if (ns)
{
BYTE *p = ns->notifications;
FILE_NOTIFY_INFORMATION *fni = (PFILE_NOTIFY_INFORMATION)p;
const DWORD min_size
= offsetof (FILE_NOTIFY_INFORMATION, FileName) + sizeof(wchar_t);
DWORD info_size = ns->size;
Lisp_Object cs = Qutf_16le;
Lisp_Object obj = w32_get_watch_object (ns->desc);
/* notifications size could be zero when the buffer of
notifications overflowed on the OS level, or when the
directory being watched was itself deleted. Do nothing in
that case. */
if (info_size
&& !NILP (obj) && CONSP (obj))
{
Lisp_Object utf_16_fn
= make_unibyte_string ((char *)fni->FileName,
fni->FileNameLength);
/* Note: mule-conf is preloaded, so utf-16le must
already be defined at this point. */
Lisp_Object fname
= code_convert_string_norecord (utf_16_fn, cs, 0);
Lisp_Object action = lispy_file_action (fni->Action);
event->kind = FILE_NOTIFY_EVENT;
event->timestamp = msg->msg.time;
event->modifiers = 0;
event->frame_or_window = callback;
event->arg = list3 (make_pointer_integer (notifications_desc),
action, fname);
kbd_buffer_store_event (event);
(*evcount)++;
if (!fni->NextEntryOffset)
break;
p += fni->NextEntryOffset;
fni = (PFILE_NOTIFY_INFORMATION)p;
info_size -= fni->NextEntryOffset;
Lisp_Object callback = XCDR (obj);
while (info_size >= min_size)
{
Lisp_Object utf_16_fn
= make_unibyte_string ((char *)fni->FileName,
fni->FileNameLength);
/* Note: mule-conf is preloaded, so utf-16le must
already be defined at this point. */
Lisp_Object fname
= code_convert_string_norecord (utf_16_fn, cs, 0);
Lisp_Object action = lispy_file_action (fni->Action);
event->kind = FILE_NOTIFY_EVENT;
event->timestamp = msg->msg.time;
event->modifiers = 0;
event->frame_or_window = callback;
event->arg = list3 (make_pointer_integer (ns->desc),
action, fname);
kbd_buffer_store_event (event);
(*evcount)++;
if (!fni->NextEntryOffset)
break;
p += fni->NextEntryOffset;
fni = (PFILE_NOTIFY_INFORMATION)p;
info_size -= fni->NextEntryOffset;
}
}
/* Free this notifications set. */
xfree (ns->notifications);
xfree (ns);
}
notification_buffer_in_use = 0;
}
else
DebPrint (("We were promised notifications, but in-use flag is zero!\n"));
leave_crit ();
/* We've stuffed all the events ourselves, so w32_read_socket shouldn't. */
event->kind = NO_EVENT;
}
......@@ -6949,6 +6963,8 @@ w32_init_main_thread (void)
DuplicateHandle (GetCurrentProcess (), GetCurrentThread (),
GetCurrentProcess (), &hMainThread, 0, TRUE,
DUPLICATE_SAME_ACCESS);
}
DWORD WINAPI w32_msg_worker (void * arg);
......
......@@ -727,10 +727,18 @@ extern void x_delete_display (struct w32_display_info *dpyinfo);
extern void x_query_color (struct frame *, XColor *);
extern volatile int notification_buffer_in_use;
extern BYTE file_notifications[16384];
extern DWORD notifications_size;
extern void *notifications_desc;
#define FILE_NOTIFICATIONS_SIZE 16384
/* Notifications come in sets. We use a doubly linked list with a
sentinel to communicate those sets from the watching threads to the
main thread. */
struct notifications_set {
LPBYTE notifications;
DWORD size;
void *desc;
struct notifications_set *next;
struct notifications_set *prev;
};
extern struct notifications_set *notifications_set_head;
extern Lisp_Object w32_get_watch_object (void *);
extern Lisp_Object lispy_file_action (DWORD);
extern int handle_file_notifications (struct input_event *);
......
......@@ -48,6 +48,19 @@ init_crit (void)
when the input queue is empty, so make it a manual reset event. */
input_available = CreateEvent (NULL, TRUE, FALSE, NULL);
/* Initialize the linked list of notifications sets that will be
used to communicate between the watching worker threads and the
main thread. */
notifications_set_head = malloc (sizeof(struct notifications_set));
if (notifications_set_head)
{
memset (notifications_set_head, 0, sizeof(struct notifications_set));
notifications_set_head->next
= notifications_set_head->prev = notifications_set_head;
}
else
DebPrint(("Out of memory: can't initialize notifications sets."));
#ifdef WINDOWSNT
keyboard_handle = input_available;
#endif /* WINDOWSNT */
......@@ -76,6 +89,21 @@ delete_crit (void)
CloseHandle (interrupt_handle);
interrupt_handle = NULL;
}
if (notifications_set_head)
{
/* Free any remaining notifications set that could be left over. */
while (notifications_set_head->next != notifications_set_head)
{
struct notifications_set *ns = notifications_set_head->next;
notifications_set_head->next = ns->next;
ns->next->prev = notifications_set_head;
if (ns->notifications)
free (ns->notifications);
free (ns);
}
}
free (notifications_set_head);
}
void
......
......@@ -433,7 +433,8 @@ longer than timeout seconds for the events to be delivered."
(write-region
"any text" nil file-notify--test-tmpfile nil 'no-message)
(read-event nil nil file-notify--test-read-event-timeout)
(delete-directory temporary-file-directory 'recursive))
(delete-directory temporary-file-directory 'recursive)
(read-event nil nil file-notify--test-read-event-timeout))
(file-notify-rm-watch file-notify--test-desc))
;; Check copy of files inside a directory.
......@@ -475,7 +476,8 @@ longer than timeout seconds for the events to be delivered."
(read-event nil nil file-notify--test-read-event-timeout)
(set-file-times file-notify--test-tmpfile '(0 0))
(read-event nil nil file-notify--test-read-event-timeout)
(delete-directory temporary-file-directory 'recursive))
(delete-directory temporary-file-directory 'recursive)
(read-event nil nil file-notify--test-read-event-timeout))
(file-notify-rm-watch file-notify--test-desc))
;; Check rename of files inside a directory.
......@@ -509,7 +511,8 @@ longer than timeout seconds for the events to be delivered."
(rename-file file-notify--test-tmpfile file-notify--test-tmpfile1)
;; After the rename, we won't get events anymore.
(read-event nil nil file-notify--test-read-event-timeout)
(delete-directory temporary-file-directory 'recursive))
(delete-directory temporary-file-directory 'recursive)
(read-event nil nil file-notify--test-read-event-timeout))
(file-notify-rm-watch file-notify--test-desc))
;; Check attribute change. Does not work for cygwin.
......@@ -527,7 +530,7 @@ longer than timeout seconds for the events to be delivered."
;; w32notify does not distinguish between `changed' and
;; `attribute-changed'.
((string-equal (file-notify--test-library) "w32notify")
'(changed changed changed changed))
'(changed changed))
;; For kqueue and in the remote case, `write-region'
;; raises also an `attribute-changed' event.
((or (string-equal (file-notify--test-library) "kqueue")
......@@ -754,7 +757,9 @@ longer than timeout seconds for the events to be delivered."
(should (file-notify-valid-p file-notify--test-desc))
;; After removing the watch, the descriptor must not be valid
;; anymore.
(read-event nil nil file-notify--test-read-event-timeout)
(file-notify-rm-watch file-notify--test-desc)
(read-event nil nil file-notify--test-read-event-timeout)
(file-notify--wait-for-events
(file-notify--test-timeout)
(not (file-notify-valid-p file-notify--test-desc)))
......@@ -776,7 +781,9 @@ longer than timeout seconds for the events to be delivered."
(should (file-notify-valid-p file-notify--test-desc))
;; After deleting the directory, the descriptor must not be
;; valid anymore.
(read-event nil nil file-notify--test-read-event-timeout)
(delete-directory file-notify--test-tmpfile t)
(read-event nil nil file-notify--test-read-event-timeout)
(file-notify--wait-for-events
(file-notify--test-timeout)
(not (file-notify-valid-p file-notify--test-desc)))
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment