Added feature #4553: Enable negative timestamps
[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 private $method;
64 private $id; // Identifier used for this lock
65 private $filepointer;
66 private $isAcquired = false;
67
68 private $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 private $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, $steps=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 $this->id = $path.md5($id);
109 $success = true;
110 break;
111 case 'semaphore':
112 $id = abs(crc32($id));
113 if (($this->id = sem_get($id, 1))==true) {
114 $success = true;
115 }
116 break;
117 case 'disable':
118 return false;
119 break;
120 }
121
122 return $success;
123 }
124
125 /**
126 * Acquire a lock and return when successful. If the lock is already open, the client will be
127 *
128 * 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.
129 *
130 * @return boolean Returns true if lock could be acquired without waiting, false otherwise.
131 */
132 public function acquire() {
133 $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.
134 $isAcquired = true;
135
136 switch ($this->method) {
137 case 'simple':
138 if (is_file($this->id)) {
139 $i = 0;
140 while ($i<$this->loops) {
141 $i++;
142 usleep($this->step*1000);
143 clearstatcache();
144 if (!is_file($this->id)) { // Lock became free, leave the loop
145 $noWait = false;
146 break;
147 }
148 }
149 } else {
150 $noWait = true;
151 }
152
153 if (($this->filepointer = touch($this->id)) == false) {
154 throw new Exception('Lock file could not be created');
155 }
156 break;
157 case 'flock':
158 if (($this->filepointer = fopen($this->id, 'w+')) == false) {
159 throw new Exception('Lock file could not be opened');
160 }
161
162 if (flock($this->filepointer, LOCK_EX|LOCK_NB) == true) { // Lock without blocking
163 $noWait = true;
164 } elseif (flock($this->filepointer, LOCK_EX) == true) { // Lock with blocking (waiting for similar locks to become released)
165 $noWait = false;
166 } else {
167 throw new Exception('Could not lock file "'.$this->id.'"');
168 }
169 break;
170 case 'semaphore':
171 if (sem_acquire($this->id)) {
172 // 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.
173 $noWait = false;
174 }
175 break;
176 case 'disable':
177 $noWait = false;
178 $isAcquired = false;
179 break;
180 }
181
182 $this->isAcquired = $isAcquired;
183 return $noWait;
184 }
185
186 /**
187 * Release the lock
188 *
189 * @return boolean Returns true on success or false on failure
190 */
191 public function release() {
192 if (!$this->isAcquired) {
193 return true;
194 }
195
196 $success = true;
197 switch ($this->method) {
198 case 'simple':
199 if (unlink($this->id) == false) {
200 $success = false;
201 }
202 break;
203 case 'flock':
204 if (flock($this->filepointer, LOCK_UN) == false) {
205 $success = false;
206 }
207 fclose($this->filepointer);
208 unlink($this->id);
209 break;
210 case 'semaphore':
211 if (!sem_release($this->id)) {
212 $success = false;
213 }
214 break;
215 case 'disable':
216 $success = false;
217 break;
218 }
219
220 $this->isAcquired = false;
221 return $success;
222 }
223
224 /**
225 * Return the locking method which is currently used
226 *
227 * @return string Locking method
228 */
229 public function getMethod() {
230 return $this->method;
231 }
232
233 /**
234 * Return the ID of which is currently used
235 *
236 * @return string Locking ID
237 */
238 public function getId() {
239 return $this->id;
240 }
241
242 /**
243 * Return the status of a lock
244 *
245 * @return string Returns true if lock is acquired, false otherwise
246 */
247 public function getLockStatus() {
248 return $this->isAcquired;
249 }
250 }
251
252
253 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']) {
254 include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']);
255 }
256 ?>