Bug #24342 » t3lib_class.t3lib_lock.php.patch

Administrator Admin, 2010-12-15 13:15

View differences:

t3lib/class.t3lib_lock.php (working copy)
45 45
 * @subpackage t3lib
46 46
 * @see	class.t3lib_tstemplate.php, class.tslib_fe.php
47 47
 */
48
class t3lib_lock {
48
class t3lib_abstract_lock {
49 49
	protected $method;
50 50
	protected $id; // Identifier used for this lock
51 51
	protected $resource; // Resource used for this lock (can be a file or a semaphore resource)
52 52
	protected $filepointer;
53 53
	protected $isAcquired = FALSE;
54
	protected $destruct=FALSE;  // destroy all resources on destruct
54 55

  
55 56
	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.
56 57
	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.
......
117 118
	 * @return	void
118 119
	 */
119 120
	function __destruct() {
121
		$this->destruct=TRUE;
120 122
		$this->release();
121 123
	}
122 124

  
......
128 130
	 * @return	boolean		Returns true if lock could be acquired without waiting, false otherwise.
129 131
	 */
130 132
	public function acquire() {
133

  
134
		$this->isAcquired = FALSE;
135
		return FALSE;
136
	}
137

  
138
	/**
139
	 * Release the lock
140
	 *
141
	 * @return	boolean		Returns TRUE on success or FALSE on failure
142
	 */
143
	public function release() {
144
		return TRUE;
145
	}
146

  
147
	/**
148
	 * Return the locking method which is currently used
149
	 *
150
	 * @return	string		Locking method
151
	 */
152
	public function getMethod() {
153
		return $this->method;
154
	}
155

  
156
	/**
157
	 * Return the ID which is currently used
158
	 *
159
	 * @return	string		Locking ID
160
	 */
161
	public function getId() {
162
		return $this->id;
163
	}
164

  
165
	/**
166
	 * Return the resource which is currently used.
167
	 * Depending on the locking method this can be a filename or a semaphore resource.
168
	 *
169
	 * @return	mixed		Locking resource (filename as string or semaphore as resource)
170
	 */
171
	public function getResource() {
172
		return $this->resource;
173
	}
174

  
175
	/**
176
	 * Return the status of a lock
177
	 *
178
	 * @return	string		Returns TRUE if lock is acquired, FALSE otherwise
179
	 */
180
	public function getLockStatus() {
181
		return $this->isAcquired;
182
	}
183

  
184
	/**
185
	 * Adds a common log entry for this locking API using t3lib_div::sysLog().
186
	 * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
187
	 *
188
	 * @param	string		$message: The message to be logged
189
	 * @param	integer		$severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
190
	 * @return	void
191
	 */
192
	public function sysLog($message, $severity = 0) {
193
		t3lib_div::sysLog('Locking [' . $this->method . '::' . $this->id . ']: ' . trim($message), 'cms', $severity);
194
	}
195
}
196
	
197
	/**
198
	 * a static lock class used for the lock file lock
199
	 * The lock ressource is not removed in case of release (except simple locking)
200
	 * the stale lock time-out is increased
201
	 *
202
	 * @author pedersen
203
	 *
204
	 */
205

  
206
class t3lib_lock_static extends t3lib_abstract_lock {
207

  
208
	/**
209
	 * Constructor:
210
	 * initializes locking, check input parameters and set variables accordingly.
211
	 *
212
	 * @param	string		ID to identify this lock in the system
213
	 * @param	string		Define which locking method to use. Defaults to "simple".
214
	 * @param	integer		Number of times a locked resource is tried to be acquired. This is only used by manual locks like the "simple" method.
215
	 * @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.
216
	 * @return	boolean		Returns true unless something went wrong
217
	 */
218
	public function __construct($id, $method = '', $loops = 0, $step = 0) {
219
		$res=parent::__construct($id,$method,$loops,$step);
220
		parent::sysLog("Static semaphore " . $this->id);
221
		return $res;
222
	}
223
	/**
224
	 * Acquire a lock and return when successful. If the lock is already open, the client will be
225
	 *
226
	 * 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.
227
	 *
228
	 * @return	boolean		Returns true if lock could be acquired without waiting, false otherwise.
229
	 */
230
	public function acquire() {
131 231
		$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.
132 232
		$isAcquired = TRUE;
133 233

  
......
136 236
				if (is_file($this->resource)) {
137 237
					$this->sysLog('Waiting for a different process to release the lock');
138 238
					$maxExecutionTime = ini_get('max_execution_time');
139
					$maxAge = time() - ($maxExecutionTime ? $maxExecutionTime : 120);
239
					$maxAge = time() - 3*($maxExecutionTime ? $maxExecutionTime : 120);
140 240
					if (@filectime($this->resource) < $maxAge) {
141 241
						@unlink($this->resource);
142 242
						$this->sysLog('Unlink stale lockfile');
......
148 248
					$filepointer = @fopen($this->resource, 'x');
149 249
					if ($filepointer !== FALSE) {
150 250
						fclose($filepointer);
151
						$this->sysLog('Lock aquired');
251
						$this->sysLog('Lock acquired');
152 252
						$noWait = ($i === 0);
153 253
						$isAcquired = TRUE;
154 254
						break;
......
174 274
				}
175 275
			break;
176 276
			case 'semaphore':
277
				$this->sysLog('Static semaphore ' . $this->id );
177 278
				if (sem_acquire($this->resource)) {
279
						$this->sysLog('Static semaphore ' . $this->id );
178 280
						// 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.
179 281
					$noWait = FALSE;
180 282
				}
......
195 297
	 * @return	boolean		Returns TRUE on success or FALSE on failure
196 298
	 */
197 299
	public function release() {
198
		if (!$this->isAcquired) {
300
		$this->sysLog('Release static semaphore ' . $this->id );
301
		if (!$this->isAcquired&& !$this->destruct) {
199 302
			return TRUE;
200 303
		}
201 304

  
......
211 314
					$success = FALSE;
212 315
				}
213 316
				fclose($this->filepointer);
214
				unlink($this->resource);
215 317
			break;
216 318
			case 'semaphore':
319
				$this->sysLog('Release static semaphore ' . $this->id );
217 320
				if (@sem_release($this->resource)) {
218
					sem_remove($this->resource);
219 321
				} else {
220 322
					$success = FALSE;
221 323
				}
......
229 331
		return $success;
230 332
	}
231 333

  
334
}
335

  
336

  
337
class t3lib_lock extends t3lib_abstract_lock {
338

  
339
	
232 340
	/**
233
	 * Return the locking method which is currently used
341
	 * Constructor:
342
	 * initializes locking, check input parameters and set variables accordingly.
234 343
	 *
235
	 * @return	string		Locking method
344
	 * @param	string		ID to identify this lock in the system
345
	 * @param	string		Define which locking method to use. Defaults to "simple".
346
	 * @param	integer		Number of times a locked resource is tried to be acquired. This is only used by manual locks like the "simple" method.
347
	 * @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.
348
	 * @return	boolean		Returns true unless something went wrong
236 349
	 */
237
	public function getMethod() {
238
		return $this->method;
350
	public function __construct($id, $method = '', $loops = 0, $step = 0) {
351
		parent::__construct($id,$method,$loops,$step);
352
		$this->initLockLock($method,$loops,$step);
239 353
	}
240

  
354
	
355
	public function initLockLock($method,$loops,$step) {
356
		if (!$GLOBALS['lockLock'] || !($GLOBALS['lockLock'] instanceof t3lib_lock_static)) {
357
			$GLOBALS['lockLock']=t3lib_div::makeInstance('t3lib_lock_static','lockLock',$method,$loops,$step);
358
		}
359
	
360
	}
241 361
	/**
242
	 * Return the ID which is currently used
362
	 * Acquire a lock and return when successful. If the lock is already open, the client will be
243 363
	 *
244
	 * @return	string		Locking ID
364
	 * 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.
365
	 *
366
	 * @return	boolean		Returns true if lock could be acquired without waiting, false otherwise.
245 367
	 */
246
	public function getId() {
247
		return $this->id;
368
	public function acquire() {
369
		$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.
370
		$isAcquired = TRUE;
371

  
372
		switch ($this->method) {
373
			case 'simple':
374
				if (is_file($this->resource)) {
375
					$this->sysLog('Waiting for a different process to release the lock');
376
					$maxExecutionTime = ini_get('max_execution_time');
377
					$maxAge = time() - ($maxExecutionTime ? $maxExecutionTime : 120);
378
					if ($GLOBALS['lockLock']->acquire() || $GLOBALS['lockLock']->getLockStatus()){
379
						if (@filectime($this->resource) < $maxAge) {
380
							@unlink($this->resource);
381
							$this->sysLog('Unlink stale lockfile');
382
						}
383
					}
384
					$GLOBALS['lockLock']->release();
385
				}
386

  
387
				$isAcquired = FALSE;
388
				for ($i = 0; $i < $this->loops; $i++) {
389
					$filepointer = @fopen($this->resource, 'x');
390
					if ($filepointer !== FALSE) {
391
						fclose($filepointer);
392
						$this->sysLog('Lock acquired');
393
						$noWait = ($i === 0);
394
						$isAcquired = TRUE;
395
						break;
396
					}
397
					usleep($this->step * 1000);
398
				}
399

  
400
				if (!$isAcquired) {
401
					throw new Exception('Lock file could not be created');
402
				}
403
			break;
404
			case 'flock':
405
				if (($this->filepointer = fopen($this->resource, 'w+')) == FALSE) {
406
					throw new Exception('Lock file could not be opened');
407
				}
408

  
409
				if (flock($this->filepointer, LOCK_EX | LOCK_NB) == TRUE) { // Lock without blocking
410
					$noWait = TRUE;
411
				} elseif (flock($this->filepointer, LOCK_EX) == TRUE) { // Lock with blocking (waiting for similar locks to become released)
412
					$noWait = FALSE;
413
				} else {
414
					throw new Exception('Could not lock file "' . $this->resource . '"');
415
				}
416
			break;
417
			case 'semaphore':
418
				$this->sysLog('Acquire semaphore ' . $this->id );
419
				if (sem_acquire($this->resource)) {
420
						// 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.
421
					$noWait = FALSE;
422
				}
423
			break;
424
			case 'disable':
425
				$noWait = FALSE;
426
				$isAcquired = FALSE;
427
			break;
428
		}
429

  
430
		$this->isAcquired = $isAcquired;
431
		return $noWait;
248 432
	}
249 433

  
250 434
	/**
251
	 * Return the resource which is currently used.
252
	 * Depending on the locking method this can be a filename or a semaphore resource.
435
	 * Release the lock
253 436
	 *
254
	 * @return	mixed		Locking resource (filename as string or semaphore as resource)
437
	 * @return	boolean		Returns TRUE on success or FALSE on failure
255 438
	 */
256
	public function getResource() {
257
		return $this->resource;
258
	}
439
	public function release() {
440
		if (!$this->isAcquired&& !$this->destruct) {
441
			return TRUE;
442
		}
259 443

  
260
	/**
261
	 * Return the status of a lock
262
	 *
263
	 * @return	string		Returns TRUE if lock is acquired, FALSE otherwise
264
	 */
265
	public function getLockStatus() {
266
		return $this->isAcquired;
444
		$success = TRUE;
445
		if ($GLOBALS['lockLock']->acquire() || $GLOBALS['lockLock']->getLockStatus()){
446
			switch ($this->method) {
447
				case 'simple':
448
					if (unlink($this->resource) == FALSE) {
449
						$success = FALSE;
450
					}
451
				break;
452
				case 'flock':
453
					if (flock($this->filepointer, LOCK_UN) == FALSE) {
454
						$success = FALSE;
455
					}
456
					fclose($this->filepointer);
457
					unlink($this->resource);
458
				break;
459
				case 'semaphore':
460
					if (@sem_release($this->resource)) {
461
						sem_remove($this->resource);
462
					} else {
463
						$success = FALSE;
464
					}
465
				break;
466
				case 'disable':
467
					$success = FALSE;
468
				break;
469
			}
470
		}
471
		$GLOBALS['lockLock']->release();
472
		$this->isAcquired = FALSE;
473
		return $success;
267 474
	}
268 475

  
269
	/**
270
	 * Adds a common log entry for this locking API using t3lib_div::sysLog().
271
	 * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
272
	 *
273
	 * @param	string		$message: The message to be logged
274
	 * @param	integer		$severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
275
	 * @return	void
276
	 */
277
	public function sysLog($message, $severity = 0) {
278
		t3lib_div::sysLog('Locking [' . $this->method . '::' . $this->id . ']: ' . trim($message), 'cms', $severity);
279
	}
280 476
}
281 477

  
282 478

  
(1-1/4)