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