Fixed bug #10086: Core uses deprecated function t3lib_db->sql()
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_lock.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008 Michael Stucki (michael@typo3.org)
6 * All rights reserved
7 *
8 * This script is part of the TYPO3 project. The TYPO3 project is
9 * free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * The GNU General Public License can be found at
15 * http://www.gnu.org/copyleft/gpl.html.
16 * A copy is found in the textfile GPL.txt and important notices to the license
17 * from the author is found in LICENSE.txt distributed with these scripts.
18 *
19 *
20 * This script is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
27 /**
28 * Class for providing locking features in TYPO3
29 *
30 * $Id$
31 *
32 * @author Michael Stucki <michael@typo3.org>
33 */
34
35 require_once(PATH_t3lib.'class.t3lib_div.php');
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 /**
51 * TYPO3 locking class
52 * This class provides an abstract layer to various locking features for TYPO3
53 *
54 * It is intended to blocks requests until some data has been generated.
55 * This is especially useful if two clients are requesting the same website short after each other. While the request of client 1 triggers building and caching of the website, client 2 will be waiting at this lock.
56 *
57 * @author Michael Stucki <michael@typo3.org>
58 * @package TYPO3
59 * @subpackage t3lib
60 * @see class.t3lib_tstemplate.php, class.tslib_fe.php
61 */
62 class t3lib_lock {
63 protected $method;
64 protected $id; // Identifier used for this lock
65 protected $resource; // Resource used for this lock (can be a file or a semaphore resource)
66 protected $filepointer;
67 protected $isAcquired = false;
68
69 protected $loops = 150; // Number of times a locked resource is tried to be acquired. This is only used by manual locks like the "simple" method.
70 protected $step = 200; // Milliseconds after lock acquire is retried. $loops * $step results in the maximum delay of a lock. Only used by manual locks like the "simple" method.
71
72
73
74
75
76 /**
77 * Constructor:
78 * initializes locking, check input parameters and set variables accordingly.
79 *
80 * @param string ID to identify this lock in the system
81 * @param string Define which locking method to use. Defaults to "simple".
82 * @param integer Number of times a locked resource is tried to be acquired. This is only used by manual locks like the "simple" method.
83 * @param integer Milliseconds after lock acquire is retried. $loops * $step results in the maximum delay of a lock. Only used by manual locks like the "simple" method.
84 * @return boolean Returns true unless something went wrong
85 */
86 public function __construct($id, $method='', $loops=0, $steps=0) {
87
88 // Input checks
89 $id = (string)$id; // Force ID to be string
90 if (intval($loops)) {
91 $this->loops = intval($loops);
92 }
93 if (intval($step)) {
94 $this->step = intval($step);
95 }
96
97 // Detect locking method
98 if (in_array($method, array('disable', 'simple', 'flock', 'semaphore'))) {
99 $this->method = $method;
100 } else {
101 throw new Exception('No such method "'.$method.'"');
102 }
103
104 $success = false;
105 switch ($this->method) {
106 case 'simple':
107 case 'flock':
108 $path = PATH_site.'typo3temp/locks/';
109 if (!is_dir($path)) {
110 t3lib_div::mkdir($path);
111 }
112 $this->id = md5($id);
113 $this->resource = $path.$this->id;
114 $success = true;
115 break;
116 case 'semaphore':
117 $this->id = abs(crc32($id));
118 if (($this->resource = sem_get($this->id, 1))==true) {
119 $success = true;
120 }
121 break;
122 case 'disable':
123 return false;
124 break;
125 }
126
127 return $success;
128 }
129
130 /**
131 * Destructor:
132 * Releases lock automatically when instance is destroyed.
133 *
134 * @return void
135 */
136 function __destruct() {
137 $this->release();
138 }
139
140 /**
141 * Acquire a lock and return when successful. If the lock is already open, the client will be
142 *
143 * It is important to know that the lock will be acquired in any case, even if the request was blocked first. Therefore, the lock needs to be released in every situation.
144 *
145 * @return boolean Returns true if lock could be acquired without waiting, false otherwise.
146 */
147 public function acquire() {
148 $noWait = true; // Default is true, which means continue without caring for other clients. In the case of TYPO3s cache management, this has no negative effect except some resource overhead.
149 $isAcquired = true;
150
151 switch ($this->method) {
152 case 'simple':
153 if (is_file($this->resource)) {
154 $this->sysLog('Waiting for a different process to release the lock');
155 $i = 0;
156 while ($i<$this->loops) {
157 $i++;
158 usleep($this->step*1000);
159 clearstatcache();
160 if (!is_file($this->resource)) { // Lock became free, leave the loop
161 $this->sysLog('Different process released the lock');
162 $noWait = false;
163 break;
164 }
165 }
166 } else {
167 $noWait = true;
168 }
169
170 if (($this->filepointer = touch($this->resource)) == false) {
171 throw new Exception('Lock file could not be created');
172 }
173 break;
174 case 'flock':
175 if (($this->filepointer = fopen($this->resource, 'w+')) == false) {
176 throw new Exception('Lock file could not be opened');
177 }
178
179 if (flock($this->filepointer, LOCK_EX|LOCK_NB) == true) { // Lock without blocking
180 $noWait = true;
181 } elseif (flock($this->filepointer, LOCK_EX) == true) { // Lock with blocking (waiting for similar locks to become released)
182 $noWait = false;
183 } else {
184 throw new Exception('Could not lock file "'.$this->resource.'"');
185 }
186 break;
187 case 'semaphore':
188 if (sem_acquire($this->resource)) {
189 // Unfortunately it seems not possible to find out if the request was blocked, so we return false in any case to make sure the operation is tried again.
190 $noWait = false;
191 }
192 break;
193 case 'disable':
194 $noWait = false;
195 $isAcquired = false;
196 break;
197 }
198
199 $this->isAcquired = $isAcquired;
200 return $noWait;
201 }
202
203 /**
204 * Release the lock
205 *
206 * @return boolean Returns true on success or false on failure
207 */
208 public function release() {
209 if (!$this->isAcquired) {
210 return true;
211 }
212
213 $success = true;
214 switch ($this->method) {
215 case 'simple':
216 if (unlink($this->resource) == false) {
217 $success = false;
218 }
219 break;
220 case 'flock':
221 if (flock($this->filepointer, LOCK_UN) == false) {
222 $success = false;
223 }
224 fclose($this->filepointer);
225 unlink($this->resource);
226 break;
227 case 'semaphore':
228 if (@sem_release($this->resource)) {
229 sem_remove($this->resource);
230 } else {
231 $success = false;
232 }
233 break;
234 case 'disable':
235 $success = false;
236 break;
237 }
238
239 $this->isAcquired = false;
240 return $success;
241 }
242
243 /**
244 * Return the locking method which is currently used
245 *
246 * @return string Locking method
247 */
248 public function getMethod() {
249 return $this->method;
250 }
251
252 /**
253 * Return the ID which is currently used
254 *
255 * @return string Locking ID
256 */
257 public function getId() {
258 return $this->id;
259 }
260
261 /**
262 * Return the resource which is currently used.
263 * Depending on the locking method this can be a filename or a semaphore resource.
264 *
265 * @return mixed Locking resource (filename as string or semaphore as resource)
266 */
267 public function getResource() {
268 return $this->resource;
269 }
270
271 /**
272 * Return the status of a lock
273 *
274 * @return string Returns true if lock is acquired, false otherwise
275 */
276 public function getLockStatus() {
277 return $this->isAcquired;
278 }
279
280 /**
281 * Adds a common log entry for this locking API using t3lib_div::sysLog().
282 * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
283 *
284 * @param string $message: The message to be logged
285 * @param integer $severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
286 * @return void
287 */
288 public function sysLog($message, $severity=0) {
289 t3lib_div::sysLog('Locking ['.$this->method.'::'.$this->id.']: '.trim($message), 'cms', $severity);
290 }
291 }
292
293
294 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']) {
295 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']);
296 }
297 ?>