Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
}