Misc updates for admin/make-tarball.txt
[bpt/emacs.git] / src / w32notify.c
index 244b0b8..d78e55f 100644 (file)
@@ -1,5 +1,5 @@
 /* Filesystem notifications support for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 2012  Free Software Foundation, Inc.
+   Copyright (C) 2012-2013 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -16,15 +16,18 @@ GNU General Public License for more details.
 You should have received a copy of the GNU General Public License
 along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
-/* Design overview:
+/* Written by Eli Zaretskii <eliz@gnu.org>.
+
+   Design overview:
 
    For each watch request, we launch a separate worker thread.  The
    worker thread runs the watch_worker function, which issues an
-   asynchronous call to ReadDirectoryChangesW, and then waits for that
-   call to complete in SleepEx.  Waiting in SleepEx puts the thread in
-   an alertable state, so it wakes up when either (a) the call to
-   ReadDirectoryChangesW completes, or (b) the main thread instructs
-   the worker thread to terminate by sending it an APC, see below.
+   asynchronous call to ReadDirectoryChangesW, and then waits in
+   SleepEx for that call to complete.  Waiting in SleepEx puts the
+   thread in an "alertable" state, so it wakes up when either (a) the
+   call to ReadDirectoryChangesW completes, or (b) the main thread
+   instructs the worker thread to terminate by sending it an APC, see
+   below.
 
    When the ReadDirectoryChangesW call completes, its completion
    routine watch_completion is automatically called.  watch_completion
@@ -59,9 +62,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    bound to a command.  The default binding is w32notify-handle-event,
    defined on subr.el.
 
-   After w32_read_socket or w32_console_read_socket is done processing
-   the notifications, it resets a flag signaling to all watch worker
-   threads that the notifications buffer is available for more input.
+   After w32_read_socket or w32_console_read_socket are done
+   processing the notifications, they reset a flag signaling to all
+   watch worker threads that the notifications buffer is available for
+   more input.
 
    When the watch is removed by a call to w32notify-rm-watch, the main
    thread requests that the worker thread terminates by queuing an APC
@@ -72,9 +76,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
    watch_completion function is called one last time with the
    ERROR_OPERATION_ABORTED status, which causes it to clean up and set
    a flag telling watch_worker to exit without issuing another
-   ReadDirectoryChangesW call.  The main thread waits for some time
-   for the worker thread to exit, and if it doesn't, terminates it
-   forcibly.  */
+   ReadDirectoryChangesW call.  Since watch_worker is the thread
+   procedure of the worker thread, exiting it causes the thread to
+   exit.  The main thread waits for some time for the worker thread to
+   exit, and if it doesn't, terminates it forcibly.  */
 
 #include <stddef.h>
 #include <errno.h>
@@ -103,12 +108,12 @@ struct notification {
   char *watchee;       /* the file we are interested in */
   HANDLE dir;          /* handle to the watched directory */
   HANDLE thr;          /* handle to the thread that watches */
-  int terminate;       /* if non-zero, request for the thread to terminate */
+  volatile int terminate; /* if non-zero, request for the thread to terminate */
   unsigned signature;
 };
 
 /* Used for communicating notifications to the main thread.  */
-int notification_buffer_in_use;
+volatile int notification_buffer_in_use;
 BYTE file_notifications[16384];
 DWORD notifications_size;
 void *notifications_desc;
@@ -120,7 +125,8 @@ static Lisp_Object Qsecurity_desc, Qsubtree, watch_list;
 /* Signal to the main thread that we have file notifications for it to
    process.  */
 static void
-send_notifications (BYTE *info, DWORD info_size, void *desc, int *terminate)
+send_notifications (BYTE *info, DWORD info_size, void *desc,
+                   volatile int *terminate)
 {
   int done = 0;
   FRAME_PTR f = SELECTED_FRAME ();
@@ -184,9 +190,9 @@ watch_end (ULONG_PTR arg)
     }
 }
 
-/* A completion routine (a.k.a. APC function) for handling events read
-   by ReadDirectoryChangesW.  Called by the OS when the thread which
-   issued the asynchronous ReadDirectoryChangesW call is in the
+/* A completion routine (a.k.a. "APC function") for handling events
+   read by ReadDirectoryChangesW.  Called by the OS when the thread
+   which issued the asynchronous ReadDirectoryChangesW call is in the
    "alertable state", i.e. waiting inside SleepEx call.  */
 VOID CALLBACK
 watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
@@ -211,16 +217,12 @@ watch_completion (DWORD status, DWORD bytes_ret, OVERLAPPED *io_info)
       /* We've been called because the main thread told us to issue
         CancelIo on the directory we watch, and watch_end did so.
         The directory handle is already closed.  We should clean up
-        and exit, signalling to the thread worker routine not to
+        and exit, signaling to the thread worker routine not to
         issue another call to ReadDirectoryChangesW.  Note that we
-        don't free the dirwatch object itself; this is done by the
-        main thread in remove_watch.  */
-      xfree (dirwatch->buf);
-      dirwatch->buf = NULL;
-      xfree (dirwatch->io_info);
-      dirwatch->io_info = NULL;
-      xfree (dirwatch->watchee);
-      dirwatch->watchee = NULL;
+        don't free the dirwatch object itself nor the memory consumed
+        by its buffers; this is done by the main thread in
+        remove_watch.  Calling malloc/free from a thread other than
+        the main thread is a no-no.  */
       dirwatch->dir = NULL;
       dirwatch->terminate = 1;
     }
@@ -253,19 +255,15 @@ watch_worker (LPVOID arg)
          {
            DebPrint (("watch_worker, abnormal exit: %lu\n", GetLastError ()));
            /* We cannot remove the dirwatch object from watch_list,
-              because we are in a separate thread.  So we free and
-              zero out all the pointers in the object, but do not
-              free the object itself.  We also don't touch the
-              signature.  This way, remove_watch can still identify
-              the object, remove it, and free its memory.  */
-           xfree (dirwatch->buf);
-           dirwatch->buf = NULL;
-           xfree (dirwatch->io_info);
-           dirwatch->io_info = NULL;
+              because we are in a separate thread.  For the same
+              reason, we also cannot free memory consumed by the
+              buffers allocated for the dirwatch object.  So we close
+              the directory handle, but do not free the object itself
+              or its buffers.  We also don't touch the signature.
+              This way, remove_watch can still identify the object,
+              remove it, and free its memory.  */
            CloseHandle (dirwatch->dir);
            dirwatch->dir = NULL;
-           xfree (dirwatch->watchee);
-           dirwatch->watchee = NULL;
            return 1;
          }
       }
@@ -557,7 +555,7 @@ FILE is the name of the file whose event is being reported.  */)
        report_file_error ("Cannot watch file", Fcons (file, Qnil));
     }
   /* Store watch object in watch list. */
-  watch_descriptor = make_number (dirwatch);
+  watch_descriptor = XIL ((EMACS_INT)dirwatch);
   watch_object = Fcons (watch_descriptor, callback);
   watch_list = Fcons (watch_object, watch_list);
 
@@ -572,17 +570,22 @@ WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.  */)
      (Lisp_Object watch_descriptor)
 {
   Lisp_Object watch_object;
-  struct notification *dirwatch =
-    (struct notification *)XINT (watch_descriptor);
+  struct notification *dirwatch;
+  int status = -1;
 
   /* Remove the watch object from watch list.  Do this before freeing
      the object, do that even if we fail to free it, watch_list is
      kept free of junk.  */
   watch_object = Fassoc (watch_descriptor, watch_list);
   if (!NILP (watch_object))
-    watch_list = Fdelete (watch_object, watch_list);
+    {
+      watch_list = Fdelete (watch_object, watch_list);
+      dirwatch = (struct notification *)XLI (watch_descriptor);
+      if (w32_valid_pointer_p (dirwatch, sizeof(struct notification)))
+       status = remove_watch (dirwatch);
+    }
 
-  if (remove_watch (dirwatch) == -1)
+  if (status == -1)
     report_file_error ("Invalid watch descriptor", Fcons (watch_descriptor,
                                                          Qnil));
 
@@ -590,9 +593,14 @@ WATCH-DESCRIPTOR should be an object returned by `w32notify-add-watch'.  */)
 }
 
 Lisp_Object
-w32_get_watch_object (Lisp_Object desc)
+w32_get_watch_object (void *desc)
 {
-  return NILP (watch_list) ? Qnil : assoc_no_quit (desc, watch_list);
+  Lisp_Object descriptor = XIL ((EMACS_INT)desc);
+
+  /* This is called from the input queue handling code, inside a
+     critical section, so we cannot possibly QUIT if watch_list is not
+     in the right condition.  */
+  return NILP (watch_list) ? Qnil : assoc_no_quit (descriptor, watch_list);
 }
 
 void