class.t3lib_lock.php

Go to the documentation of this file.
00001 <?php
00002 /***************************************************************
00003 *  Copyright notice
00004 *
00005 *  (c) 2008 Michael Stucki (michael@typo3.org)
00006 *  All rights reserved
00007 *
00008 *  This script is part of the TYPO3 project. The TYPO3 project is
00009 *  free software; you can redistribute it and/or modify
00010 *  it under the terms of the GNU General Public License as published by
00011 *  the Free Software Foundation; either version 2 of the License, or
00012 *  (at your option) any later version.
00013 *
00014 *  The GNU General Public License can be found at
00015 *  http://www.gnu.org/copyleft/gpl.html.
00016 *  A copy is found in the textfile GPL.txt and important notices to the license
00017 *  from the author is found in LICENSE.txt distributed with these scripts.
00018 *
00019 *
00020 *  This script is distributed in the hope that it will be useful,
00021 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00022 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00023 *  GNU General Public License for more details.
00024 *
00025 *  This copyright notice MUST APPEAR in all copies of the script!
00026 ***************************************************************/
00027 /**
00028  * Class for providing locking features in TYPO3
00029  *
00030  * $Id: class.t3lib_lock.php 3510 2008-04-01 20:42:57Z flyguide $
00031  *
00032  * @author  Michael Stucki <michael@typo3.org>
00033  */
00034 
00035 require_once(PATH_t3lib.'class.t3lib_div.php');
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 /**
00051  * TYPO3 locking class
00052  * This class provides an abstract layer to various locking features for TYPO3
00053  *
00054  * It is intended to blocks requests until some data has been generated.
00055  * 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.
00056  *
00057  * @author  Michael Stucki <michael@typo3.org>
00058  * @package TYPO3
00059  * @subpackage t3lib
00060  * @see class.t3lib_tstemplate.php, class.tslib_fe.php
00061  */
00062 class t3lib_lock {
00063     protected $method;
00064     protected $id;      // Identifier used for this lock
00065     protected $resource;    // Resource used for this lock (can be a file or a semaphore resource)
00066     protected $filepointer;
00067     protected $isAcquired = false;
00068 
00069     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.
00070     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.
00071 
00072 
00073 
00074 
00075 
00076     /**
00077      * Constructor:
00078      * initializes locking, check input parameters and set variables accordingly.
00079      *
00080      * @param   string      ID to identify this lock in the system
00081      * @param   string      Define which locking method to use. Defaults to "simple".
00082      * @param   integer     Number of times a locked resource is tried to be acquired. This is only used by manual locks like the "simple" method.
00083      * @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.
00084      * @return  boolean     Returns true unless something went wrong
00085      */
00086     public function __construct($id, $method='', $loops=0, $steps=0)    {
00087 
00088             // Input checks
00089         $id = (string)$id;  // Force ID to be string
00090         if (intval($loops)) {
00091             $this->loops = intval($loops);
00092         }
00093         if (intval($step)) {
00094             $this->step = intval($step);
00095         }
00096 
00097             // Detect locking method
00098         if (in_array($method, array('disable', 'simple', 'flock', 'semaphore'))) {
00099             $this->method = $method;
00100         } else {
00101             throw new Exception('No such method "'.$method.'"');
00102         }
00103 
00104         $success = false;
00105         switch ($this->method) {
00106             case 'simple':
00107             case 'flock':
00108                 $path = PATH_site.'typo3temp/locks/';
00109                 if (!is_dir($path)) {
00110                     t3lib_div::mkdir($path);
00111                 }
00112                 $this->id = md5($id);
00113                 $this->resource = $path.$this->id;
00114                 $success = true;
00115             break;
00116             case 'semaphore':
00117                 $this->id = abs(crc32($id));
00118                 if (($this->resource = sem_get($this->id, 1))==true) {
00119                     $success = true;
00120                 }
00121             break;
00122             case 'disable':
00123                 return false;
00124             break;
00125         }
00126 
00127         return $success;
00128     }
00129 
00130     /**
00131      * Destructor:
00132      * Releases lock automatically when instance is destroyed.
00133      *
00134      * @return  void
00135      */
00136     function __destruct() {
00137         $this->release();
00138     }
00139 
00140     /**
00141      * Acquire a lock and return when successful. If the lock is already open, the client will be
00142      *
00143      * 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.
00144      *
00145      * @return  boolean     Returns true if lock could be acquired without waiting, false otherwise.
00146      */
00147     public function acquire()   {
00148         $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.
00149         $isAcquired = true;
00150 
00151         switch ($this->method) {
00152             case 'simple':
00153                 if (is_file($this->resource)) {
00154                     $this->sysLog('Waiting for a different process to release the lock');
00155                     $i = 0;
00156                     while ($i<$this->loops) {
00157                         $i++;
00158                         usleep($this->step*1000);
00159                         clearstatcache();
00160                         if (!is_file($this->resource)) {    // Lock became free, leave the loop
00161                             $this->sysLog('Different process released the lock');
00162                             $noWait = false;
00163                             break;
00164                         }
00165                     }
00166                 } else {
00167                     $noWait = true;
00168                 }
00169 
00170                 if (($this->filepointer = touch($this->resource)) == false) {
00171                     throw new Exception('Lock file could not be created');
00172                 }
00173             break;
00174             case 'flock':
00175                 if (($this->filepointer = fopen($this->resource, 'w+')) == false) {
00176                     throw new Exception('Lock file could not be opened');
00177                 }
00178 
00179                 if (flock($this->filepointer, LOCK_EX|LOCK_NB) == true) {   // Lock without blocking
00180                     $noWait = true;
00181                 } elseif (flock($this->filepointer, LOCK_EX) == true) {     // Lock with blocking (waiting for similar locks to become released)
00182                     $noWait = false;
00183                 } else {
00184                     throw new Exception('Could not lock file "'.$this->resource.'"');
00185                 }
00186             break;
00187             case 'semaphore':
00188                 if (sem_acquire($this->resource)) {
00189                         // 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.
00190                     $noWait = false;
00191                 }
00192             break;
00193             case 'disable':
00194                 $noWait = false;
00195                 $isAcquired = false;
00196             break;
00197         }
00198 
00199         $this->isAcquired = $isAcquired;
00200         return $noWait;
00201     }
00202 
00203     /**
00204      * Release the lock
00205      *
00206      * @return  boolean     Returns true on success or false on failure
00207      */
00208     public function release()   {
00209         if (!$this->isAcquired) {
00210             return true;
00211         }
00212 
00213         $success = true;
00214         switch ($this->method) {
00215             case 'simple':
00216                 if (unlink($this->resource) == false) {
00217                     $success = false;
00218                 }
00219             break;
00220             case 'flock':
00221                 if (flock($this->filepointer, LOCK_UN) == false) {
00222                     $success = false;
00223                 }
00224                 fclose($this->filepointer);
00225                 unlink($this->resource);
00226             break;
00227             case 'semaphore':
00228                 if (@sem_release($this->resource)) {
00229                     sem_remove($this->resource);
00230                 } else {
00231                     $success = false;
00232                 }
00233             break;
00234             case 'disable':
00235                 $success = false;
00236             break;
00237         }
00238 
00239         $this->isAcquired = false;
00240         return $success;
00241     }
00242 
00243     /**
00244      * Return the locking method which is currently used
00245      *
00246      * @return  string      Locking method
00247      */
00248     public function getMethod() {
00249         return $this->method;
00250     }
00251 
00252     /**
00253      * Return the ID which is currently used
00254      *
00255      * @return  string      Locking ID
00256      */
00257     public function getId() {
00258         return $this->id;
00259     }
00260 
00261     /**
00262      * Return the resource which is currently used.
00263      * Depending on the locking method this can be a filename or a semaphore resource.
00264      *
00265      * @return  mixed       Locking resource (filename as string or semaphore as resource)
00266      */
00267     public function getResource()   {
00268         return $this->resource;
00269     }
00270 
00271     /**
00272      * Return the status of a lock
00273      *
00274      * @return  string      Returns true if lock is acquired, false otherwise
00275      */
00276     public function getLockStatus() {
00277         return $this->isAcquired;
00278     }
00279 
00280     /**
00281      * Adds a common log entry for this locking API using t3lib_div::sysLog().
00282      * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
00283      *
00284      * @param   string      $message: The message to be logged
00285      * @param   integer     $severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
00286      * @return  void
00287      */
00288     public function sysLog($message, $severity=0) {
00289         t3lib_div::sysLog('Locking ['.$this->method.'::'.$this->id.']: '.trim($message), 'cms', $severity);
00290     }
00291 }
00292 
00293 
00294 if (defined('TYPO3_MODE') && $TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php'])  {
00295     include_once($TYPO3_CONF_VARS[TYPO3_MODE]['XCLASS']['t3lib/class.t3lib_lock.php']);
00296 }
00297 ?>

Generated on Sat Jan 3 04:23:26 2009 for TYPO3 API by  doxygen 1.4.7