From 150a7a6e99d3cf8fd0b6315e7b184b5cce0f4ce4 Mon Sep 17 00:00:00 2001
From: rpj <rpj>
Date: Mon, 25 Apr 2005 00:45:59 +0000
Subject: Fix timeout bug

---
 sem_timedwait.c | 476 ++++++++++++++++++++++++++------------------------------
 1 file changed, 220 insertions(+), 256 deletions(-)

diff --git a/sem_timedwait.c b/sem_timedwait.c
index 7fba45f..01bab42 100644
--- a/sem_timedwait.c
+++ b/sem_timedwait.c
@@ -1,256 +1,220 @@
-/*
- * -------------------------------------------------------------
- *
- * Module: sem_timedwait.c
- *
- * Purpose:
- *	Semaphores aren't actually part of the PThreads standard.
- *	They are defined by the POSIX Standard:
- *
- *		POSIX 1003.1b-1993	(POSIX.1b)
- *
- * -------------------------------------------------------------
- *
- * --------------------------------------------------------------------------
- *
- *      Pthreads-win32 - POSIX Threads Library for Win32
- *      Copyright(C) 1998 John E. Bossom
- *      Copyright(C) 1999,2005 Pthreads-win32 contributors
- * 
- *      Contact Email: rpj@callisto.canberra.edu.au
- * 
- *      The current list of contributors is contained
- *      in the file CONTRIBUTORS included with the source
- *      code distribution. The list can also be seen at the
- *      following World Wide Web location:
- *      http://sources.redhat.com/pthreads-win32/contributors.html
- * 
- *      This library is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU Lesser General Public
- *      License as published by the Free Software Foundation; either
- *      version 2 of the License, or (at your option) any later version.
- * 
- *      This library is distributed in the hope that it will be useful,
- *      but WITHOUT ANY WARRANTY; without even the implied warranty of
- *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *      Lesser General Public License for more details.
- * 
- *      You should have received a copy of the GNU Lesser General Public
- *      License along with this library in the file COPYING.LIB;
- *      if not, write to the Free Software Foundation, Inc.,
- *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
- */
-
-#ifndef _UWIN
-//#include <process.h>
-#endif
-#ifndef NEED_FTIME
-#include <sys/timeb.h>
-#endif
-
-#include "pthread.h"
-#include "semaphore.h"
-#include "implement.h"
-
-static void PTW32_CDECL
-ptw32_sem_timedwait_cleanup (void * sem)
-{
-  sem_t s = (sem_t) sem;
-
-  if (pthread_mutex_lock (&s->lock) == 0)
-    {
-      ++s->value;
-      /*
-       * Don't release the W32 sema, it doesn't need adjustment
-       * because it doesn't record the number of waiters.
-       */
-      (void) pthread_mutex_unlock (&s->lock);
-    }
-}
-
-int
-sem_timedwait (sem_t * sem, const struct timespec *abstime)
-     /*
-      * ------------------------------------------------------
-      * DOCPUBLIC
-      *      This function waits on a semaphore possibly until
-      *      'abstime' time.
-      *
-      * PARAMETERS
-      *      sem
-      *              pointer to an instance of sem_t
-      *
-      *      abstime
-      *              pointer to an instance of struct timespec
-      *
-      * DESCRIPTION
-      *      This function waits on a semaphore. If the
-      *      semaphore value is greater than zero, it decreases
-      *      its value by one. If the semaphore value is zero, then
-      *      the calling thread (or process) is blocked until it can
-      *      successfully decrease the value or until interrupted by
-      *      a signal.
-      *
-      *      If 'abstime' is a NULL pointer then this function will
-      *      block until it can successfully decrease the value or
-      *      until interrupted by a signal.
-      *
-      * RESULTS
-      *              0               successfully decreased semaphore,
-      *              -1              failed, error in errno
-      * ERRNO
-      *              EINVAL          'sem' is not a valid semaphore,
-      *              ENOSYS          semaphores are not supported,
-      *              EINTR           the function was interrupted by a signal,
-      *              EDEADLK         a deadlock condition was detected.
-      *              ETIMEDOUT       abstime elapsed before success.
-      *
-      * ------------------------------------------------------
-      */
-{
-  int result = 0;
-  sem_t s = *sem;
-
-#ifdef NEED_FTIME
-
-  struct timespec currSysTime;
-
-#else /* NEED_FTIME */
-
-  struct _timeb currSysTime;
-
-#endif /* NEED_FTIME */
-
-  const int64_t NANOSEC_PER_MILLISEC = 1000000;
-  const int64_t MILLISEC_PER_SEC = 1000;
-  DWORD milliseconds;
-  int64_t tmpAbsMilliseconds;
-  int64_t tmpCurrMilliseconds;
-
-  if (sem == NULL)
-    {
-      result = EINVAL;
-    }
-  else
-    {
-      if (abstime == NULL)
-	{
-	  milliseconds = INFINITE;
-	}
-      else
-	{
-	  /* 
-	   * Calculate timeout as milliseconds from current system time. 
-	   */
-
-	  /*
-	   * subtract current system time from abstime in a way that checks
-	   * that abstime is never in the past, or is never equivalent to the
-	   * defined INFINITE value (0xFFFFFFFF).
-	   *
-	   * Assume all integers are unsigned, i.e. cannot test if less than 0.
-	   */
-	  tmpAbsMilliseconds =  (int64_t)abstime->tv_sec * MILLISEC_PER_SEC;
-	  tmpAbsMilliseconds += ((int64_t)abstime->tv_nsec + (NANOSEC_PER_MILLISEC/2)) / NANOSEC_PER_MILLISEC;
-
-	  /* get current system time */
-
-#ifdef NEED_FTIME
-
-	  {
-	    FILETIME ft;
-	    SYSTEMTIME st;
-
-	    GetSystemTime(&st);
-	    SystemTimeToFileTime(&st, &ft);
-	    /*
-	     * GetSystemTimeAsFileTime(&ft); would be faster,
-	     * but it does not exist on WinCE
-	     */
-
-	    ptw32_filetime_to_timespec(&ft, &currSysTime);
-	  }
-
-	  tmpCurrMilliseconds = (int64_t)currSysTime.tv_sec * MILLISEC_PER_SEC;
-	  tmpCurrMilliseconds += ((int64_t)currSysTime.tv_nsec + (NANOSEC_PER_MILLISEC/2)) / NANOSEC_PER_MILLISEC;
-
-#else /* ! NEED_FTIME */
-
-	  _ftime(&currSysTime);
-
-	  tmpCurrMilliseconds = (int64_t) currSysTime.time * MILLISEC_PER_SEC;
-	  tmpCurrMilliseconds += (int64_t) currSysTime.millitm;
-
-#endif /* NEED_FTIME */
-
-	  if (tmpAbsMilliseconds > tmpCurrMilliseconds)
-	    {
-	      milliseconds = (DWORD) (tmpAbsMilliseconds - tmpCurrMilliseconds);
-	      if (milliseconds == INFINITE)
-		{
-		  /* Timeouts must be finite */
-		  milliseconds--;
-		}
-	    }
-	  else
-	    {
-	      /* The abstime given is in the past */
-	      milliseconds = 0;
-	    }
-	}
-
-#ifdef NEED_SEM
-
-      result = (pthreadCancelableTimedWait (s->event, milliseconds));
-
-#else /* NEED_SEM */
-
-      pthread_testcancel();
-
-      if ((result = pthread_mutex_lock (&s->lock)) == 0)
-	{
-	  int v = --s->value;
-	  (void) pthread_mutex_unlock (&s->lock);
-
-	  if (v < 0)
-	    {
-	      /* Must wait */
-#ifdef _MSC_VER
-#pragma inline_depth(0)
-#endif
-              pthread_cleanup_push(ptw32_sem_timedwait_cleanup, (void *) s);
-	      result = pthreadCancelableTimedWait (s->sem, milliseconds);
-	      /*
-	       * Restore the semaphore counter if no longer waiting
-	       * and not taking the semaphore. This will occur if the
-	       * thread is cancelled while waiting, or the wake was
-	       * not the result of a post event given to us, e.g. a timeout.
-	       */
-              pthread_cleanup_pop(result);
-#ifdef _MSC_VER
-#pragma inline_depth()
-#endif
-	    }
-	}
-
-#endif
-
-    }
-
-  if (result != 0)
-    {
-
-      errno = result;
-      return -1;
-
-    }
-
-#ifdef NEED_SEM
-
-  ptw32_decrease_semaphore (sem);
-
-#endif /* NEED_SEM */
-
-  return 0;
-
-}				/* sem_timedwait */
+/*
+ * -------------------------------------------------------------
+ *
+ * Module: sem_timedwait.c
+ *
+ * Purpose:
+ *	Semaphores aren't actually part of the PThreads standard.
+ *	They are defined by the POSIX Standard:
+ *
+ *		POSIX 1003.1b-1993	(POSIX.1b)
+ *
+ * -------------------------------------------------------------
+ *
+ * --------------------------------------------------------------------------
+ *
+ *      Pthreads-win32 - POSIX Threads Library for Win32
+ *      Copyright(C) 1998 John E. Bossom
+ *      Copyright(C) 1999,2005 Pthreads-win32 contributors
+ * 
+ *      Contact Email: rpj@callisto.canberra.edu.au
+ * 
+ *      The current list of contributors is contained
+ *      in the file CONTRIBUTORS included with the source
+ *      code distribution. The list can also be seen at the
+ *      following World Wide Web location:
+ *      http://sources.redhat.com/pthreads-win32/contributors.html
+ * 
+ *      This library is free software; you can redistribute it and/or
+ *      modify it under the terms of the GNU Lesser General Public
+ *      License as published by the Free Software Foundation; either
+ *      version 2 of the License, or (at your option) any later version.
+ * 
+ *      This library is distributed in the hope that it will be useful,
+ *      but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *      Lesser General Public License for more details.
+ * 
+ *      You should have received a copy of the GNU Lesser General Public
+ *      License along with this library in the file COPYING.LIB;
+ *      if not, write to the Free Software Foundation, Inc.,
+ *      59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include "pthread.h"
+#include "semaphore.h"
+#include "implement.h"
+
+
+typedef struct {
+  sem_t sem;
+  int * resultPtr;
+} sem_timedwait_cleanup_args_t;
+
+
+static void PTW32_CDECL
+ptw32_sem_timedwait_cleanup (void * args)
+{
+  sem_timedwait_cleanup_args_t * a = (sem_timedwait_cleanup_args_t *)args;
+  sem_t s = a->sem;
+
+  if (pthread_mutex_lock (&s->lock) == 0)
+    {
+      /*
+       * We either timed out or were cancelled.
+       * If someone posted since then we try to take the semaphore.
+       * Otherwise the semaphore count may be wrong after we
+       * return. In the case of a cancellation, it is as if we
+       * were cancelled just before we return (after taking the semaphore)
+       * which is ok.
+       */
+      if (WaitForSingleObject(s->sem, 0) == WAIT_OBJECT_0)
+	{
+	  /* We got the semaphore on the second attempt */
+	  *(a->resultPtr) = 0;
+	}
+      else
+	{
+	  /* Indicate we're no longer waiting */
+	  s->value++;
+#ifdef NEED_SEM
+	  if (s->value > 0)
+	    {
+	      s->leftToUnblock = 0;
+	    }
+#else
+          /*
+           * Don't release the W32 sema, it doesn't need adjustment
+           * because it doesn't record the number of waiters.
+           */
+#endif
+	}
+      (void) pthread_mutex_unlock (&s->lock);
+    }
+}
+
+
+int
+sem_timedwait (sem_t * sem, const struct timespec *abstime)
+     /*
+      * ------------------------------------------------------
+      * DOCPUBLIC
+      *      This function waits on a semaphore possibly until
+      *      'abstime' time.
+      *
+      * PARAMETERS
+      *      sem
+      *              pointer to an instance of sem_t
+      *
+      *      abstime
+      *              pointer to an instance of struct timespec
+      *
+      * DESCRIPTION
+      *      This function waits on a semaphore. If the
+      *      semaphore value is greater than zero, it decreases
+      *      its value by one. If the semaphore value is zero, then
+      *      the calling thread (or process) is blocked until it can
+      *      successfully decrease the value or until interrupted by
+      *      a signal.
+      *
+      *      If 'abstime' is a NULL pointer then this function will
+      *      block until it can successfully decrease the value or
+      *      until interrupted by a signal.
+      *
+      * RESULTS
+      *              0               successfully decreased semaphore,
+      *              -1              failed, error in errno
+      * ERRNO
+      *              EINVAL          'sem' is not a valid semaphore,
+      *              ENOSYS          semaphores are not supported,
+      *              EINTR           the function was interrupted by a signal,
+      *              EDEADLK         a deadlock condition was detected.
+      *              ETIMEDOUT       abstime elapsed before success.
+      *
+      * ------------------------------------------------------
+      */
+{
+  int result = 0;
+  sem_t s = *sem;
+
+  if (sem == NULL)
+    {
+      result = EINVAL;
+    }
+  else
+    {
+      DWORD milliseconds;
+
+      if (abstime == NULL)
+	{
+	  milliseconds = INFINITE;
+	}
+      else
+	{
+	  /* 
+	   * Calculate timeout as milliseconds from current system time. 
+	   */
+	  milliseconds = ptw32_relmillisecs (abstime);
+	}
+
+      pthread_testcancel();
+
+      if ((result = pthread_mutex_lock (&s->lock)) == 0)
+	{
+	  int v = --s->value;
+	  (void) pthread_mutex_unlock (&s->lock);
+
+	  if (v < 0)
+	    {
+#ifdef NEED_SEM
+	      int timedout;
+#endif
+	      sem_timedwait_cleanup_args_t cleanup_args;
+
+	      cleanup_args.sem = s;
+	      cleanup_args.resultPtr = &result;
+
+#ifdef _MSC_VER
+#pragma inline_depth(0)
+#endif
+	      /* Must wait */
+              pthread_cleanup_push(ptw32_sem_timedwait_cleanup, (void *) &cleanup_args);
+#ifdef NEED_SEM
+	      timedout =
+#endif
+	      result = pthreadCancelableTimedWait (s->sem, milliseconds);
+	      pthread_cleanup_pop(result);
+#ifdef _MSC_VER
+#pragma inline_depth()
+#endif
+
+#ifdef NEED_SEM
+
+	      if (!timedout && pthread_mutex_lock (&s->lock) == 0)
+	        {
+	          if (s->leftToUnblock > 0)
+	            {
+		      --s->leftToUnblock;
+		      SetEvent(s->sem);
+		    }
+	          (void) pthread_mutex_unlock (&s->lock);
+	        }
+
+#endif /* NEED_SEM */
+
+	    }
+	}
+
+    }
+
+  if (result != 0)
+    {
+
+      errno = result;
+      return -1;
+
+    }
+
+  return 0;
+
+}				/* sem_timedwait */
-- 
cgit v1.2.3