Added feature #15668: TYPO3 Backend should display flags for translations as sprite...
[Packages/TYPO3.CMS.git] / t3lib / class.t3lib_lock.php
1 <?php
2 /***************************************************************
3 * Copyright notice
4 *
5 * (c) 2008-2010 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
36
37
38
39
40
41
42
43
44
45
46
47
48
49 /**
50 * TYPO3 locking class
51 * This class provides an abstract layer to various locking features for TYPO3
52 *
53 * It is intended to blocks requests until some data has been generated.
54 * 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.
55 *
56 * @author Michael Stucki <michael@typo3.org>
57 * @package TYPO3
58 * @subpackage t3lib
59 * @see class.t3lib_tstemplate.php, class.tslib_fe.php
60 */
61 class t3lib_lock {
62 protected $method;
63 protected $id; // Identifier used for this lock
64 protected $resource; // Resource used for this lock (can be a file or a semaphore resource)
65 protected $filepointer;
66 protected $isAcquired = false;
67
68 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.
69 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.
70
71
72
73
74
75 /**
76 * Constructor:
77 * initializes locking, check input parameters and set variables accordingly.
78 *
79 * @param string ID to identify this lock in the system
80 * @param string Define which locking method to use. Defaults to "simple".
81 * @param integer Number of times a locked resource is tried to be acquired. This is only used by manual locks like the "simple" method.
82 * @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.
83 * @return boolean Returns true unless something went wrong
84 */
85 public function __construct($id, $method='', $loops=0, $step=0) {
86
87 // Input checks
88 $id = (string)$id; // Force ID to be string
89 if (intval($loops)) {
90 $this->loops = intval($loops);
91 }
92 if (intval($step)) {
93 $this->step = intval($step);
94 }
95
96 // Detect locking method
97 if (in_array($method, array('disable', 'simple', 'flock', 'semaphore'))) {
98 $this->method = $method;
99 } else {
100 throw new Exception('No such method "' . $method . '"');
101 }
102
103 $success = FALSE;
104 switch ($this->method) {
105 case 'simple':
106 case 'flock':
107 $path = PATH_site . 'typo3temp/locks/';
108 if (!is_dir($path)) {
109 t3lib_div::mkdir($path);
110 }
111 $this->id = md5($id);
112 $this->resource = $path . $this->id;
113 $success = TRUE;
114 break;
115 case 'semaphore':
116 $this->id = abs(crc32($id));
117 if (($this->resource = sem_get($this->id, 1)) == TRUE) {
118 $success = TRUE;
119 }
120 break;
121 case 'disable':
122 return FALSE;
123 break;
124 }
125
126 return $success;
127 }
128
129 /**
130 * Destructor:
131 * Releases lock automatically when instance is destroyed.
132 *
133 * @return void
134 */
135 function __destruct() {
136 $this->release();
137 }
138
139 /**
140 * Acquire a lock and return when successful. If the lock is already open, the client will be
141 *
142 * 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.
143 *
144 * @return boolean Returns true if lock could be acquired without waiting, false otherwise.
145 */
146 public function acquire() {
147 $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.
148 $isAcquired = TRUE;
149
150 switch ($this->method) {
151 case 'simple':
152 if (is_file($this->resource)) {
153 $this->sysLog('Waiting for a different process to release the lock');
154 $maxExecutionTime = ini_get('max_execution_time');
155 $maxAge = time() - ($maxExecutionTime ? $maxExecutionTime : 120);
156 if(@filectime($this->resource) < $maxAge) {
157 @unlink($this->resource);
158 $this->sysLog('Unlink stale lockfile');
159 }
160 }
161
162 $isAcquired = FALSE;
163 for ($i = 0; $i < $this->loops; $i++) {
164 $filepointer = @fopen($this->resource, 'x');
165 if ($filepointer !== FALSE) {
166 fclose($filepointer);
167 $this->sysLog('Lock aquired');
168 $noWait = ($i === 0);
169 $isAcquired = TRUE;
170 break;
171 }
172 usleep($this->step * 1000);
173 }
174
175 if (!$isAcquired) {
176 throw new Exception('Lock file could not be created');
177 }
178 break;
179 case 'flock':
180 if (($this->filepointer = fopen($this->resource, 'w+')) == FALSE) {
181 throw new Exception('Lock file could not be opened');
182 }
183
184 if (flock($this->filepointer, LOCK_EX|LOCK_NB) == TRUE) { // Lock without blocking
185 $noWait = TRUE;
186 } elseif (flock($this->filepointer, LOCK_EX) == TRUE) { // Lock with blocking (waiting for similar locks to become released)
187 $noWait = FALSE;
188 } else {
189 throw new Exception('Could not lock file "'.$this->resource.'"');
190 }
191 break;
192 case 'semaphore':
193 if (sem_acquire($this->resource)) {
194 // 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.
195 $noWait = FALSE;
196 }
197 break;
198 case 'disable':
199 $noWait = FALSE;
200 $isAcquired = FALSE;
201 break;
202 }
203
204 $this->isAcquired = $isAcquired;
205 return $noWait;
206 }
207
208 /**
209 * Release the lock
210 *
211 * @return boolean Returns TRUE on success or FALSE on failure
212 */
213 public function release() {
214 if (!$this->isAcquired) {
215 return TRUE;
216 }
217
218 $success = TRUE;
219 switch ($this->method) {
220 case 'simple':
221 if (unlink($this->resource) == FALSE) {
222 $success = FALSE;
223 }
224 break;
225 case 'flock':
226 if (flock($this->filepointer, LOCK_UN) == FALSE) {
227 $success = FALSE;
228 }
229 fclose($this->filepointer);
230 unlink($this->resource);
231 break;
232 case 'semaphore':
233 if (@sem_release($this->resource)) {
234 sem_remove($this->resource);
235 } else {
236 $success = FALSE;
237 }
238 break;
239 case 'disable':
240 $success = FALSE;
241 break;
242 }
243
244 $this->isAcquired = FALSE;
245 return $success;
246 }
247
248 /**
249 * Return the locking method which is currently used
250 *
251 * @return string Locking method
252 */
253 public function getMethod() {
254 return $this->method;
255 }
256
257 /**
258 * Return the ID which is currently used
259 *
260 * @return string Locking ID
261 */
262 public function getId() {
263 return $this->id;
264 }
265
266 /**
267 * Return the resource which is currently used.
268 * Depending on the locking method this can be a filename or a semaphore resource.
269 *
270 * @return mixed Locking resource (filename as string or semaphore as resource)
271 */
272 public function getResource() {
273 return $this->resource;
274 }
275
276 /**
277 * Return the status of a lock
278 *
279 * @return string Returns TRUE if lock is acquired, FALSE otherwise
280 */
281 public function getLockStatus() {
282 return $this->isAcquired;
283 }
284
285 /**
286 * Adds a common log entry for this locking API using t3lib_div::sysLog().
287 * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
288 *
289 * @param string $message: The message to be logged
290 * @param integer $severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
291 * @return void
292 */
293 public function sysLog($message, $severity=0) {
294 t3lib_div::sysLog('Locking [' . $this->method . '::' . $this->id.']: ' . trim($message), 'cms', $severity);
295 }
296 }
297
298
299 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']) {
300 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']);
301 }
302 ?>