cms/drupal/includes/lock.inc
author ymh <ymh.work@gmail.com>
Fri, 17 Nov 2017 16:05:03 +0100
changeset 563 41e4eb4d8b82
parent 541 e756a8c72c3d
permissions -rwxr-xr-x
Correct Handle creation and correct permalink
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
541
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
<?php
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
 * @file
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
 * A database-mediated implementation of a locking mechanism.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
 * @defgroup lock Locking mechanisms
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
 * @{
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
 * Functions to coordinate long-running operations across requests.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
 * In most environments, multiple Drupal page requests (a.k.a. threads or
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
 * processes) will execute in parallel. This leads to potential conflicts or
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
 * race conditions when two requests execute the same code at the same time. A
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
 * common example of this is a rebuild like menu_rebuild() where we invoke many
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
 * hook implementations to get and process data from all active modules, and
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
 * then delete the current data in the database to insert the new afterwards.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
 * This is a cooperative, advisory lock system. Any long-running operation
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
 * that could potentially be attempted in parallel by multiple requests should
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
 * try to acquire a lock before proceeding. By obtaining a lock, one request
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
 * notifies any other requests that a specific operation is in progress which
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
 * must not be executed in parallel.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
 * To use this API, pick a unique name for the lock. A sensible choice is the
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
 * name of the function performing the operation. A very simple example use of
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
 * this API:
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
 * @code
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
 * function mymodule_long_operation() {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
 *   if (lock_acquire('mymodule_long_operation')) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
 *     // Do the long operation here.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
 *     // ...
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
 *     lock_release('mymodule_long_operation');
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
 *   }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
 * }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
 * @endcode
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
 * If a function acquires a lock it should always release it when the
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
 * operation is complete by calling lock_release(), as in the example.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
 * A function that has acquired a lock may attempt to renew a lock (extend the
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
 * duration of the lock) by calling lock_acquire() again during the operation.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
 * Failure to renew a lock is indicative that another request has acquired
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
 * the lock, and that the current operation may need to be aborted.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
 * If a function fails to acquire a lock it may either immediately return, or
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
 * it may call lock_wait() if the rest of the current page request requires
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
 * that the operation in question be complete. After lock_wait() returns,
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
 * the function may again attempt to acquire the lock, or may simply allow the
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
 * page request to proceed on the assumption that a parallel request completed
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
 * the operation.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
 * lock_acquire() and lock_wait() will automatically break (delete) a lock
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
 * whose duration has exceeded the timeout specified when it was acquired.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
 * Alternative implementations of this API (such as APC) may be substituted
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
 * by setting the 'lock_inc' variable to an alternate include filepath. Since
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
 * this is an API intended to support alternative implementations, code using
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
 * this API should never rely upon specific implementation details (for example
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
 * no code should look for or directly modify a lock in the {semaphore} table).
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
 * Initialize the locking system.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
function lock_initialize() {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
  global $locks;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
  $locks = array();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
 * Helper function to get this request's unique id.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
function _lock_id() {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
  // Do not use drupal_static(). This identifier refers to the current
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
  // client request, and must not be changed under any circumstances
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
  // else the shutdown handler may fail to release our locks.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
  static $lock_id;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
  if (!isset($lock_id)) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
    // Assign a unique id.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
    $lock_id = uniqid(mt_rand(), TRUE);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
    // We only register a shutdown function if a lock is used.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
    drupal_register_shutdown_function('lock_release_all', $lock_id);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
  return $lock_id;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
 * Acquire (or renew) a lock, but do not block if it fails.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
 * @param $name
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
 *   The name of the lock. Limit of name's length is 255 characters.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
 * @param $timeout
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
 *   A number of seconds (float) before the lock expires (minimum of 0.001).
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
 * @return
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
 *   TRUE if the lock was acquired, FALSE if it failed.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
function lock_acquire($name, $timeout = 30.0) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
  global $locks;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
  // Insure that the timeout is at least 1 ms.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
  $timeout = max($timeout, 0.001);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
  $expire = microtime(TRUE) + $timeout;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   108
  if (isset($locks[$name])) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   109
    // Try to extend the expiration of a lock we already acquired.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   110
    $success = (bool) db_update('semaphore')
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   111
      ->fields(array('expire' => $expire))
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   112
      ->condition('name', $name)
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   113
      ->condition('value', _lock_id())
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   114
      ->execute();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   115
    if (!$success) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   116
      // The lock was broken.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   117
      unset($locks[$name]);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   118
    }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   119
    return $success;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   120
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   121
  else {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   122
    // Optimistically try to acquire the lock, then retry once if it fails.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   123
    // The first time through the loop cannot be a retry.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   124
    $retry = FALSE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   125
    // We always want to do this code at least once.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   126
    do {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   127
      try {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   128
        db_insert('semaphore')
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   129
          ->fields(array(
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   130
            'name' => $name,
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   131
            'value' => _lock_id(),
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   132
            'expire' => $expire,
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   133
          ))
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   134
          ->execute();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   135
        // We track all acquired locks in the global variable.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   136
        $locks[$name] = TRUE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   137
        // We never need to try again.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   138
        $retry = FALSE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   139
      }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   140
      catch (PDOException $e) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   141
        // Suppress the error. If this is our first pass through the loop,
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   142
        // then $retry is FALSE. In this case, the insert must have failed
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   143
        // meaning some other request acquired the lock but did not release it.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   144
        // We decide whether to retry by checking lock_may_be_available()
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   145
        // Since this will break the lock in case it is expired.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   146
        $retry = $retry ? FALSE : lock_may_be_available($name);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   147
      }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   148
      // We only retry in case the first attempt failed, but we then broke
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   149
      // an expired lock.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   150
    } while ($retry);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   151
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   152
  return isset($locks[$name]);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   153
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   154
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   155
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   156
 * Check if lock acquired by a different process may be available.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   157
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   158
 * If an existing lock has expired, it is removed.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   159
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   160
 * @param $name
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   161
 *   The name of the lock.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   162
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   163
 * @return
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   164
 *   TRUE if there is no lock or it was removed, FALSE otherwise.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   165
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   166
function lock_may_be_available($name) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   167
  $lock = db_query('SELECT expire, value FROM {semaphore} WHERE name = :name', array(':name' => $name))->fetchAssoc();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   168
  if (!$lock) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   169
    return TRUE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   170
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   171
  $expire = (float) $lock['expire'];
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   172
  $now = microtime(TRUE);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   173
  if ($now > $expire) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   174
    // We check two conditions to prevent a race condition where another
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   175
    // request acquired the lock and set a new expire time. We add a small
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   176
    // number to $expire to avoid errors with float to string conversion.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   177
    return (bool) db_delete('semaphore')
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   178
      ->condition('name', $name)
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   179
      ->condition('value', $lock['value'])
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   180
      ->condition('expire', 0.0001 + $expire, '<=')
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   181
      ->execute();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   182
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   183
  return FALSE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   184
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   185
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   186
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   187
 * Wait for a lock to be available.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   188
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   189
 * This function may be called in a request that fails to acquire a desired
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   190
 * lock. This will block further execution until the lock is available or the
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   191
 * specified delay in seconds is reached. This should not be used with locks
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   192
 * that are acquired very frequently, since the lock is likely to be acquired
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   193
 * again by a different request while waiting.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   194
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   195
 * @param $name
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   196
 *   The name of the lock.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   197
 * @param $delay
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   198
 *   The maximum number of seconds to wait, as an integer.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   199
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   200
 * @return
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   201
 *   TRUE if the lock holds, FALSE if it is available.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   202
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   203
function lock_wait($name, $delay = 30) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   204
  // Pause the process for short periods between calling
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   205
  // lock_may_be_available(). This prevents hitting the database with constant
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   206
  // database queries while waiting, which could lead to performance issues.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   207
  // However, if the wait period is too long, there is the potential for a
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   208
  // large number of processes to be blocked waiting for a lock, especially
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   209
  // if the item being rebuilt is commonly requested. To address both of these
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   210
  // concerns, begin waiting for 25ms, then add 25ms to the wait period each
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   211
  // time until it reaches 500ms. After this point polling will continue every
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   212
  // 500ms until $delay is reached.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   213
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   214
  // $delay is passed in seconds, but we will be using usleep(), which takes
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   215
  // microseconds as a parameter. Multiply it by 1 million so that all
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   216
  // further numbers are equivalent.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   217
  $delay = (int) $delay * 1000000;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   218
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   219
  // Begin sleeping at 25ms.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   220
  $sleep = 25000;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   221
  while ($delay > 0) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   222
    // This function should only be called by a request that failed to get a
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   223
    // lock, so we sleep first to give the parallel request a chance to finish
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   224
    // and release the lock.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   225
    usleep($sleep);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   226
    // After each sleep, increase the value of $sleep until it reaches
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   227
    // 500ms, to reduce the potential for a lock stampede.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   228
    $delay = $delay - $sleep;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   229
    $sleep = min(500000, $sleep + 25000, $delay);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   230
    if (lock_may_be_available($name)) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   231
      // No longer need to wait.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   232
      return FALSE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   233
    }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   234
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   235
  // The caller must still wait longer to get the lock.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   236
  return TRUE;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   237
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   238
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   239
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   240
 * Release a lock previously acquired by lock_acquire().
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   241
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   242
 * This will release the named lock if it is still held by the current request.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   243
 *
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   244
 * @param $name
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   245
 *   The name of the lock.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   246
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   247
function lock_release($name) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   248
  global $locks;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   249
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   250
  unset($locks[$name]);
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   251
  db_delete('semaphore')
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   252
    ->condition('name', $name)
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   253
    ->condition('value', _lock_id())
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   254
    ->execute();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   255
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   256
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   257
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   258
 * Release all previously acquired locks.
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   259
 */
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   260
function lock_release_all($lock_id = NULL) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   261
  global $locks;
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   262
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   263
  $locks = array();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   264
  if (empty($lock_id)) {
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   265
    $lock_id = _lock_id();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   266
  }
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   267
  db_delete('semaphore')
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   268
    ->condition('value', $lock_id)
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   269
    ->execute();
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   270
}
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   271
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   272
/**
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   273
 * @} End of "defgroup lock".
e756a8c72c3d integrate drupal and correct build process. update version
ymh <ymh.work@gmail.com>
parents:
diff changeset
   274
 */