OSDN Git Service

* bsd_mutex.cc: Include limits.h.
authorcorinna <corinna>
Thu, 30 Dec 2004 15:58:27 +0000 (15:58 +0000)
committercorinna <corinna>
Thu, 30 Dec 2004 15:58:27 +0000 (15:58 +0000)
(MSLEEP_MUTEX): New define for third parameter to msleep_event_name.
(MSLEEP_SEM): Ditto.
(MSLEEP_EVENT): Ditto.
(msleep_event_name): Add third parameter to allow multiple
synchronization objects per ident.
(_msleep): Implement new synchronization technique to make sure
that all threads have been woken up by a corresponding wakeup call.
(wakeup): Ditto.

winsup/cygserver/ChangeLog
winsup/cygserver/bsd_mutex.cc

index 157b093..9b4ed31 100644 (file)
@@ -1,3 +1,15 @@
+2004-12-30  Corinna Vinschen  <corinna@vinschen.de>
+
+       * bsd_mutex.cc: Include limits.h.
+       (MSLEEP_MUTEX): New define for third parameter to msleep_event_name.
+       (MSLEEP_SEM): Ditto.
+       (MSLEEP_EVENT): Ditto.
+       (msleep_event_name): Add third parameter to allow multiple
+       synchronization objects per ident.
+       (_msleep): Implement new synchronization technique to make sure
+       that all threads have been woken up by a corresponding wakeup call.
+       (wakeup): Ditto.
+
 2004-10-18  Corinna Vinschen  <corinna@vinschen.de>
 
        * sysv_sem.cc: Redefine offsetof to circumvent build problems with
index 8738914..2721748 100644 (file)
@@ -13,6 +13,7 @@ details. */
 #define _KERNEL 1
 #define __BSD_VISIBLE 1
 #include <sys/smallprint.h>
+#include <limits.h>
 
 #include "process.h"
 #include "cygserver_ipc.h"
@@ -96,13 +97,20 @@ mtx_destroy (mtx *m)
 /*
  * Helper functions for msleep/wakeup.
  */
+
+/* Values for which */
+#define MSLEEP_MUTEX   0
+#define MSLEEP_SEM     1
+#define MSLEEP_EVENT   2
+
 static char *
-msleep_event_name (void *ident, char *name)
+msleep_event_name (void *ident, char *name, int which)
 {
   if (wincap.has_terminal_services ())
-    __small_sprintf (name, "Global\\cygserver.msleep.evt.%08x", ident);
+    __small_sprintf (name, "Global\\cygserver.msleep.evt.%1d.%08x",
+                    which, ident);
   else
-    __small_sprintf (name, "cygserver.msleep.evt.%08x", ident);
+    __small_sprintf (name, "cygserver.msleep.evt.%1d.%08x", which, ident);
   return name;
 }
 
@@ -179,8 +187,37 @@ _msleep (void *ident, struct mtx *mtx, int priority,
 {
   int ret = -1;
   char name[64];
-  msleep_event_name (ident, name);
-  HANDLE evt = CreateEvent (NULL, TRUE, FALSE, name);
+
+  /* The mutex is used to indicate an ident specific critical section.
+     The critical section is needed to synchronize access to the
+     semaphore and eventually the event object.  The whole idea is
+     that a wakeup is *guaranteed* to wakeup *all* threads.  If that's
+     not synchronized, sleeping threads could return into the msleep
+     function before all other threads have called CloseHandle(evt).
+     That's bad, since the event still exists and is signalled! */
+  HANDLE mutex = CreateMutex (NULL, FALSE,
+                             msleep_event_name (ident, name, MSLEEP_MUTEX));
+  if (!mutex)
+    panic ("CreateMutex in msleep (%s) failed: %E", wmesg);
+  WaitForSingleObject (mutex, INFINITE);
+
+  /* Ok, we're in the critical section now.  We create an ident specific
+     semaphore, which is used to synchronize the waiting threads. */
+  HANDLE sem = CreateSemaphore (NULL, 0, LONG_MAX,
+                               msleep_event_name (ident, name, MSLEEP_SEM));
+  if (!sem)
+    panic ("CreateSemaphore in msleep (%s) failed: %E", wmesg);
+
+  /* This thread is one more thread sleeping.  The semaphore value is
+     so used as a counter of sleeping threads.  That info is needed by
+     the wakeup function. */
+  ReleaseSemaphore (sem, 1, NULL);
+
+  /* Leave critical section. */
+  ReleaseMutex (mutex);
+
+  HANDLE evt = CreateEvent (NULL, TRUE, FALSE,
+                           msleep_event_name (ident, name, MSLEEP_EVENT));
   if (!evt)
     panic ("CreateEvent in msleep (%s) failed: %E", wmesg);
   if (mtx)
@@ -199,6 +236,7 @@ _msleep (void *ident, struct mtx *mtx, int priority,
   if ((priority & PCATCH)
       && td->client->signal_arrived () != INVALID_HANDLE_VALUE)
     obj_cnt = 4;
+  
   switch (WaitForMultipleObjects (obj_cnt, obj, FALSE, timo ?: INFINITE))
     {
       case WAIT_OBJECT_0:      /* wakeup() has been called. */
@@ -220,17 +258,23 @@ _msleep (void *ident, struct mtx *mtx, int priority,
        panic ("wait in msleep (%s) failed, %E", wmesg);
        break;
     }
-#if 0
-  /* Dismiss event before entering mutex. */
-  /* CV 2004-09-06, Don't dismiss for now. 
-     TODO: Dismissing was meant to solve a problem with heavy load but
-     there's no proof that it helps.  On the contrary, it breaks msgtest
-     in the testsuite.  As long as I don't get a testcase to track that
-     down, I'll keep it that way. */
-  ResetEvent (evt);
-#endif
+
   CloseHandle (evt);
+  /* wakeup has reset the semaphore to 0.  Now indicate that this thread
+     has called CloseHandle (evt) and enter the critical section.  The
+     critical section is still hold by wakeup, until all formerly sleeping
+     threads have indicated that the event has been dismissed.  That's
+     the signal for wakeup that it's the only thread still holding a
+     handle to the event object.  wakeup will then close the last handle
+     and leave the critical section. */
+  ReleaseSemaphore (sem, 1, NULL);
+  WaitForSingleObject (mutex, INFINITE);
+  CloseHandle (sem);
+  ReleaseMutex (mutex);
+  CloseHandle (mutex);
+
   set_priority (old_priority);
+
   if (mtx && !(priority & PDROP))
     mtx_lock (mtx);
   return ret;
@@ -243,22 +287,69 @@ int
 wakeup (void *ident)
 {
   char name[64];
-  msleep_event_name (ident, name);
-  HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE, name);
-  if (!evt)
+  LONG threads;
+
+  HANDLE evt = OpenEvent (EVENT_MODIFY_STATE, FALSE,
+                         msleep_event_name (ident, name, MSLEEP_EVENT));
+  if (!evt) /* No thread is waiting. */
     {
       /* Another round of different error codes returned by 9x and NT
          systems. Oh boy... */
       if (  (!wincap.is_winnt () && GetLastError () != ERROR_INVALID_NAME)
          || (wincap.is_winnt () && GetLastError () != ERROR_FILE_NOT_FOUND))
        panic ("OpenEvent (%s) in wakeup failed: %E", name);
+      return 0;
     }
+
+  /* The mutex is used to indicate an ident specific critical section.
+     The critical section is needed to synchronize access to the
+     semaphore and eventually the event object.  The whole idea is
+     that a wakeup is *guaranteed* to wakeup *all* threads.  If that's
+     not synchronized, sleeping threads could return into the msleep
+     function before all other threads have called CloseHandle(evt).
+     That's bad, since the event still exists and is signalled! */
+  HANDLE mutex = OpenMutex (MUTEX_ALL_ACCESS, FALSE,
+                           msleep_event_name (ident, name, MSLEEP_MUTEX));
+  if (!mutex)
+    panic ("OpenMutex (%s) in wakeup failed: %E", name);
+  WaitForSingleObject (mutex, INFINITE);
+  /* Ok, we're in the critical section now.  We create an ident specific
+     semaphore, which is used to synchronize the waiting threads. */
+  HANDLE sem = OpenSemaphore (SEMAPHORE_ALL_ACCESS, FALSE,
+                             msleep_event_name (ident, name, MSLEEP_SEM));
+  if (!sem)
+    panic ("OpenSemaphore (%s) in wakeup failed: %E", name);
+  ReleaseSemaphore (sem, 1, &threads);
+  /* `threads' is the number of waiting threads.  Now reset the semaphore
+     to 0 and wait for this number of threads to indicate that they have
+     called CloseHandle (evt).  Then it's save to do the same here in
+     wakeup, which then means that the event object is destroyed and
+     can get safely recycled. */
+  for (int i = threads + 1; i > 0; --i)
+    WaitForSingleObject (sem, INFINITE);
+
+  if (!SetEvent (evt))
+    panic ("SetEvent (%s) in wakeup failed, %E", name);
+
+  /* Now wait for all threads which were waiting for this wakeup. */
+  while (threads-- > 0)
+    WaitForSingleObject (sem, INFINITE);
+
+  /* Now our handle is the last handle to this event object. */
+  CloseHandle (evt);
+  /* But paranoia rulez, so we check here again. */
+  evt = OpenEvent (EVENT_MODIFY_STATE, FALSE,
+                  msleep_event_name (ident, name, MSLEEP_EVENT));
   if (evt)
-    {
-      if (!SetEvent (evt))
-       panic ("SetEvent (%s) in wakeup failed, %E", name);
-      CloseHandle (evt);
-    }
+    panic ("Event %s has not been destroyed.  Obviously I can't count :-(",
+          name);
+
+  CloseHandle (sem);
+
+  /* Leave critical section (all of wakeup is critical). */
+  ReleaseMutex (mutex);
+  CloseHandle (mutex);
+
   return 0;
 }