2 /***************************************************************
5 * (c) 2008-2009 Michael Stucki (michael@typo3.org)
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.
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.
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.
25 * This copyright notice MUST APPEAR in all copies of the script!
26 ***************************************************************/
28 * Class for providing locking features in TYPO3
32 * @author Michael Stucki <michael@typo3.org>
51 * This class provides an abstract layer to various locking features for TYPO3
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.
56 * @author Michael Stucki <michael@typo3.org>
59 * @see class.t3lib_tstemplate.php, class.tslib_fe.php
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
;
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.
77 * initializes locking, check input parameters and set variables accordingly.
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
85 public function __construct($id, $method='', $loops=0, $step=0) {
88 $id = (string)$id; // Force ID to be string
90 $this->loops
= intval($loops);
93 $this->step
= intval($step);
96 // Detect locking method
97 if (in_array($method, array('disable', 'simple', 'flock', 'semaphore'))) {
98 $this->method
= $method;
100 throw new Exception('No such method "' . $method . '"');
104 switch ($this->method
) {
107 $path = PATH_site
. 'typo3temp/locks/';
108 if (!is_dir($path)) {
109 t3lib_div
::mkdir($path);
111 $this->id
= md5($id);
112 $this->resource
= $path . $this->id
;
116 $this->id
= abs(crc32($id));
117 if (($this->resource
= sem_get($this->id
, 1)) == TRUE
) {
131 * Releases lock automatically when instance is destroyed.
135 function __destruct() {
140 * Acquire a lock and return when successful. If the lock is already open, the client will be
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.
144 * @return boolean Returns true if lock could be acquired without waiting, false otherwise.
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.
150 switch ($this->method
) {
152 if (is_file($this->resource
)) {
153 $this->sysLog('Waiting for a different process to release the lock');
155 while ($i<$this->loops
) {
157 usleep($this->step
* 1000);
159 if (!is_file($this->resource
)) { // Lock became free, leave the loop
160 $this->sysLog('Different process released the lock');
169 if (($this->filepointer
= touch($this->resource
)) == FALSE
) {
170 throw new Exception('Lock file could not be created');
174 if (($this->filepointer
= fopen($this->resource
, 'w+')) == FALSE
) {
175 throw new Exception('Lock file could not be opened');
178 if (flock($this->filepointer
, LOCK_EX|LOCK_NB
) == TRUE
) { // Lock without blocking
180 } elseif (flock($this->filepointer
, LOCK_EX
) == TRUE
) { // Lock with blocking (waiting for similar locks to become released)
183 throw new Exception('Could not lock file "'.$this->resource
.'"');
187 if (sem_acquire($this->resource
)) {
188 // 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.
198 $this->isAcquired
= $isAcquired;
205 * @return boolean Returns TRUE on success or FALSE on failure
207 public function release() {
208 if (!$this->isAcquired
) {
213 switch ($this->method
) {
215 if (unlink($this->resource
) == FALSE
) {
220 if (flock($this->filepointer
, LOCK_UN
) == FALSE
) {
223 fclose($this->filepointer
);
224 unlink($this->resource
);
227 if (@sem_release
($this->resource
)) {
228 sem_remove($this->resource
);
238 $this->isAcquired
= FALSE
;
243 * Return the locking method which is currently used
245 * @return string Locking method
247 public function getMethod() {
248 return $this->method
;
252 * Return the ID which is currently used
254 * @return string Locking ID
256 public function getId() {
261 * Return the resource which is currently used.
262 * Depending on the locking method this can be a filename or a semaphore resource.
264 * @return mixed Locking resource (filename as string or semaphore as resource)
266 public function getResource() {
267 return $this->resource
;
271 * Return the status of a lock
273 * @return string Returns TRUE if lock is acquired, FALSE otherwise
275 public function getLockStatus() {
276 return $this->isAcquired
;
280 * Adds a common log entry for this locking API using t3lib_div::sysLog().
281 * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
283 * @param string $message: The message to be logged
284 * @param integer $severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
287 public function sysLog($message, $severity=0) {
288 t3lib_div
::sysLog('Locking [' . $this->method
. '::' . $this->id
.']: ' . trim($message), 'cms', $severity);
293 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE
]['XCLASS']['t3lib/class.t3lib_lock.php']) {
294 include_once($TYPO3_CONF_VARS[TYPO3_MODE
]['XCLASS']['t3lib/class.t3lib_lock.php']);