Re: SELECT ... FOR UPDATE [WAIT integer | NOWAIT] for 8.5 - Mailing list pgsql-hackers

From Boszormenyi Zoltan
Subject Re: SELECT ... FOR UPDATE [WAIT integer | NOWAIT] for 8.5
Date
Msg-id 4A719663.6060209@cybertec.at
Whole thread Raw
In response to Re: SELECT ... FOR UPDATE [WAIT integer | NOWAIT] for 8.5  (Alvaro Herrera <alvherre@commandprompt.com>)
Responses Re: SELECT ... FOR UPDATE [WAIT integer | NOWAIT] for 8.5
List pgsql-hackers
Alvaro Herrera írta:
> Boszormenyi Zoltan wrote:
>
>
>> The vague consensus for syntax options was that the GUC
>> 'lock_timeout' and WAIT [N] extension (wherever NOWAIT
>> is allowed) both should be implemented.
>>
>> Behaviour would be that N seconds timeout should be
>> applied to every lock that the statement would take.
>>
>
> In http://archives.postgresql.org/message-id/291.1242053201@sss.pgh.pa.us
> Tom argues that lock_timeout should be sufficient.  I'm not sure what
> does WAIT [N] buy

Okay, we implemented only the lock_timeout GUC.
Patch attached, hopefully in an acceptable form.
Documentation included in the patch, lock_timeout
works the same way as statement_timeout, takes
value in milliseconds and 0 disables the timeout.

Best regards,
Zoltán Böszörményi

--
Bible has answers for everything. Proof:
"But let your communication be, Yea, yea; Nay, nay: for whatsoever is more
than these cometh of evil." (Matthew 5:37) - basics of digital technology.
"May your kingdom come" - superficial description of plate tectonics

----------------------------------
Zoltán Böszörményi
Cybertec Schönig & Schönig GmbH
http://www.postgresql.at/

diff -dcrpN pgsql.orig/doc/src/sgml/config.sgml pgsql/doc/src/sgml/config.sgml
*** pgsql.orig/doc/src/sgml/config.sgml    2009-07-17 07:50:48.000000000 +0200
--- pgsql/doc/src/sgml/config.sgml    2009-07-30 13:12:07.000000000 +0200
*************** COPY postgres_log FROM '/full/path/to/lo
*** 4018,4023 ****
--- 4018,4046 ----
        </listitem>
       </varlistentry>

+      <varlistentry id="guc-lock-timeout" xreflabel="lock_timeout">
+       <term><varname>lock_timeout</varname> (<type>integer</type>)</term>
+       <indexterm>
+        <primary><varname>lock_timeout</> configuration parameter</primary>
+       </indexterm>
+       <listitem>
+        <para>
+         Abort any statement that tries to lock any rows or tables and the lock
+         has to wait more than the specified number of milliseconds, starting
+         from the time the command arrives at the server from the client.
+         If <varname>log_min_error_statement</> is set to <literal>ERROR</> or
+         lower, the statement that timed out will also be logged.
+         A value of zero (the default) turns off the limitation.
+        </para>
+
+        <para>
+         Setting <varname>lock_timeout</> in
+         <filename>postgresql.conf</> is not recommended because it
+         affects all sessions.
+        </para>
+       </listitem>
+      </varlistentry>
+
       <varlistentry id="guc-vacuum-freeze-table-age" xreflabel="vacuum_freeze_table_age">
        <term><varname>vacuum_freeze_table_age</varname> (<type>integer</type>)</term>
        <indexterm>
diff -dcrpN pgsql.orig/doc/src/sgml/ref/lock.sgml pgsql/doc/src/sgml/ref/lock.sgml
*** pgsql.orig/doc/src/sgml/ref/lock.sgml    2009-01-16 11:44:56.000000000 +0100
--- pgsql/doc/src/sgml/ref/lock.sgml    2009-07-30 13:29:07.000000000 +0200
*************** where <replaceable class="PARAMETER">loc
*** 39,46 ****
     <literal>NOWAIT</literal> is specified, <command>LOCK
     TABLE</command> does not wait to acquire the desired lock: if it
     cannot be acquired immediately, the command is aborted and an
!    error is emitted.  Once obtained, the lock is held for the
!    remainder of the current transaction.  (There is no <command>UNLOCK
     TABLE</command> command; locks are always released at transaction
     end.)
    </para>
--- 39,49 ----
     <literal>NOWAIT</literal> is specified, <command>LOCK
     TABLE</command> does not wait to acquire the desired lock: if it
     cannot be acquired immediately, the command is aborted and an
!    error is emitted. If <varname>lock_timeout</varname> is set to a value
!    higher than 0, and the lock cannot be acquired under the specified
!    timeout value in milliseconds, the command is aborted and an error
!    is emitted. Once obtained, the lock is held for the remainder of
!    the current transaction.  (There is no <command>UNLOCK
     TABLE</command> command; locks are always released at transaction
     end.)
    </para>
diff -dcrpN pgsql.orig/doc/src/sgml/ref/select.sgml pgsql/doc/src/sgml/ref/select.sgml
*** pgsql.orig/doc/src/sgml/ref/select.sgml    2009-05-04 11:00:49.000000000 +0200
--- pgsql/doc/src/sgml/ref/select.sgml    2009-07-30 13:36:57.000000000 +0200
*************** FOR SHARE [ OF <replaceable class="param
*** 1101,1106 ****
--- 1101,1114 ----
     </para>

     <para>
+     If <literal>NOWAIT</> option is not specified and <varname>lock_timeout</varname>
+     is set to a value higher than 0, and the lock needs to wait more than
+     the specified value in milliseconds, the command reports an error after
+     timing out, rather than waiting indefinitely. The note in the previous
+     paragraph applies to the <varname>lock_timeout</varname>, too.
+    </para>
+
+    <para>
      <literal>FOR SHARE</literal> behaves similarly, except that it
      acquires a shared rather than exclusive lock on each retrieved
      row.  A shared lock blocks other transactions from performing
diff -dcrpN pgsql.orig/src/backend/access/heap/heapam.c pgsql/src/backend/access/heap/heapam.c
*** pgsql.orig/src/backend/access/heap/heapam.c    2009-06-13 18:24:46.000000000 +0200
--- pgsql/src/backend/access/heap/heapam.c    2009-07-30 12:29:17.000000000 +0200
*************** l3:
*** 3142,3157 ****
           */
          if (!have_tuple_lock)
          {
              if (nowait)
!             {
!                 if (!ConditionalLockTuple(relation, tid, tuple_lock_type))
!                     ereport(ERROR,
                              (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                      errmsg("could not obtain lock on row in relation \"%s\"",
                             RelationGetRelationName(relation))));
!             }
!             else
!                 LockTuple(relation, tid, tuple_lock_type);
              have_tuple_lock = true;
          }

--- 3142,3160 ----
           */
          if (!have_tuple_lock)
          {
+             bool    result;
+
              if (nowait)
!                 result = ConditionalLockTuple(relation, tid, tuple_lock_type);
!             else
!                 result = TimedLockTuple(relation, tid, tuple_lock_type);
!
!             if (!result)
!                 ereport(ERROR,
                              (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                      errmsg("could not obtain lock on row in relation \"%s\"",
                             RelationGetRelationName(relation))));
!
              have_tuple_lock = true;
          }

*************** l3:
*** 3172,3188 ****
          }
          else if (infomask & HEAP_XMAX_IS_MULTI)
          {
              /* wait for multixact to end */
              if (nowait)
!             {
!                 if (!ConditionalMultiXactIdWait((MultiXactId) xwait))
!                     ereport(ERROR,
                              (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                      errmsg("could not obtain lock on row in relation \"%s\"",
                             RelationGetRelationName(relation))));
-             }
-             else
-                 MultiXactIdWait((MultiXactId) xwait);

              LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

--- 3175,3191 ----
          }
          else if (infomask & HEAP_XMAX_IS_MULTI)
          {
+             bool    result;
              /* wait for multixact to end */
              if (nowait)
!                 result = ConditionalMultiXactIdWait((MultiXactId) xwait);
!             else
!                 result = TimedMultiXactIdWait((MultiXactId) xwait);
!             if (!result)
!                 ereport(ERROR,
                              (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                      errmsg("could not obtain lock on row in relation \"%s\"",
                             RelationGetRelationName(relation))));

              LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

*************** l3:
*** 3207,3223 ****
          }
          else
          {
              /* wait for regular transaction to end */
              if (nowait)
!             {
!                 if (!ConditionalXactLockTableWait(xwait))
!                     ereport(ERROR,
                              (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                      errmsg("could not obtain lock on row in relation \"%s\"",
                             RelationGetRelationName(relation))));
-             }
-             else
-                 XactLockTableWait(xwait);

              LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

--- 3210,3226 ----
          }
          else
          {
+             bool    result;
              /* wait for regular transaction to end */
              if (nowait)
!                 result = ConditionalXactLockTableWait(xwait);
!             else
!                 result = TimedXactLockTableWait(xwait);
!             if (!result)
!                 ereport(ERROR,
                              (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                      errmsg("could not obtain lock on row in relation \"%s\"",
                             RelationGetRelationName(relation))));

              LockBuffer(*buffer, BUFFER_LOCK_EXCLUSIVE);

diff -dcrpN pgsql.orig/src/backend/access/transam/multixact.c pgsql/src/backend/access/transam/multixact.c
*** pgsql.orig/src/backend/access/transam/multixact.c    2009-07-13 11:16:21.000000000 +0200
--- pgsql/src/backend/access/transam/multixact.c    2009-07-30 12:31:18.000000000 +0200
*************** ConditionalMultiXactIdWait(MultiXactId m
*** 636,641 ****
--- 636,678 ----
  }

  /*
+  * TimedMultiXactIdWait
+  *        As above, but only lock if we can get the lock under lock_timeout.
+  */
+ bool
+ TimedMultiXactIdWait(MultiXactId multi)
+ {
+     bool        result = true;
+     TransactionId *members;
+     int            nmembers;
+
+     nmembers = GetMultiXactIdMembers(multi, &members);
+
+     if (nmembers >= 0)
+     {
+         int            i;
+
+         for (i = 0; i < nmembers; i++)
+         {
+             TransactionId member = members[i];
+
+             debug_elog4(DEBUG2, "ConditionalMultiXactIdWait: trying %d (%u)",
+                         i, member);
+             if (!TransactionIdIsCurrentTransactionId(member))
+             {
+                 result = TimedXactLockTableWait(member);
+                 if (!result)
+                     break;
+             }
+         }
+
+         pfree(members);
+     }
+
+     return result;
+ }
+
+ /*
   * CreateMultiXactId
   *        Make a new MultiXactId
   *
diff -dcrpN pgsql.orig/src/backend/commands/lockcmds.c pgsql/src/backend/commands/lockcmds.c
*** pgsql.orig/src/backend/commands/lockcmds.c    2009-06-13 18:24:48.000000000 +0200
--- pgsql/src/backend/commands/lockcmds.c    2009-07-30 11:09:49.000000000 +0200
*************** LockTableRecurse(Oid reloid, RangeVar *r
*** 65,70 ****
--- 65,71 ----
  {
      Relation    rel;
      AclResult    aclresult;
+     bool        result;

      /*
       * Acquire the lock.  We must do this first to protect against concurrent
*************** LockTableRecurse(Oid reloid, RangeVar *r
*** 72,97 ****
       * won't fail.
       */
      if (nowait)
      {
!         if (!ConditionalLockRelationOid(reloid, lockmode))
!         {
!             /* try to throw error by name; relation could be deleted... */
!             char       *relname = rv ? rv->relname : get_rel_name(reloid);

!             if (relname)
!                 ereport(ERROR,
                          (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                           errmsg("could not obtain lock on relation \"%s\"",
                                  relname)));
!             else
!                 ereport(ERROR,
                          (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                        errmsg("could not obtain lock on relation with OID %u",
                               reloid)));
-         }
      }
-     else
-         LockRelationOid(reloid, lockmode);

      /*
       * Now that we have the lock, check to see if the relation really exists
--- 73,97 ----
       * won't fail.
       */
      if (nowait)
+         result = ConditionalLockRelationOid(reloid, lockmode);
+     else
+         result = TimedLockRelationOid(reloid, lockmode);
+     if (!result)
      {
!         /* try to throw error by name; relation could be deleted... */
!         char       *relname = rv ? rv->relname : get_rel_name(reloid);

!         if (relname)
!             ereport(ERROR,
                          (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                           errmsg("could not obtain lock on relation \"%s\"",
                                  relname)));
!         else
!             ereport(ERROR,
                          (errcode(ERRCODE_LOCK_NOT_AVAILABLE),
                        errmsg("could not obtain lock on relation with OID %u",
                               reloid)));
      }

      /*
       * Now that we have the lock, check to see if the relation really exists
diff -dcrpN pgsql.orig/src/backend/port/posix_sema.c pgsql/src/backend/port/posix_sema.c
*** pgsql.orig/src/backend/port/posix_sema.c    2009-06-13 18:24:55.000000000 +0200
--- pgsql/src/backend/port/posix_sema.c    2009-07-30 10:37:20.000000000 +0200
***************
*** 24,29 ****
--- 24,30 ----
  #include "miscadmin.h"
  #include "storage/ipc.h"
  #include "storage/pg_sema.h"
+ #include "storage/proc.h" /* for LockTimeout */


  #ifdef USE_NAMED_POSIX_SEMAPHORES
*************** PGSemaphoreTryLock(PGSemaphore sema)
*** 313,315 ****
--- 314,359 ----

      return true;
  }
+
+ /*
+  * PGSemaphoreTimedLock
+  *
+  * Lock a semaphore only if able to do so under the lock_timeout
+  */
+ bool
+ PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+ {
+     int            errStatus;
+     struct timespec timeout;
+
+
+     /*
+      * See notes in sysv_sema.c's implementation of PGSemaphoreLock. Just as
+      * that code does for semop(), we handle both the case where sem_wait()
+      * returns errno == EINTR after a signal, and the case where it just keeps
+      * waiting.
+      */
+     do
+     {
+         ImmediateInterruptOK = interruptOK;
+         CHECK_FOR_INTERRUPTS();
+         if (LockTimeout)
+         {
+             timeout.tv_sec = LockTimeout / 1000;
+             timeout.tv_nsec = (LockTimeout % 1000) * 1000000;
+             errStatus = sem_timedwait(PG_SEM_REF(sema), &timeout);
+         }
+         else
+             errStatus = sem_wait(PG_SEM_REF(sema));
+         ImmediateInterruptOK = false;
+     } while (errStatus < 0 && errno == EINTR);
+
+     if (errStatus < 0)
+     {
+         if (errno == ETIMEDOUT)
+             return false;        /* failed to lock it */
+         /* Otherwise we got trouble */
+         elog(FATAL, "sem_wait failed: %m");
+     }
+     return true;
+ }
diff -dcrpN pgsql.orig/src/backend/port/sysv_sema.c pgsql/src/backend/port/sysv_sema.c
*** pgsql.orig/src/backend/port/sysv_sema.c    2009-06-13 18:24:55.000000000 +0200
--- pgsql/src/backend/port/sysv_sema.c    2009-07-30 10:37:37.000000000 +0200
***************
*** 30,35 ****
--- 30,36 ----
  #include "miscadmin.h"
  #include "storage/ipc.h"
  #include "storage/pg_sema.h"
+ #include "storage/proc.h" /* for LockTimeout */


  #ifndef HAVE_UNION_SEMUN
*************** PGSemaphoreTryLock(PGSemaphore sema)
*** 497,499 ****
--- 498,590 ----

      return true;
  }
+
+ /*
+  * PGSemaphoreTimedLock
+  *
+  * Lock a semaphore only if able to do so under the lock_timeout
+  */
+ bool
+ PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+ {
+     int            errStatus;
+     struct sembuf sops;
+     struct timespec timeout;
+
+     sops.sem_op = -1;            /* decrement */
+     sops.sem_flg = 0;
+     sops.sem_num = sema->semNum;
+
+     /*
+      * Note: if errStatus is -1 and errno == EINTR then it means we returned
+      * from the operation prematurely because we were sent a signal.  So we
+      * try and lock the semaphore again.
+      *
+      * Each time around the loop, we check for a cancel/die interrupt.    On
+      * some platforms, if such an interrupt comes in while we are waiting, it
+      * will cause the semop() call to exit with errno == EINTR, allowing us to
+      * service the interrupt (if not in a critical section already) during the
+      * next loop iteration.
+      *
+      * Once we acquire the lock, we do NOT check for an interrupt before
+      * returning.  The caller needs to be able to record ownership of the lock
+      * before any interrupt can be accepted.
+      *
+      * There is a window of a few instructions between CHECK_FOR_INTERRUPTS
+      * and entering the semop() call.  If a cancel/die interrupt occurs in
+      * that window, we would fail to notice it until after we acquire the lock
+      * (or get another interrupt to escape the semop()).  We can avoid this
+      * problem by temporarily setting ImmediateInterruptOK to true before we
+      * do CHECK_FOR_INTERRUPTS; then, a die() interrupt in this interval will
+      * execute directly.  However, there is a huge pitfall: there is another
+      * window of a few instructions after the semop() before we are able to
+      * reset ImmediateInterruptOK.    If an interrupt occurs then, we'll lose
+      * control, which means that the lock has been acquired but our caller did
+      * not get a chance to record the fact. Therefore, we only set
+      * ImmediateInterruptOK if the caller tells us it's OK to do so, ie, the
+      * caller does not need to record acquiring the lock.  (This is currently
+      * true for lockmanager locks, since the process that granted us the lock
+      * did all the necessary state updates. It's not true for SysV semaphores
+      * used to implement LW locks or emulate spinlocks --- but the wait time
+      * for such locks should not be very long, anyway.)
+      *
+      * On some platforms, signals marked SA_RESTART (which is most, for us)
+      * will not interrupt the semop(); it will just keep waiting.  Therefore
+      * it's necessary for cancel/die interrupts to be serviced directly by the
+      * signal handler.    On these platforms the behavior is really the same
+      * whether the signal arrives just before the semop() begins, or while it
+      * is waiting.    The loop on EINTR is thus important only for other types
+      * of interrupts.
+      */
+     do
+     {
+         ImmediateInterruptOK = interruptOK;
+         CHECK_FOR_INTERRUPTS();
+         if (LockTimeout)
+         {
+             timeout.tv_sec = LockTimeout / 1000;
+             timeout.tv_nsec = (LockTimeout % 1000) * 1000000;
+             errStatus = semtimedop(sema->semId, &sops, 1, &timeout);
+         }
+         else
+             errStatus = semop(sema->semId, &sops, 1);
+         ImmediateInterruptOK = false;
+     } while (errStatus < 0 && errno == EINTR);
+
+     if (errStatus < 0)
+     {
+         /* Expect EAGAIN or EWOULDBLOCK (platform-dependent) */
+ #ifdef EAGAIN
+         if (errno == EAGAIN)
+             return false;        /* failed to lock it */
+ #endif
+ #if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
+         if (errno == EWOULDBLOCK)
+             return false;        /* failed to lock it */
+ #endif
+         /* Otherwise we got trouble */
+         elog(FATAL, "semop(id=%d) failed: %m", sema->semId);
+     }
+     return true;
+ }
+
diff -dcrpN pgsql.orig/src/backend/port/win32_sema.c pgsql/src/backend/port/win32_sema.c
*** pgsql.orig/src/backend/port/win32_sema.c    2009-06-13 18:24:55.000000000 +0200
--- pgsql/src/backend/port/win32_sema.c    2009-07-30 10:37:57.000000000 +0200
***************
*** 16,21 ****
--- 16,22 ----
  #include "miscadmin.h"
  #include "storage/ipc.h"
  #include "storage/pg_sema.h"
+ #include "storage/proc.h" /* for LockTimeout */

  static HANDLE *mySemSet;        /* IDs of sema sets acquired so far */
  static int    numSems;            /* number of sema sets acquired so far */
*************** PGSemaphoreTryLock(PGSemaphore sema)
*** 205,207 ****
--- 206,267 ----
      /* keep compiler quiet */
      return false;
  }
+
+ /*
+  * PGSemaphoreTimedLock
+  *
+  * Lock a semaphore only if able to do so under the lock_timeout
+  * Serve the interrupt if interruptOK is true.
+  */
+ bool
+ PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK)
+ {
+     DWORD        ret;
+     HANDLE        wh[2];
+
+     wh[0] = *sema;
+     wh[1] = pgwin32_signal_event;
+
+     /*
+      * As in other implementations of PGSemaphoreLock, we need to check for
+      * cancel/die interrupts each time through the loop.  But here, there is
+      * no hidden magic about whether the syscall will internally service a
+      * signal --- we do that ourselves.
+      */
+     do
+     {
+         ImmediateInterruptOK = interruptOK;
+         CHECK_FOR_INTERRUPTS();
+
+         errno = 0;
+         ret = WaitForMultipleObjectsEx(2, wh, FALSE, LockTimeout ? LockTimeout : INFINITE, TRUE);
+
+         if (ret == WAIT_OBJECT_0)
+         {
+             /* We got it! */
+             return true;
+         }
+         else if (ret == WAIT_TIMEOUT)
+         {
+             /* Can't get it */
+             errno = EAGAIN;
+             return false;
+         }
+         else if (ret == WAIT_OBJECT_0 + 1)
+         {
+             /* Signal event is set - we have a signal to deliver */
+             pgwin32_dispatch_queued_signals();
+             errno = EINTR;
+         }
+         else
+             /* Otherwise we are in trouble */
+             errno = EIDRM;
+
+         ImmediateInterruptOK = false;
+     } while (errno == EINTR);
+
+     if (errno != 0)
+         ereport(FATAL,
+                 (errmsg("could not lock semaphore: error code %d", (int) GetLastError())));
+ }
+
diff -dcrpN pgsql.orig/src/backend/storage/lmgr/lmgr.c pgsql/src/backend/storage/lmgr/lmgr.c
*** pgsql.orig/src/backend/storage/lmgr/lmgr.c    2009-01-02 17:15:28.000000000 +0100
--- pgsql/src/backend/storage/lmgr/lmgr.c    2009-07-30 12:28:44.000000000 +0200
***************
*** 21,26 ****
--- 21,27 ----
  #include "catalog/catalog.h"
  #include "miscadmin.h"
  #include "storage/lmgr.h"
+ #include "storage/proc.h"
  #include "storage/procarray.h"
  #include "utils/inval.h"

*************** LockRelationOid(Oid relid, LOCKMODE lock
*** 76,82 ****

      SetLocktagRelationOid(&tag, relid);

!     res = LockAcquire(&tag, lockmode, false, false);

      /*
       * Now that we have the lock, check for invalidation messages, so that we
--- 77,83 ----

      SetLocktagRelationOid(&tag, relid);

!     res = LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);

      /*
       * Now that we have the lock, check for invalidation messages, so that we
*************** ConditionalLockRelationOid(Oid relid, LO
*** 108,114 ****

      SetLocktagRelationOid(&tag, relid);

!     res = LockAcquire(&tag, lockmode, false, true);

      if (res == LOCKACQUIRE_NOT_AVAIL)
          return false;
--- 109,144 ----

      SetLocktagRelationOid(&tag, relid);

!     res = LockAcquire(&tag, lockmode, false, true, INFINITE_TIMEOUT);
!
!     if (res == LOCKACQUIRE_NOT_AVAIL)
!         return false;
!
!     /*
!      * Now that we have the lock, check for invalidation messages; see notes
!      * in LockRelationOid.
!      */
!     if (res != LOCKACQUIRE_ALREADY_HELD)
!         AcceptInvalidationMessages();
!
!     return true;
! }
!
! /*
!  *        LockTimeoutRelationOid
!  *
!  * As LockRelationOid, but only lock if we can under lock_timeout.
!  * Returns TRUE iff the lock was acquired.
!  */
! bool
! TimedLockRelationOid(Oid relid, LOCKMODE lockmode)
! {
!     LOCKTAG        tag;
!     LockAcquireResult res;
!
!     SetLocktagRelationOid(&tag, relid);
!
!     res = LockAcquire(&tag, lockmode, false, false, LockTimeout);

      if (res == LOCKACQUIRE_NOT_AVAIL)
          return false;
*************** LockRelation(Relation relation, LOCKMODE
*** 171,177 ****
                           relation->rd_lockInfo.lockRelId.dbId,
                           relation->rd_lockInfo.lockRelId.relId);

!     res = LockAcquire(&tag, lockmode, false, false);

      /*
       * Now that we have the lock, check for invalidation messages; see notes
--- 201,207 ----
                           relation->rd_lockInfo.lockRelId.dbId,
                           relation->rd_lockInfo.lockRelId.relId);

!     res = LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);

      /*
       * Now that we have the lock, check for invalidation messages; see notes
*************** ConditionalLockRelation(Relation relatio
*** 198,204 ****
                           relation->rd_lockInfo.lockRelId.dbId,
                           relation->rd_lockInfo.lockRelId.relId);

!     res = LockAcquire(&tag, lockmode, false, true);

      if (res == LOCKACQUIRE_NOT_AVAIL)
          return false;
--- 228,234 ----
                           relation->rd_lockInfo.lockRelId.dbId,
                           relation->rd_lockInfo.lockRelId.relId);

!     res = LockAcquire(&tag, lockmode, false, true, INFINITE_TIMEOUT);

      if (res == LOCKACQUIRE_NOT_AVAIL)
          return false;
*************** LockRelationIdForSession(LockRelId *reli
*** 250,256 ****

      SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);

!     (void) LockAcquire(&tag, lockmode, true, false);
  }

  /*
--- 280,286 ----

      SET_LOCKTAG_RELATION(tag, relid->dbId, relid->relId);

!     (void) LockAcquire(&tag, lockmode, true, false, INFINITE_TIMEOUT);
  }

  /*
*************** LockRelationForExtension(Relation relati
*** 285,291 ****
                                  relation->rd_lockInfo.lockRelId.dbId,
                                  relation->rd_lockInfo.lockRelId.relId);

!     (void) LockAcquire(&tag, lockmode, false, false);
  }

  /*
--- 315,321 ----
                                  relation->rd_lockInfo.lockRelId.dbId,
                                  relation->rd_lockInfo.lockRelId.relId);

!     (void) LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);
  }

  /*
*************** LockPage(Relation relation, BlockNumber
*** 319,325 ****
                       relation->rd_lockInfo.lockRelId.relId,
                       blkno);

!     (void) LockAcquire(&tag, lockmode, false, false);
  }

  /*
--- 349,355 ----
                       relation->rd_lockInfo.lockRelId.relId,
                       blkno);

!     (void) LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);
  }

  /*
*************** ConditionalLockPage(Relation relation, B
*** 338,344 ****
                       relation->rd_lockInfo.lockRelId.relId,
                       blkno);

!     return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
  }

  /*
--- 368,374 ----
                       relation->rd_lockInfo.lockRelId.relId,
                       blkno);

!     return (LockAcquire(&tag, lockmode, false, true, INFINITE_TIMEOUT) != LOCKACQUIRE_NOT_AVAIL);
  }

  /*
*************** LockTuple(Relation relation, ItemPointer
*** 375,381 ****
                        ItemPointerGetBlockNumber(tid),
                        ItemPointerGetOffsetNumber(tid));

!     (void) LockAcquire(&tag, lockmode, false, false);
  }

  /*
--- 405,411 ----
                        ItemPointerGetBlockNumber(tid),
                        ItemPointerGetOffsetNumber(tid));

!     (void) LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);
  }

  /*
*************** ConditionalLockTuple(Relation relation,
*** 395,401 ****
                        ItemPointerGetBlockNumber(tid),
                        ItemPointerGetOffsetNumber(tid));

!     return (LockAcquire(&tag, lockmode, false, true) != LOCKACQUIRE_NOT_AVAIL);
  }

  /*
--- 425,451 ----
                        ItemPointerGetBlockNumber(tid),
                        ItemPointerGetOffsetNumber(tid));

!     return (LockAcquire(&tag, lockmode, false, true, INFINITE_TIMEOUT) != LOCKACQUIRE_NOT_AVAIL);
! }
!
! /*
!  *        TimedLockTuple
!  *
!  * As above, but only lock if we can get the lock under lock_timeout.
!  * Returns TRUE iff the lock was acquired.
!  */
! bool
! TimedLockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode)
! {
!     LOCKTAG        tag;
!
!     SET_LOCKTAG_TUPLE(tag,
!                       relation->rd_lockInfo.lockRelId.dbId,
!                       relation->rd_lockInfo.lockRelId.relId,
!                       ItemPointerGetBlockNumber(tid),
!                       ItemPointerGetOffsetNumber(tid));
!
!     return (LockAcquire(&tag, lockmode, false, false, LockTimeout) != LOCKACQUIRE_NOT_AVAIL);
  }

  /*
*************** XactLockTableInsert(TransactionId xid)
*** 429,435 ****

      SET_LOCKTAG_TRANSACTION(tag, xid);

!     (void) LockAcquire(&tag, ExclusiveLock, false, false);
  }

  /*
--- 479,485 ----

      SET_LOCKTAG_TRANSACTION(tag, xid);

!     (void) LockAcquire(&tag, ExclusiveLock, false, false, INFINITE_TIMEOUT);
  }

  /*
*************** XactLockTableWait(TransactionId xid)
*** 473,479 ****

          SET_LOCKTAG_TRANSACTION(tag, xid);

!         (void) LockAcquire(&tag, ShareLock, false, false);

          LockRelease(&tag, ShareLock, false);

--- 523,529 ----

          SET_LOCKTAG_TRANSACTION(tag, xid);

!         (void) LockAcquire(&tag, ShareLock, false, false, INFINITE_TIMEOUT);

          LockRelease(&tag, ShareLock, false);

*************** ConditionalXactLockTableWait(Transaction
*** 501,507 ****

          SET_LOCKTAG_TRANSACTION(tag, xid);

!         if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
              return false;

          LockRelease(&tag, ShareLock, false);
--- 551,589 ----

          SET_LOCKTAG_TRANSACTION(tag, xid);

!         if (LockAcquire(&tag, ShareLock, false, true, INFINITE_TIMEOUT) == LOCKACQUIRE_NOT_AVAIL)
!             return false;
!
!         LockRelease(&tag, ShareLock, false);
!
!         if (!TransactionIdIsInProgress(xid))
!             break;
!         xid = SubTransGetParent(xid);
!     }
!
!     return true;
! }
!
!
! /*
!  *        TimedXactLockTableWait
!  *
!  * As above, but only lock if we can get the lock under lock_timeout.
!  * Returns TRUE if the lock was acquired.
!  */
! bool
! TimedXactLockTableWait(TransactionId xid)
! {
!     LOCKTAG        tag;
!
!     for (;;)
!     {
!         Assert(TransactionIdIsValid(xid));
!         Assert(!TransactionIdEquals(xid, GetTopTransactionIdIfAny()));
!
!         SET_LOCKTAG_TRANSACTION(tag, xid);
!
!         if (LockAcquire(&tag, ShareLock, false, false, LockTimeout) == LOCKACQUIRE_NOT_AVAIL)
              return false;

          LockRelease(&tag, ShareLock, false);
*************** VirtualXactLockTableInsert(VirtualTransa
*** 531,537 ****

      SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

!     (void) LockAcquire(&tag, ExclusiveLock, false, false);
  }

  /*
--- 613,619 ----

      SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

!     (void) LockAcquire(&tag, ExclusiveLock, false, false, INFINITE_TIMEOUT);
  }

  /*
*************** VirtualXactLockTableWait(VirtualTransact
*** 549,555 ****

      SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

!     (void) LockAcquire(&tag, ShareLock, false, false);

      LockRelease(&tag, ShareLock, false);
  }
--- 631,637 ----

      SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

!     (void) LockAcquire(&tag, ShareLock, false, false, INFINITE_TIMEOUT);

      LockRelease(&tag, ShareLock, false);
  }
*************** ConditionalVirtualXactLockTableWait(Virt
*** 569,575 ****

      SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

!     if (LockAcquire(&tag, ShareLock, false, true) == LOCKACQUIRE_NOT_AVAIL)
          return false;

      LockRelease(&tag, ShareLock, false);
--- 651,657 ----

      SET_LOCKTAG_VIRTUALTRANSACTION(tag, vxid);

!     if (LockAcquire(&tag, ShareLock, false, true, INFINITE_TIMEOUT) == LOCKACQUIRE_NOT_AVAIL)
          return false;

      LockRelease(&tag, ShareLock, false);
*************** LockDatabaseObject(Oid classid, Oid obji
*** 598,604 ****
                         objid,
                         objsubid);

!     (void) LockAcquire(&tag, lockmode, false, false);
  }

  /*
--- 680,686 ----
                         objid,
                         objsubid);

!     (void) LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);
  }

  /*
*************** LockSharedObject(Oid classid, Oid objid,
*** 636,642 ****
                         objid,
                         objsubid);

!     (void) LockAcquire(&tag, lockmode, false, false);

      /* Make sure syscaches are up-to-date with any changes we waited for */
      AcceptInvalidationMessages();
--- 718,724 ----
                         objid,
                         objsubid);

!     (void) LockAcquire(&tag, lockmode, false, false, INFINITE_TIMEOUT);

      /* Make sure syscaches are up-to-date with any changes we waited for */
      AcceptInvalidationMessages();
*************** LockSharedObjectForSession(Oid classid,
*** 678,684 ****
                         objid,
                         objsubid);

!     (void) LockAcquire(&tag, lockmode, true, false);
  }

  /*
--- 760,766 ----
                         objid,
                         objsubid);

!     (void) LockAcquire(&tag, lockmode, true, false, INFINITE_TIMEOUT);
  }

  /*
diff -dcrpN pgsql.orig/src/backend/storage/lmgr/lock.c pgsql/src/backend/storage/lmgr/lock.c
*** pgsql.orig/src/backend/storage/lmgr/lock.c    2009-06-13 18:24:57.000000000 +0200
--- pgsql/src/backend/storage/lmgr/lock.c    2009-07-30 14:32:24.000000000 +0200
*************** PROCLOCK_PRINT(const char *where, const
*** 254,260 ****
  static uint32 proclock_hash(const void *key, Size keysize);
  static void RemoveLocalLock(LOCALLOCK *locallock);
  static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
! static void WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner);
  static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
              PROCLOCK *proclock, LockMethod lockMethodTable);
  static void CleanUpLock(LOCK *lock, PROCLOCK *proclock,
--- 254,261 ----
  static uint32 proclock_hash(const void *key, Size keysize);
  static void RemoveLocalLock(LOCALLOCK *locallock);
  static void GrantLockLocal(LOCALLOCK *locallock, ResourceOwner owner);
! static int  WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner,
!             int lock_timeout);
  static bool UnGrantLock(LOCK *lock, LOCKMODE lockmode,
              PROCLOCK *proclock, LockMethod lockMethodTable);
  static void CleanUpLock(LOCK *lock, PROCLOCK *proclock,
*************** LockAcquireResult
*** 467,473 ****
  LockAcquire(const LOCKTAG *locktag,
              LOCKMODE lockmode,
              bool sessionLock,
!             bool dontWait)
  {
      LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid;
      LockMethod    lockMethodTable;
--- 468,475 ----
  LockAcquire(const LOCKTAG *locktag,
              LOCKMODE lockmode,
              bool sessionLock,
!             bool dontWait,
!             int lock_timeout)
  {
      LOCKMETHODID lockmethodid = locktag->locktag_lockmethodid;
      LockMethod    lockMethodTable;
*************** LockAcquire(const LOCKTAG *locktag,
*** 745,750 ****
--- 747,754 ----
      }
      else
      {
+         int    wait_result;
+
          Assert(status == STATUS_FOUND);

          /*
*************** LockAcquire(const LOCKTAG *locktag,
*** 794,800 ****
                                           locktag->locktag_type,
                                           lockmode);

!         WaitOnLock(locallock, owner);

          TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1,
                                          locktag->locktag_field2,
--- 798,804 ----
                                           locktag->locktag_type,
                                           lockmode);

!         wait_result = WaitOnLock(locallock, owner, lock_timeout);

          TRACE_POSTGRESQL_LOCK_WAIT_DONE(locktag->locktag_field1,
                                          locktag->locktag_field2,
*************** LockAcquire(const LOCKTAG *locktag,
*** 813,818 ****
--- 817,847 ----
           * Check the proclock entry status, in case something in the ipc
           * communication doesn't work correctly.
           */
+         if (wait_result == STATUS_WAITING)
+         {
+             if (proclock->holdMask == 0)
+             {
+                 SHMQueueDelete(&proclock->lockLink);
+                 SHMQueueDelete(&proclock->procLink);
+                 if (!hash_search_with_hash_value(LockMethodProcLockHash,
+                                                  (void *) &(proclock->tag),
+                                                  proclock_hashcode,
+                                                  HASH_REMOVE,
+                                                  NULL))
+                     elog(PANIC, "proclock table corrupted");
+             }
+             else
+                 PROCLOCK_PRINT("LockAcquire: TIMED OUT", proclock);
+             lock->nRequested--;
+             lock->requested[lockmode]--;
+             LOCK_PRINT("LockAcquire: TIMED OUT", lock, lockmode);
+             Assert((lock->nRequested > 0) && (lock->requested[lockmode] >= 0));
+             Assert(lock->nGranted <= lock->nRequested);
+             LWLockRelease(partitionLock);
+             if (locallock->nLocks == 0)
+                 RemoveLocalLock(locallock);
+             return LOCKACQUIRE_NOT_AVAIL;
+         }
          if (!(proclock->holdMask & LOCKBIT_ON(lockmode)))
          {
              PROCLOCK_PRINT("LockAcquire: INCONSISTENT", proclock);
*************** GrantAwaitedLock(void)
*** 1105,1118 ****
   * Caller must have set MyProc->heldLocks to reflect locks already held
   * on the lockable object by this process.
   *
   * The appropriate partition lock must be held at entry.
   */
! static void
! WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner)
  {
      LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
      LockMethod    lockMethodTable = LockMethods[lockmethodid];
      char       *volatile new_status = NULL;

      LOCK_PRINT("WaitOnLock: sleeping on lock",
                 locallock->lock, locallock->tag.mode);
--- 1134,1153 ----
   * Caller must have set MyProc->heldLocks to reflect locks already held
   * on the lockable object by this process.
   *
+  * Result: (returns value of ProcSleep())
+  *      STATUS_OK if we acquired the lock
+  *      STATUS_ERROR if not (deadlock)
+  *      STATUS_WAITING if not (timeout)
+  *
   * The appropriate partition lock must be held at entry.
   */
! static int
! WaitOnLock(LOCALLOCK *locallock, ResourceOwner owner, int lock_timeout)
  {
      LOCKMETHODID lockmethodid = LOCALLOCK_LOCKMETHOD(*locallock);
      LockMethod    lockMethodTable = LockMethods[lockmethodid];
      char       *volatile new_status = NULL;
+     int        wait_status;

      LOCK_PRINT("WaitOnLock: sleeping on lock",
                 locallock->lock, locallock->tag.mode);
*************** WaitOnLock(LOCALLOCK *locallock, Resourc
*** 1154,1161 ****
       */
      PG_TRY();
      {
!         if (ProcSleep(locallock, lockMethodTable) != STATUS_OK)
          {
              /*
               * We failed as a result of a deadlock, see CheckDeadLock(). Quit
               * now.
--- 1189,1208 ----
       */
      PG_TRY();
      {
!         wait_status = ProcSleep(locallock, lockMethodTable, lock_timeout);
!         switch (wait_status)
          {
+         case STATUS_OK:
+             /* Got lock */
+             break;
+         case STATUS_WAITING:
+             /*
+              * We failed as a result of a timeout. Quit now.
+              */
+             LOCK_PRINT("WaitOnLock: timeout on lock",
+                        locallock->lock, locallock->tag.mode);
+             break;
+         default:
              /*
               * We failed as a result of a deadlock, see CheckDeadLock(). Quit
               * now.
*************** WaitOnLock(LOCALLOCK *locallock, Resourc
*** 1202,1207 ****
--- 1249,1256 ----

      LOCK_PRINT("WaitOnLock: wakeup on lock",
                 locallock->lock, locallock->tag.mode);
+
+     return wait_status;
  }

  /*
diff -dcrpN pgsql.orig/src/backend/storage/lmgr/lwlock.c pgsql/src/backend/storage/lmgr/lwlock.c
*** pgsql.orig/src/backend/storage/lmgr/lwlock.c    2009-01-02 17:15:28.000000000 +0100
--- pgsql/src/backend/storage/lmgr/lwlock.c    2009-07-30 10:45:39.000000000 +0200
*************** LWLockConditionalAcquire(LWLockId lockid
*** 554,559 ****
--- 554,756 ----
  }

  /*
+  * LWLockTimedAcquire - acquire a lightweight lock in the specified mode
+  *
+  * If the lock is not available, sleep until it is or until lock_timeout
+  * whichever is sooner
+  *
+  * Side effect: cancel/die interrupts are held off until lock release.
+  */
+ bool
+ LWLockTimedAcquire(LWLockId lockid, LWLockMode mode)
+ {
+     volatile LWLock *lock = &(LWLockArray[lockid].lock);
+     PGPROC       *proc = MyProc;
+     bool        retry = false;
+     int            extraWaits = 0;
+     bool        timeout;
+
+     PRINT_LWDEBUG("LWLockAcquire", lockid, lock);
+
+ #ifdef LWLOCK_STATS
+     /* Set up local count state first time through in a given process */
+     if (counts_for_pid != MyProcPid)
+     {
+         int           *LWLockCounter = (int *) ((char *) LWLockArray - 2 * sizeof(int));
+         int            numLocks = LWLockCounter[1];
+
+         sh_acquire_counts = calloc(numLocks, sizeof(int));
+         ex_acquire_counts = calloc(numLocks, sizeof(int));
+         block_counts = calloc(numLocks, sizeof(int));
+         counts_for_pid = MyProcPid;
+         on_shmem_exit(print_lwlock_stats, 0);
+     }
+     /* Count lock acquisition attempts */
+     if (mode == LW_EXCLUSIVE)
+         ex_acquire_counts[lockid]++;
+     else
+         sh_acquire_counts[lockid]++;
+ #endif   /* LWLOCK_STATS */
+
+     /*
+      * We can't wait if we haven't got a PGPROC.  This should only occur
+      * during bootstrap or shared memory initialization.  Put an Assert here
+      * to catch unsafe coding practices.
+      */
+     Assert(!(proc == NULL && IsUnderPostmaster));
+
+     /* Ensure we will have room to remember the lock */
+     if (num_held_lwlocks >= MAX_SIMUL_LWLOCKS)
+         elog(ERROR, "too many LWLocks taken");
+
+     /*
+      * Lock out cancel/die interrupts until we exit the code section protected
+      * by the LWLock.  This ensures that interrupts will not interfere with
+      * manipulations of data structures in shared memory.
+      */
+     HOLD_INTERRUPTS();
+
+     /*
+      * Loop here to try to acquire lock after each time we are signaled by
+      * LWLockRelease.
+      *
+      * NOTE: it might seem better to have LWLockRelease actually grant us the
+      * lock, rather than retrying and possibly having to go back to sleep. But
+      * in practice that is no good because it means a process swap for every
+      * lock acquisition when two or more processes are contending for the same
+      * lock.  Since LWLocks are normally used to protect not-very-long
+      * sections of computation, a process needs to be able to acquire and
+      * release the same lock many times during a single CPU time slice, even
+      * in the presence of contention.  The efficiency of being able to do that
+      * outweighs the inefficiency of sometimes wasting a process dispatch
+      * cycle because the lock is not free when a released waiter finally gets
+      * to run.    See pgsql-hackers archives for 29-Dec-01.
+      */
+     for (;;)
+     {
+         bool        mustwait;
+
+         /* Acquire mutex.  Time spent holding mutex should be short! */
+         SpinLockAcquire(&lock->mutex);
+
+         /* If retrying, allow LWLockRelease to release waiters again */
+         if (retry)
+             lock->releaseOK = true;
+
+         /* If I can get the lock, do so quickly. */
+         if (mode == LW_EXCLUSIVE)
+         {
+             if (lock->exclusive == 0 && lock->shared == 0)
+             {
+                 lock->exclusive++;
+                 mustwait = false;
+             }
+             else
+                 mustwait = true;
+         }
+         else
+         {
+             if (lock->exclusive == 0)
+             {
+                 lock->shared++;
+                 mustwait = false;
+             }
+             else
+                 mustwait = true;
+         }
+
+         if (!mustwait)
+             break;                /* got the lock */
+
+         /*
+          * Add myself to wait queue.
+          *
+          * If we don't have a PGPROC structure, there's no way to wait. This
+          * should never occur, since MyProc should only be null during shared
+          * memory initialization.
+          */
+         if (proc == NULL)
+             elog(PANIC, "cannot wait without a PGPROC structure");
+
+         proc->lwWaiting = true;
+         proc->lwExclusive = (mode == LW_EXCLUSIVE);
+         proc->lwWaitLink = NULL;
+         if (lock->head == NULL)
+             lock->head = proc;
+         else
+             lock->tail->lwWaitLink = proc;
+         lock->tail = proc;
+
+         /* Can release the mutex now */
+         SpinLockRelease(&lock->mutex);
+
+         /*
+          * Wait until awakened.
+          *
+          * Since we share the process wait semaphore with the regular lock
+          * manager and ProcWaitForSignal, and we may need to acquire an LWLock
+          * while one of those is pending, it is possible that we get awakened
+          * for a reason other than being signaled by LWLockRelease. If so,
+          * loop back and wait again.  Once we've gotten the LWLock,
+          * re-increment the sema by the number of additional signals received,
+          * so that the lock manager or signal manager will see the received
+          * signal when it next waits.
+          */
+         LOG_LWDEBUG("LWLockAcquire", lockid, "waiting");
+
+ #ifdef LWLOCK_STATS
+         block_counts[lockid]++;
+ #endif
+
+         TRACE_POSTGRESQL_LWLOCK_WAIT_START(lockid, mode);
+
+         for (;;)
+         {
+             /* "false" means cannot accept cancel/die interrupt here. */
+             timeout = !PGSemaphoreTimedLock(&proc->sem, false);
+             if (timeout)
+                 break;
+             if (!proc->lwWaiting)
+                 break;
+             extraWaits++;
+         }
+
+         TRACE_POSTGRESQL_LWLOCK_WAIT_DONE(lockid, mode);
+
+         if (timeout)
+         {
+             LOG_LWDEBUG("LWLockTimedAcquire", lockid, "timed out");
+             break;
+         }
+
+         LOG_LWDEBUG("LWLockTimedAcquire", lockid, "awakened");
+
+         /* Now loop back and try to acquire lock again. */
+         retry = true;
+     }
+
+     /* We are done updating shared state of the lock itself. */
+     SpinLockRelease(&lock->mutex);
+
+     if (timeout)
+         goto out;
+
+     TRACE_POSTGRESQL_LWLOCK_ACQUIRE(lockid, mode);
+
+     /* Add lock to list of locks held by this backend */
+     held_lwlocks[num_held_lwlocks++] = lockid;
+
+ out:
+     /*
+      * Fix the process wait semaphore's count for any absorbed wakeups.
+      */
+     while (extraWaits-- > 0)
+         PGSemaphoreUnlock(&proc->sem);
+
+     return !timeout;
+ }
+
+ /*
   * LWLockRelease - release a previously acquired lock
   */
  void
diff -dcrpN pgsql.orig/src/backend/storage/lmgr/proc.c pgsql/src/backend/storage/lmgr/proc.c
*** pgsql.orig/src/backend/storage/lmgr/proc.c    2009-06-13 18:24:57.000000000 +0200
--- pgsql/src/backend/storage/lmgr/proc.c    2009-07-30 11:35:54.000000000 +0200
***************
*** 50,55 ****
--- 50,56 ----
  /* GUC variables */
  int            DeadlockTimeout = 1000;
  int            StatementTimeout = 0;
+ int            LockTimeout = 0;
  bool        log_lock_waits = false;

  /* Pointer to this process's PGPROC struct, if any */
*************** ProcQueueInit(PROC_QUEUE *queue)
*** 717,723 ****
   * The lock table's partition lock must be held at entry, and will be held
   * at exit.
   *
!  * Result: STATUS_OK if we acquired the lock, STATUS_ERROR if not (deadlock).
   *
   * ASSUME: that no one will fiddle with the queue until after
   *        we release the partition lock.
--- 718,727 ----
   * The lock table's partition lock must be held at entry, and will be held
   * at exit.
   *
!  * Result:
!  *    STATUS_OK if we acquired the lock
!  *    STATUS_ERROR if not (deadlock)
!  *    STATUS_WAITING if not (timeout)
   *
   * ASSUME: that no one will fiddle with the queue until after
   *        we release the partition lock.
*************** ProcQueueInit(PROC_QUEUE *queue)
*** 728,734 ****
   * semaphore is normally zero, so when we try to acquire it, we sleep.
   */
  int
! ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable)
  {
      LOCKMODE    lockmode = locallock->tag.mode;
      LOCK       *lock = locallock->lock;
--- 732,738 ----
   * semaphore is normally zero, so when we try to acquire it, we sleep.
   */
  int
! ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, int lock_timeout)
  {
      LOCKMODE    lockmode = locallock->tag.mode;
      LOCK       *lock = locallock->lock;
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 889,895 ****
       */
      do
      {
!         PGSemaphoreLock(&MyProc->sem, true);

          /*
           * waitStatus could change from STATUS_WAITING to something else
--- 893,902 ----
       */
      do
      {
!         if (lock_timeout == INFINITE_TIMEOUT)
!             PGSemaphoreLock(&MyProc->sem, true);
!         else if (!PGSemaphoreTimedLock(&MyProc->sem, true))
!             break;

          /*
           * waitStatus could change from STATUS_WAITING to something else
*************** ProcSleep(LOCALLOCK *locallock, LockMeth
*** 906,912 ****
          {
              PGPROC       *autovac = GetBlockingAutoVacuumPgproc();

!             LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);

              /*
               * Only do it if the worker is not working to protect against Xid
--- 913,922 ----
          {
              PGPROC       *autovac = GetBlockingAutoVacuumPgproc();

!             if (lock_timeout == INFINITE_TIMEOUT)
!                 LWLockAcquire(ProcArrayLock, LW_EXCLUSIVE);
!             else if (!LWLockTimedAcquire(ProcArrayLock, LW_EXCLUSIVE))
!                 break;

              /*
               * Only do it if the worker is not working to protect against Xid
diff -dcrpN pgsql.orig/src/backend/utils/adt/lockfuncs.c pgsql/src/backend/utils/adt/lockfuncs.c
*** pgsql.orig/src/backend/utils/adt/lockfuncs.c    2009-01-02 17:15:30.000000000 +0100
--- pgsql/src/backend/utils/adt/lockfuncs.c    2009-07-30 11:40:19.000000000 +0200
*************** pg_advisory_lock_int8(PG_FUNCTION_ARGS)
*** 337,343 ****

      SET_LOCKTAG_INT64(tag, key);

!     (void) LockAcquire(&tag, ExclusiveLock, true, false);

      PG_RETURN_VOID();
  }
--- 337,343 ----

      SET_LOCKTAG_INT64(tag, key);

!     (void) LockAcquire(&tag, ExclusiveLock, true, false, INFINITE_TIMEOUT);

      PG_RETURN_VOID();
  }
*************** pg_advisory_lock_shared_int8(PG_FUNCTION
*** 353,359 ****

      SET_LOCKTAG_INT64(tag, key);

!     (void) LockAcquire(&tag, ShareLock, true, false);

      PG_RETURN_VOID();
  }
--- 353,359 ----

      SET_LOCKTAG_INT64(tag, key);

!     (void) LockAcquire(&tag, ShareLock, true, false, INFINITE_TIMEOUT);

      PG_RETURN_VOID();
  }
*************** pg_try_advisory_lock_int8(PG_FUNCTION_AR
*** 372,378 ****

      SET_LOCKTAG_INT64(tag, key);

!     res = LockAcquire(&tag, ExclusiveLock, true, true);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
--- 372,378 ----

      SET_LOCKTAG_INT64(tag, key);

!     res = LockAcquire(&tag, ExclusiveLock, true, true, INFINITE_TIMEOUT);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
*************** pg_try_advisory_lock_shared_int8(PG_FUNC
*** 391,397 ****

      SET_LOCKTAG_INT64(tag, key);

!     res = LockAcquire(&tag, ShareLock, true, true);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
--- 391,397 ----

      SET_LOCKTAG_INT64(tag, key);

!     res = LockAcquire(&tag, ShareLock, true, true, INFINITE_TIMEOUT);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
*************** pg_advisory_lock_int4(PG_FUNCTION_ARGS)
*** 446,452 ****

      SET_LOCKTAG_INT32(tag, key1, key2);

!     (void) LockAcquire(&tag, ExclusiveLock, true, false);

      PG_RETURN_VOID();
  }
--- 446,452 ----

      SET_LOCKTAG_INT32(tag, key1, key2);

!     (void) LockAcquire(&tag, ExclusiveLock, true, false, INFINITE_TIMEOUT);

      PG_RETURN_VOID();
  }
*************** pg_advisory_lock_shared_int4(PG_FUNCTION
*** 463,469 ****

      SET_LOCKTAG_INT32(tag, key1, key2);

!     (void) LockAcquire(&tag, ShareLock, true, false);

      PG_RETURN_VOID();
  }
--- 463,469 ----

      SET_LOCKTAG_INT32(tag, key1, key2);

!     (void) LockAcquire(&tag, ShareLock, true, false, INFINITE_TIMEOUT);

      PG_RETURN_VOID();
  }
*************** pg_try_advisory_lock_int4(PG_FUNCTION_AR
*** 483,489 ****

      SET_LOCKTAG_INT32(tag, key1, key2);

!     res = LockAcquire(&tag, ExclusiveLock, true, true);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
--- 483,489 ----

      SET_LOCKTAG_INT32(tag, key1, key2);

!     res = LockAcquire(&tag, ExclusiveLock, true, true, INFINITE_TIMEOUT);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
*************** pg_try_advisory_lock_shared_int4(PG_FUNC
*** 503,509 ****

      SET_LOCKTAG_INT32(tag, key1, key2);

!     res = LockAcquire(&tag, ShareLock, true, true);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
--- 503,509 ----

      SET_LOCKTAG_INT32(tag, key1, key2);

!     res = LockAcquire(&tag, ShareLock, true, true, INFINITE_TIMEOUT);

      PG_RETURN_BOOL(res != LOCKACQUIRE_NOT_AVAIL);
  }
diff -dcrpN pgsql.orig/src/backend/utils/misc/guc.c pgsql/src/backend/utils/misc/guc.c
*** pgsql.orig/src/backend/utils/misc/guc.c    2009-07-23 14:40:12.000000000 +0200
--- pgsql/src/backend/utils/misc/guc.c    2009-07-30 10:08:25.000000000 +0200
*************** static struct config_int ConfigureNamesI
*** 1539,1544 ****
--- 1539,1554 ----
      },

      {
+         {"lock_timeout", PGC_USERSET, CLIENT_CONN_STATEMENT,
+             gettext_noop("Sets the maximum allowed timeout for any lock taken by a statement."),
+             gettext_noop("A value of 0 turns off the timeout."),
+             GUC_UNIT_MS
+         },
+         &LockTimeout,
+         0, 0, INT_MAX, NULL, NULL
+     },
+
+     {
          {"vacuum_freeze_min_age", PGC_USERSET, CLIENT_CONN_STATEMENT,
              gettext_noop("Minimum age at which VACUUM should freeze a table row."),
              NULL
diff -dcrpN pgsql.orig/src/include/access/multixact.h pgsql/src/include/access/multixact.h
*** pgsql.orig/src/include/access/multixact.h    2009-01-02 17:15:37.000000000 +0100
--- pgsql/src/include/access/multixact.h    2009-07-30 12:25:55.000000000 +0200
*************** extern bool MultiXactIdIsRunning(MultiXa
*** 48,53 ****
--- 48,54 ----
  extern bool MultiXactIdIsCurrent(MultiXactId multi);
  extern void MultiXactIdWait(MultiXactId multi);
  extern bool ConditionalMultiXactIdWait(MultiXactId multi);
+ extern bool TimedMultiXactIdWait(MultiXactId multi);
  extern void MultiXactIdSetOldestMember(void);
  extern int    GetMultiXactIdMembers(MultiXactId multi, TransactionId **xids);

diff -dcrpN pgsql.orig/src/include/storage/lmgr.h pgsql/src/include/storage/lmgr.h
*** pgsql.orig/src/include/storage/lmgr.h    2009-06-13 18:25:05.000000000 +0200
--- pgsql/src/include/storage/lmgr.h    2009-07-30 12:25:19.000000000 +0200
*************** extern void RelationInitLockInfo(Relatio
*** 25,30 ****
--- 25,31 ----
  /* Lock a relation */
  extern void LockRelationOid(Oid relid, LOCKMODE lockmode);
  extern bool ConditionalLockRelationOid(Oid relid, LOCKMODE lockmode);
+ extern bool TimedLockRelationOid(Oid relid, LOCKMODE lockmode);
  extern void UnlockRelationId(LockRelId *relid, LOCKMODE lockmode);
  extern void UnlockRelationOid(Oid relid, LOCKMODE lockmode);

*************** extern void UnlockPage(Relation relation
*** 48,53 ****
--- 49,56 ----
  extern void LockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);
  extern bool ConditionalLockTuple(Relation relation, ItemPointer tid,
                       LOCKMODE lockmode);
+ extern bool TimedLockTuple(Relation relation, ItemPointer tid,
+                      LOCKMODE lockmode);
  extern void UnlockTuple(Relation relation, ItemPointer tid, LOCKMODE lockmode);

  /* Lock an XID (used to wait for a transaction to finish) */
*************** extern void XactLockTableInsert(Transact
*** 55,60 ****
--- 58,64 ----
  extern void XactLockTableDelete(TransactionId xid);
  extern void XactLockTableWait(TransactionId xid);
  extern bool ConditionalXactLockTableWait(TransactionId xid);
+ extern bool TimedXactLockTableWait(TransactionId xid);

  /* Lock a VXID (used to wait for a transaction to finish) */
  extern void VirtualXactLockTableInsert(VirtualTransactionId vxid);
diff -dcrpN pgsql.orig/src/include/storage/lock.h pgsql/src/include/storage/lock.h
*** pgsql.orig/src/include/storage/lock.h    2009-04-14 10:28:46.000000000 +0200
--- pgsql/src/include/storage/lock.h    2009-07-30 11:28:44.000000000 +0200
*************** extern uint32 LockTagHashCode(const LOCK
*** 476,482 ****
  extern LockAcquireResult LockAcquire(const LOCKTAG *locktag,
              LOCKMODE lockmode,
              bool sessionLock,
!             bool dontWait);
  extern bool LockRelease(const LOCKTAG *locktag,
              LOCKMODE lockmode, bool sessionLock);
  extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
--- 476,483 ----
  extern LockAcquireResult LockAcquire(const LOCKTAG *locktag,
              LOCKMODE lockmode,
              bool sessionLock,
!             bool dontWait,
!             int lock_timeout);
  extern bool LockRelease(const LOCKTAG *locktag,
              LOCKMODE lockmode, bool sessionLock);
  extern void LockReleaseAll(LOCKMETHODID lockmethodid, bool allLocks);
diff -dcrpN pgsql.orig/src/include/storage/lwlock.h pgsql/src/include/storage/lwlock.h
*** pgsql.orig/src/include/storage/lwlock.h    2009-03-04 10:27:30.000000000 +0100
--- pgsql/src/include/storage/lwlock.h    2009-07-30 11:36:44.000000000 +0200
*************** extern bool Trace_lwlocks;
*** 92,97 ****
--- 92,98 ----
  extern LWLockId LWLockAssign(void);
  extern void LWLockAcquire(LWLockId lockid, LWLockMode mode);
  extern bool LWLockConditionalAcquire(LWLockId lockid, LWLockMode mode);
+ extern bool LWLockTimedAcquire(LWLockId lockid, LWLockMode mode);
  extern void LWLockRelease(LWLockId lockid);
  extern void LWLockReleaseAll(void);
  extern bool LWLockHeldByMe(LWLockId lockid);
diff -dcrpN pgsql.orig/src/include/storage/pg_sema.h pgsql/src/include/storage/pg_sema.h
*** pgsql.orig/src/include/storage/pg_sema.h    2009-01-02 17:15:39.000000000 +0100
--- pgsql/src/include/storage/pg_sema.h    2009-07-30 10:36:19.000000000 +0200
*************** extern void PGSemaphoreUnlock(PGSemaphor
*** 80,83 ****
--- 80,86 ----
  /* Lock a semaphore only if able to do so without blocking */
  extern bool PGSemaphoreTryLock(PGSemaphore sema);

+ /* Lock a semaphore only if able to do so under the lock_timeout */
+ extern bool PGSemaphoreTimedLock(PGSemaphore sema, bool interruptOK);
+
  #endif   /* PG_SEMA_H */
diff -dcrpN pgsql.orig/src/include/storage/proc.h pgsql/src/include/storage/proc.h
*** pgsql.orig/src/include/storage/proc.h    2009-02-26 12:23:28.000000000 +0100
--- pgsql/src/include/storage/proc.h    2009-07-30 11:15:01.000000000 +0200
*************** typedef struct PROC_HDR
*** 146,155 ****
--- 146,158 ----
   */
  #define NUM_AUXILIARY_PROCS        3

+ /* For checking LockTimeout */
+ #define INFINITE_TIMEOUT        0

  /* configurable options */
  extern int    DeadlockTimeout;
  extern int    StatementTimeout;
+ extern int    LockTimeout;
  extern bool log_lock_waits;

  extern volatile bool cancel_from_timeout;
*************** extern bool HaveNFreeProcs(int n);
*** 168,174 ****
  extern void ProcReleaseLocks(bool isCommit);

  extern void ProcQueueInit(PROC_QUEUE *queue);
! extern int    ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable);
  extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
  extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
  extern void LockWaitCancel(void);
--- 171,177 ----
  extern void ProcReleaseLocks(bool isCommit);

  extern void ProcQueueInit(PROC_QUEUE *queue);
! extern int    ProcSleep(LOCALLOCK *locallock, LockMethod lockMethodTable, int lock_timeout);
  extern PGPROC *ProcWakeup(PGPROC *proc, int waitStatus);
  extern void ProcLockWakeup(LockMethod lockMethodTable, LOCK *lock);
  extern void LockWaitCancel(void);

pgsql-hackers by date:

Previous
From: Santosh Ahuja
Date:
Subject: Compiling Postgres 8.3.7 MSVC 2005
Next
From: Magnus Hagander
Date:
Subject: Re: Compiling Postgres 8.3.7 MSVC 2005