Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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