<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace core\lock;
> use coding_exception;
/**
>
* Timing wrapper around a lock factory.
*
* This passes all calls through to the underlying lock factory, but adds timing information on how
* long it takes to get a lock and how long the lock is held for.
*
* @package core
* @category lock
* @copyright 2022 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class timing_wrapper_lock_factory implements lock_factory {
/** @var lock_factory Real lock factory */
protected $factory;
/** @var string Type (Frankenstyle) used for these locks */
protected $type;
/**
* Constructor required by interface.
*
* @param string $type Type (should be same as passed to real lock factory)
* @param lock_factory $factory Real lock factory
*/
public function __construct($type, lock_factory $factory = null) {
$this->type = $type;
if (!$factory) {
// This parameter has to be optional because of the interface, but it is actually
// required.
throw new \coding_exception('The $factory parameter must be specified');
}
$this->factory = $factory;
}
/**
* Gets the real lock factory that this is wrapping.
*
* @return lock_factory ReaL lock factory
*/
public function get_real_factory(): lock_factory {
return $this->factory;
}
/**
* Implementation of lock_factory::get_lock that defers to function inner_get_lock and keeps
* track of how long it took.
*
* @param string $resource Identifier for the lock
* @param int $timeout Number of seconds to wait for a lock before giving up
* @param int $maxlifetime Number of seconds to wait before reclaiming a stale lock
* @return \core\lock\lock|boolean - An instance of \core\lock\lock if the lock was obtained, or false.
*/
public function get_lock($resource, $timeout, $maxlifetime = 86400) {
$before = microtime(true);
$result = $this->factory->get_lock($resource, $timeout, $maxlifetime);
$after = microtime(true);
self::record_lock_data($after, $before, $this->type, $resource, (bool)$result, $result);
if ($result) {
$result->init_factory($this);
}
return $result;
}
/**
* Records statistics about a lock to the performance data.
*
* @param float $after The time after the lock was achieved.
* @param float $before The time before the lock was requested.
* @param string $type The type of lock.
* @param string $resource The resource being locked.
* @param bool $result Whether the lock was successful.
* @param lock|string $lock A value uniquely identifying the lock.
* @return void
*/
public static function record_lock_data(float $after, float $before, string $type, string $resource, bool $result, $lock) {
global $PERF;
$duration = $after - $before;
if (empty($PERF->locks)) {
$PERF->locks = [];
}
$lockdata = (object) [
'type' => $type,
'resource' => $resource,
'wait' => $duration,
'success' => $result
];
if ($result) {
$lockdata->lock = $lock;
$lockdata->timestart = $after;
}
$PERF->locks[] = $lockdata;
}
/**
* Release a lock that was previously obtained with {@see get_lock}.
*
* @param lock $lock - The lock to release.
* @return boolean - True if the lock is no longer held (including if it was never held).
*/
public function release_lock(lock $lock) {
self::record_lock_released_data($lock);
return $this->factory->release_lock($lock);
}
/**
* Find the lock in the performance info and update it with the time held.
*
* @param lock|string $lock A value uniquely identifying the lock.
* @return void
*/
public static function record_lock_released_data($lock) {
global $PERF;
// Find this lock in the list of locks we got, looking backwards since it is probably
// the last one.
for ($index = count($PERF->locks) - 1; $index >= 0; $index--) {
$lockdata = $PERF->locks[$index];
if (!empty($lockdata->lock) && $lockdata->lock === $lock) {
// Update the time held.
unset($lockdata->lock);
$lockdata->held = microtime(true) - $lockdata->timestart;
break;
}
}
}
/**
* Calls parent factory to check if it supports timeout.
*
* @return boolean False if attempting to get a lock will block indefinitely.
*/
public function supports_timeout() {
return $this->factory->supports_timeout();
}
/**
* Calls parent factory to check if it auto-releases locks.
*
* @return boolean True if this lock type will be automatically released when the current process ends.
*/
public function supports_auto_release() {
return $this->factory->supports_auto_release();
}
/**
< * Calls parent factory to check if it supports recursion.
< *
* @deprecated since Moodle 3.10.
< * @return boolean True if attempting to get 2 locks on the same resource will "stack"
*/
public function supports_recursion() {
< return $this->factory->supports_recursion();
> throw new coding_exception('The function supports_recursion() has been removed, please do not use it anymore.');
}
/**
* Calls parent factory to check if it is available.
*
* @return boolean True if this lock type is available in this environment.
*/
public function is_available() {
return $this->factory->is_available();
}
/**
< * Calls parent factory to try to extend the lock.
< *
* @deprecated since Moodle 3.10.
< * @param lock $lock Lock obtained from this factory
< * @param int $maxlifetime New max time to hold the lock
< * @return boolean True if the lock was extended.
*/
< public function extend_lock(lock $lock, $maxlifetime = 86400) {
< return $this->factory->extend_lock($lock, $maxlifetime);
> public function extend_lock() {
> throw new coding_exception('The function extend_lock() has been removed, please do not use it anymore.');
}
}