Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  namespace core\lock;
  18  
  19  /**
  20   * Timing wrapper around a lock factory.
  21   *
  22   * This passes all calls through to the underlying lock factory, but adds timing information on how
  23   * long it takes to get a lock and how long the lock is held for.
  24   *
  25   * @package core
  26   * @category lock
  27   * @copyright 2022 The Open University
  28   * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  29   */
  30  class timing_wrapper_lock_factory implements lock_factory {
  31  
  32      /** @var lock_factory Real lock factory */
  33      protected $factory;
  34  
  35      /** @var string Type (Frankenstyle) used for these locks */
  36      protected $type;
  37  
  38      /**
  39       * Constructor required by interface.
  40       *
  41       * @param string $type Type (should be same as passed to real lock factory)
  42       * @param lock_factory $factory Real lock factory
  43       */
  44      public function __construct($type, lock_factory $factory = null) {
  45          $this->type = $type;
  46          if (!$factory) {
  47              // This parameter has to be optional because of the interface, but it is actually
  48              // required.
  49              throw new \coding_exception('The $factory parameter must be specified');
  50          }
  51          $this->factory = $factory;
  52      }
  53  
  54      /**
  55       * Gets the real lock factory that this is wrapping.
  56       *
  57       * @return lock_factory ReaL lock factory
  58       */
  59      public function get_real_factory(): lock_factory {
  60          return $this->factory;
  61      }
  62  
  63      /**
  64       * Implementation of lock_factory::get_lock that defers to function inner_get_lock and keeps
  65       * track of how long it took.
  66       *
  67       * @param string $resource Identifier for the lock
  68       * @param int $timeout Number of seconds to wait for a lock before giving up
  69       * @param int $maxlifetime Number of seconds to wait before reclaiming a stale lock
  70       * @return \core\lock\lock|boolean - An instance of \core\lock\lock if the lock was obtained, or false.
  71       */
  72      public function get_lock($resource, $timeout, $maxlifetime = 86400) {
  73          $before = microtime(true);
  74  
  75          $result = $this->factory->get_lock($resource, $timeout, $maxlifetime);
  76  
  77          $after = microtime(true);
  78          self::record_lock_data($after, $before, $this->type, $resource, (bool)$result, $result);
  79          if ($result) {
  80              $result->init_factory($this);
  81          }
  82  
  83          return $result;
  84      }
  85  
  86      /**
  87       * Records statistics about a lock to the performance data.
  88       *
  89       * @param float $after The time after the lock was achieved.
  90       * @param float $before The time before the lock was requested.
  91       * @param string $type The type of lock.
  92       * @param string $resource The resource being locked.
  93       * @param bool $result Whether the lock was successful.
  94       * @param lock|string $lock A value uniquely identifying the lock.
  95       * @return void
  96       */
  97      public static function record_lock_data(float $after, float $before, string $type, string $resource, bool $result, $lock) {
  98          global $PERF;
  99          $duration = $after - $before;
 100          if (empty($PERF->locks)) {
 101              $PERF->locks = [];
 102          }
 103          $lockdata = (object) [
 104              'type' => $type,
 105              'resource' => $resource,
 106              'wait' => $duration,
 107              'success' => $result
 108          ];
 109          if ($result) {
 110              $lockdata->lock = $lock;
 111              $lockdata->timestart = $after;
 112          }
 113          $PERF->locks[] = $lockdata;
 114      }
 115  
 116      /**
 117       * Release a lock that was previously obtained with {@see get_lock}.
 118       *
 119       * @param lock $lock - The lock to release.
 120       * @return boolean - True if the lock is no longer held (including if it was never held).
 121       */
 122      public function release_lock(lock $lock) {
 123          self::record_lock_released_data($lock);
 124          return $this->factory->release_lock($lock);
 125      }
 126  
 127      /**
 128       * Find the lock in the performance info and update it with the time held.
 129       *
 130       * @param lock|string $lock A value uniquely identifying the lock.
 131       * @return void
 132       */
 133      public static function record_lock_released_data($lock) {
 134          global $PERF;
 135  
 136          // Find this lock in the list of locks we got, looking backwards since it is probably
 137          // the last one.
 138          for ($index = count($PERF->locks) - 1; $index >= 0; $index--) {
 139              $lockdata = $PERF->locks[$index];
 140              if (!empty($lockdata->lock) && $lockdata->lock === $lock) {
 141                  // Update the time held.
 142                  unset($lockdata->lock);
 143                  $lockdata->held = microtime(true) - $lockdata->timestart;
 144                  break;
 145              }
 146          }
 147      }
 148  
 149      /**
 150       * Calls parent factory to check if it supports timeout.
 151       *
 152       * @return boolean False if attempting to get a lock will block indefinitely.
 153       */
 154      public function supports_timeout() {
 155          return $this->factory->supports_timeout();
 156      }
 157  
 158      /**
 159       * Calls parent factory to check if it auto-releases locks.
 160       *
 161       * @return boolean True if this lock type will be automatically released when the current process ends.
 162       */
 163      public function supports_auto_release() {
 164          return $this->factory->supports_auto_release();
 165      }
 166  
 167      /**
 168       * Calls parent factory to check if it supports recursion.
 169       *
 170       * @deprecated since Moodle 3.10.
 171       * @return boolean True if attempting to get 2 locks on the same resource will "stack"
 172       */
 173      public function supports_recursion() {
 174          return $this->factory->supports_recursion();
 175      }
 176  
 177      /**
 178       * Calls parent factory to check if it is available.
 179       *
 180       * @return boolean True if this lock type is available in this environment.
 181       */
 182      public function is_available() {
 183          return $this->factory->is_available();
 184      }
 185  
 186      /**
 187       * Calls parent factory to try to extend the lock.
 188       *
 189       * @deprecated since Moodle 3.10.
 190       * @param lock $lock Lock obtained from this factory
 191       * @param int $maxlifetime New max time to hold the lock
 192       * @return boolean True if the lock was extended.
 193       */
 194      public function extend_lock(lock $lock, $maxlifetime = 86400) {
 195          return $this->factory->extend_lock($lock, $maxlifetime);
 196      }
 197  }