[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * @return boolean - False - not process specific. 95 */ 96 public function supports_recursion() { 97 return false; 98 } 99 100 /** 101 * This function generates a unique token for the lock to use. 102 * It is important that this token is not solely based on time as this could lead 103 * to duplicates in a clustered environment (especially on VMs due to poor time precision). 104 */ 105 protected function generate_unique_token() { 106 return generate_uuid(); 107 } 108 109 /** 110 * Create and get a lock 111 * @param string $resource - The identifier for the lock. Should use frankenstyle prefix. 112 * @param int $timeout - The number of seconds to wait for a lock before giving up. 113 * @param int $maxlifetime - Unused by this lock type. 114 * @return boolean - true if a lock was obtained. 115 */ 116 public function get_lock($resource, $timeout, $maxlifetime = 86400) { 117 118 $token = $this->generate_unique_token(); 119 $now = time(); 120 $giveuptime = $now + $timeout; 121 $expires = $now + $maxlifetime; 122 123 if (!$this->db->record_exists('lock_db', array('resourcekey' => $resource))) { 124 $record = new \stdClass(); 125 $record->resourcekey = $resource; 126 $result = $this->db->insert_record('lock_db', $record); 127 } 128 129 $params = array('expires' => $expires, 130 'token' => $token, 131 'resourcekey' => $resource, 132 'now' => $now); 133 $sql = 'UPDATE {lock_db} 134 SET 135 expires = :expires, 136 owner = :token 137 WHERE 138 resourcekey = :resourcekey AND 139 (owner IS NULL OR expires < :now)'; 140 141 do { 142 $now = time(); 143 $params['now'] = $now; 144 $this->db->execute($sql, $params); 145 146 $countparams = array('owner' => $token, 'resourcekey' => $resource); 147 $result = $this->db->count_records('lock_db', $countparams); 148 $locked = $result === 1; 149 if (!$locked) { 150 usleep(rand(10000, 250000)); // Sleep between 10 and 250 milliseconds. 151 } 152 // Try until the giveup time. 153 } while (!$locked && $now < $giveuptime); 154 155 if ($locked) { 156 $this->openlocks[$token] = 1; 157 return new lock($token, $this); 158 } 159 160 return false; 161 } 162 163 /** 164 * Release a lock that was previously obtained with @lock. 165 * @param lock $lock - a lock obtained from this factory. 166 * @return boolean - true if the lock is no longer held (including if it was never held). 167 */ 168 public function release_lock(lock $lock) { 169 $params = array('noexpires' => null, 170 'token' => $lock->get_key(), 171 'noowner' => null); 172 173 $sql = 'UPDATE {lock_db} 174 SET 175 expires = :noexpires, 176 owner = :noowner 177 WHERE 178 owner = :token'; 179 $result = $this->db->execute($sql, $params); 180 if ($result) { 181 unset($this->openlocks[$lock->get_key()]); 182 } 183 return $result; 184 } 185 186 /** 187 * Extend a lock that was previously obtained with @lock. 188 * @param lock $lock - a lock obtained from this factory. 189 * @param int $maxlifetime - the new lifetime for the lock (in seconds). 190 * @return boolean - true if the lock was extended. 191 */ 192 public function extend_lock(lock $lock, $maxlifetime = 86400) { 193 $now = time(); 194 $expires = $now + $maxlifetime; 195 $params = array('expires' => $expires, 196 'token' => $lock->get_key()); 197 198 $sql = 'UPDATE {lock_db} 199 SET 200 expires = :expires, 201 WHERE 202 owner = :token'; 203 204 $this->db->execute($sql, $params); 205 $countparams = array('owner' => $lock->get_key()); 206 $result = $this->count_records('lock_db', $countparams); 207 208 return $result === 0; 209 } 210 211 /** 212 * Auto release any open locks on shutdown. 213 * This is required, because we may be using persistent DB connections. 214 */ 215 public function auto_release() { 216 // Called from the shutdown handler. Must release all open locks. 217 foreach ($this->openlocks as $key => $unused) { 218 $lock = new lock($key, $this); 219 $lock->release(); 220 } 221 } 222 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |