See Release Notes
Long Term Support Release
Differences Between: [Versions 39 and 401] [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 /** 18 * This is a db record locking factory. 19 * 20 * @package core 21 * @category lock 22 * @copyright Damyon Wiese 2013 23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 24 */ 25 26 namespace core\lock; 27 28 defined('MOODLE_INTERNAL') || die(); 29 30 /** 31 * This is a db record locking factory. 32 * 33 * This lock factory uses record locks relying on sql of the form "SET XXX where YYY" and checking if the 34 * value was set. It supports timeouts, autorelease and can work on any DB. The downside - is this 35 * will always be slower than some shared memory type locking function. 36 * 37 * @package core 38 * @category lock 39 * @copyright Damyon Wiese 2013 40 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 41 */ 42 class db_record_lock_factory implements lock_factory { 43 44 /** @var \moodle_database $db Hold a reference to the global $DB */ 45 protected $db; 46 47 /** @var string $type Used to prefix lock keys */ 48 protected $type; 49 50 /** @var array $openlocks - List of held locks - used by auto-release */ 51 protected $openlocks = array(); 52 53 /** 54 * Is available. 55 * @return boolean - True if this lock type is available in this environment. 56 */ 57 public function is_available() { 58 return true; 59 } 60 61 /** 62 * Almighty constructor. 63 * @param string $type - Used to prefix lock keys. 64 */ 65 public function __construct($type) { 66 global $DB; 67 68 $this->type = $type; 69 // Save a reference to the global $DB so it will not be released while we still have open locks. 70 $this->db = $DB; 71 72 \core_shutdown_manager::register_function(array($this, 'auto_release')); 73 } 74 75 /** 76 * Return information about the blocking behaviour of the lock type on this platform. 77 * @return boolean - True 78 */ 79 public function supports_timeout() { 80 return true; 81 } 82 83 /** 84 * Will this lock type will be automatically released when a process ends. 85 * 86 * @return boolean - True (shutdown handler) 87 */ 88 public function supports_auto_release() { 89 return true; 90 } 91 92 /** 93 * Multiple locks for the same resource can be held by a single process. 94 * 95 * @deprecated since Moodle 3.10. 96 * @return boolean - False - not process specific. 97 */ 98 public function supports_recursion() { 99 debugging('The function supports_recursion() is deprecated, please do not use it anymore.', 100 DEBUG_DEVELOPER); 101 return false; 102 } 103 104 /** 105 * This function generates a unique token for the lock to use. 106 * It is important that this token is not solely based on time as this could lead 107 * to duplicates in a clustered environment (especially on VMs due to poor time precision). 108 */ 109 protected function generate_unique_token() { 110 return \core\uuid::generate(); 111 } 112 113 /** 114 * Create and get a lock 115 * @param string $resource - The identifier for the lock. Should use frankenstyle prefix. 116 * @param int $timeout - The number of seconds to wait for a lock before giving up. 117 * @param int $maxlifetime - Unused by this lock type. 118 * @return boolean - true if a lock was obtained. 119 */ 120 public function get_lock($resource, $timeout, $maxlifetime = 86400) { 121 122 $token = $this->generate_unique_token(); 123 $now = time(); 124 $giveuptime = $now + $timeout; 125 $expires = $now + $maxlifetime; 126 127 $resourcekey = $this->type . '_' . $resource; 128 129 if (!$this->db->record_exists('lock_db', array('resourcekey' => $resourcekey))) { 130 $record = new \stdClass(); 131 $record->resourcekey = $resourcekey; 132 $result = $this->db->insert_record('lock_db', $record); 133 } 134 135 $params = array('expires' => $expires, 136 'token' => $token, 137 'resourcekey' => $resourcekey, 138 'now' => $now); 139 $sql = 'UPDATE {lock_db} 140 SET 141 expires = :expires, 142 owner = :token 143 WHERE 144 resourcekey = :resourcekey AND 145 (owner IS NULL OR expires < :now)'; 146 147 do { 148 $now = time(); 149 $params['now'] = $now; 150 $this->db->execute($sql, $params); 151 152 $countparams = array('owner' => $token, 'resourcekey' => $resourcekey); 153 $result = $this->db->count_records('lock_db', $countparams); 154 $locked = $result === 1; 155 if (!$locked && $timeout > 0) { 156 usleep(rand(10000, 250000)); // Sleep between 10 and 250 milliseconds. 157 } 158 // Try until the giveup time. 159 } while (!$locked && $now < $giveuptime); 160 161 if ($locked) { 162 $this->openlocks[$token] = 1; 163 return new lock($token, $this); 164 } 165 166 return false; 167 } 168 169 /** 170 * Release a lock that was previously obtained with @lock. 171 * @param lock $lock - a lock obtained from this factory. 172 * @return boolean - true if the lock is no longer held (including if it was never held). 173 */ 174 public function release_lock(lock $lock) { 175 $params = array('noexpires' => null, 176 'token' => $lock->get_key(), 177 'noowner' => null); 178 179 $sql = 'UPDATE {lock_db} 180 SET 181 expires = :noexpires, 182 owner = :noowner 183 WHERE 184 owner = :token'; 185 $result = $this->db->execute($sql, $params); 186 if ($result) { 187 unset($this->openlocks[$lock->get_key()]); 188 } 189 return $result; 190 } 191 192 /** 193 * Extend a lock that was previously obtained with @lock. 194 * 195 * @deprecated since Moodle 3.10. 196 * @param lock $lock - a lock obtained from this factory. 197 * @param int $maxlifetime - the new lifetime for the lock (in seconds). 198 * @return boolean - true if the lock was extended. 199 */ 200 public function extend_lock(lock $lock, $maxlifetime = 86400) { 201 debugging('The function extend_lock() is deprecated, please do not use it anymore.', 202 DEBUG_DEVELOPER); 203 204 $now = time(); 205 $expires = $now + $maxlifetime; 206 $params = array('expires' => $expires, 207 'token' => $lock->get_key()); 208 209 $sql = 'UPDATE {lock_db} 210 SET 211 expires = :expires, 212 WHERE 213 owner = :token'; 214 215 $this->db->execute($sql, $params); 216 $countparams = array('owner' => $lock->get_key()); 217 $result = $this->count_records('lock_db', $countparams); 218 219 return $result === 0; 220 } 221 222 /** 223 * Auto release any open locks on shutdown. 224 * This is required, because we may be using persistent DB connections. 225 */ 226 public function auto_release() { 227 // Called from the shutdown handler. Must release all open locks. 228 foreach ($this->openlocks as $key => $unused) { 229 $lock = new lock($key, $this); 230 $lock->release(); 231 } 232 } 233 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body