]> granicus.if.org Git - postgresql/commitdiff
Introduce timed waits for condition variables.
authorThomas Munro <tmunro@postgresql.org>
Sat, 13 Jul 2019 01:40:36 +0000 (13:40 +1200)
committerThomas Munro <tmunro@postgresql.org>
Sat, 13 Jul 2019 01:51:05 +0000 (13:51 +1200)
Provide ConditionVariableTimedSleep(), like ConditionVariableSleep()
but with a timeout argument.

Author: Shawn Debnath
Reviewed-by: Kyotaro Horiguchi, Thomas Munro
Discussion: https://postgr.es/m/eeb06007ccfe46e399df6af18bfcd15a@EX13D05UWC002.ant.amazon.com

src/backend/storage/lmgr/condition_variable.c
src/include/storage/condition_variable.h

index 58b7b514720dfc7dec055d38abc62a7363f77cc7..bc8607efe457b980c4d125e84dabd92eb978ca95 100644 (file)
@@ -19,6 +19,7 @@
 #include "postgres.h"
 
 #include "miscadmin.h"
+#include "portability/instr_time.h"
 #include "storage/condition_variable.h"
 #include "storage/ipc.h"
 #include "storage/proc.h"
@@ -122,8 +123,24 @@ ConditionVariablePrepareToSleep(ConditionVariable *cv)
 void
 ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
 {
-       WaitEvent       event;
-       bool            done = false;
+       (void) ConditionVariableTimedSleep(cv, -1 /* no timeout */ ,
+                                                                          wait_event_info);
+}
+
+/*
+ * Wait for a condition variable to be signaled or a timeout to be reached.
+ *
+ * Returns true when timeout expires, otherwise returns false.
+ *
+ * See ConditionVariableSleep() for general usage.
+ */
+bool
+ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
+                                                       uint32 wait_event_info)
+{
+       long            cur_timeout = -1;
+       instr_time      start_time;
+       instr_time      cur_time;
 
        /*
         * If the caller didn't prepare to sleep explicitly, then do so now and
@@ -143,23 +160,37 @@ ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
        if (cv_sleep_target != cv)
        {
                ConditionVariablePrepareToSleep(cv);
-               return;
+               return false;
        }
 
-       do
+       /*
+        * Record the current time so that we can calculate the remaining timeout
+        * if we are woken up spuriously.
+        */
+       if (timeout >= 0)
        {
-               CHECK_FOR_INTERRUPTS();
+               INSTR_TIME_SET_CURRENT(start_time);
+               Assert(timeout >= 0 && timeout <= INT_MAX);
+               cur_timeout = timeout;
+       }
+
+       while (true)
+       {
+               WaitEvent       event;
+               bool            done = false;
 
                /*
                 * Wait for latch to be set.  (If we're awakened for some other
                 * reason, the code below will cope anyway.)
                 */
-               (void) WaitEventSetWait(cv_wait_event_set, -1, &event, 1,
+               (void) WaitEventSetWait(cv_wait_event_set, cur_timeout, &event, 1,
                                                                wait_event_info);
 
                /* Reset latch before examining the state of the wait list. */
                ResetLatch(MyLatch);
 
+               CHECK_FOR_INTERRUPTS();
+
                /*
                 * If this process has been taken out of the wait list, then we know
                 * that it has been signaled by ConditionVariableSignal (or
@@ -182,7 +213,23 @@ ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
                        proclist_push_tail(&cv->wakeup, MyProc->pgprocno, cvWaitLink);
                }
                SpinLockRelease(&cv->mutex);
-       } while (!done);
+
+               /* We were signaled, so return */
+               if (done)
+                       return false;
+
+               /* If we're not done, update cur_timeout for next iteration */
+               if (timeout >= 0)
+               {
+                       INSTR_TIME_SET_CURRENT(cur_time);
+                       INSTR_TIME_SUBTRACT(cur_time, start_time);
+                       cur_timeout = timeout - (long) INSTR_TIME_GET_MILLISEC(cur_time);
+
+                       /* Have we crossed the timeout threshold? */
+                       if (cur_timeout <= 0)
+                               return true;
+               }
+       }
 }
 
 /*
index 2a0249392ccc98dbc9bbea21726346248a6d8c78..ee06e051ce1246ec8511d5bda433327223613622 100644 (file)
@@ -43,6 +43,8 @@ extern void ConditionVariableInit(ConditionVariable *cv);
  * the condition variable.
  */
 extern void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info);
+extern bool ConditionVariableTimedSleep(ConditionVariable *cv, long timeout,
+                                                                               uint32 wait_event_info);
 extern void ConditionVariableCancelSleep(void);
 
 /*