Fixed bug #7630: Several problems with locking API (t3lib_lock)
authorOliver Hader <oliver.hader@typo3.org>
Wed, 27 Feb 2008 23:30:14 +0000 (23:30 +0000)
committerOliver Hader <oliver.hader@typo3.org>
Wed, 27 Feb 2008 23:30:14 +0000 (23:30 +0000)
git-svn-id: https://svn.typo3.org/TYPO3v4/Core/trunk@3312 709f56b5-9817-0410-a4d7-c38de5d9e867

ChangeLog
t3lib/class.t3lib_lock.php
typo3/sysext/cms/tslib/class.tslib_fe.php

index 47aa065..17eba1f 100755 (executable)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+2008-02-28  Oliver Hader  <oh@inpublica.de>
+
+       * Fixed bug #7630: Several problems with locking API (t3lib_lock)
+
 2008-02-27  Stanislas Rolland  <stanislas.rolland@fructifor.ca>
 
        * Fixed bug #7697: Syntax error raised by Mozilla 1.7 when loading htmlArea RTE
index 02e7f9a..726c7d4 100644 (file)
@@ -62,6 +62,7 @@ require_once(PATH_t3lib.'class.t3lib_div.php');
 class t3lib_lock {
        private $method;
        private $id;            // Identifier used for this lock
+       private $resource;      // Resource used for this lock (can be a file or a semaphore resource) 
        private $filepointer;
        private $isAcquired = false;
 
@@ -94,7 +95,7 @@ class t3lib_lock {
                }
 
                        // Detect locking method
-               if (in_array($method, array('disable', 'simple','flock','semaphore'))) {
+               if (in_array($method, array('disable', 'simple', 'flock', 'semaphore'))) {
                        $this->method = $method;
                } else {
                        throw new Exception('No such method "'.$method.'"');
@@ -105,12 +106,16 @@ class t3lib_lock {
                        case 'simple':
                        case 'flock':
                                $path = PATH_site.'typo3temp/locks/';
-                               $this->id = $path.md5($id);
+                               if (!is_dir($path)) {
+                                       t3lib_div::mkdir($path);
+                               }
+                               $this->id = md5($id);
+                               $this->resource = $path.$this->id;
                                $success = true;
                        break;
                        case 'semaphore':
-                               $id = abs(crc32($id));
-                               if (($this->id = sem_get($id, 1))==true) {
+                               $this->id = abs(crc32($id));
+                               if (($this->resource = sem_get($this->id, 1))==true) {
                                        $success = true;
                                }
                        break;
@@ -135,13 +140,15 @@ class t3lib_lock {
 
                switch ($this->method) {
                        case 'simple':
-                               if (is_file($this->id)) {
+                               if (is_file($this->resource)) {
+                                       $this->sysLog('Waiting for a different process to release the lock');
                                        $i = 0;
                                        while ($i<$this->loops) {
                                                $i++;
                                                usleep($this->step*1000);
                                                clearstatcache();
-                                               if (!is_file($this->id)) {      // Lock became free, leave the loop
+                                               if (!is_file($this->resource)) {        // Lock became free, leave the loop
+                                                       $this->sysLog('Different process released the lock');
                                                        $noWait = false;
                                                        break;
                                                }
@@ -150,12 +157,12 @@ class t3lib_lock {
                                        $noWait = true;
                                }
 
-                               if (($this->filepointer = touch($this->id)) == false) {
+                               if (($this->filepointer = touch($this->resource)) == false) {
                                        throw new Exception('Lock file could not be created');
                                }
                        break;
                        case 'flock':
-                               if (($this->filepointer = fopen($this->id, 'w+')) == false) {
+                               if (($this->filepointer = fopen($this->resource, 'w+')) == false) {
                                        throw new Exception('Lock file could not be opened');
                                }
 
@@ -164,11 +171,11 @@ class t3lib_lock {
                                } elseif (flock($this->filepointer, LOCK_EX) == true) {         // Lock with blocking (waiting for similar locks to become released)
                                        $noWait = false;
                                } else {
-                                       throw new Exception('Could not lock file "'.$this->id.'"');
+                                       throw new Exception('Could not lock file "'.$this->resource.'"');
                                }
                        break;
                        case 'semaphore':
-                               if (sem_acquire($this->id)) {
+                               if (sem_acquire($this->resource)) {
                                                // 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.
                                        $noWait = false;
                                }
@@ -196,7 +203,7 @@ class t3lib_lock {
                $success = true;
                switch ($this->method) {
                        case 'simple':
-                               if (unlink($this->id) == false) {
+                               if (unlink($this->resource) == false) {
                                        $success = false;
                                }
                        break;
@@ -205,10 +212,12 @@ class t3lib_lock {
                                        $success = false;
                                }
                                fclose($this->filepointer);
-                               unlink($this->id);
+                               unlink($this->resource);
                        break;
                        case 'semaphore':
-                               if (!sem_release($this->id)) {
+                               if (sem_release($this->resource)) {
+                                       sem_remove($this->resource);
+                               } else {
                                        $success = false;
                                }
                        break;
@@ -231,7 +240,7 @@ class t3lib_lock {
        }
 
        /**
-        * Return the ID of which is currently used
+        * Return the ID which is currently used
         *
         * @return      string          Locking ID
         */
@@ -240,6 +249,16 @@ class t3lib_lock {
        }
 
        /**
+        * Return the resource which is currently used.
+        * Depending on the locking method this can be a filename or a semaphore resource.
+        *
+        * @return      mixed           Locking resource (filename as string or semaphore as resource)
+        */
+       public function getResource()   {
+               return $this->resource;
+       }
+
+       /**
         * Return the status of a lock
         *
         * @return      string          Returns true if lock is acquired, false otherwise
@@ -247,6 +266,18 @@ class t3lib_lock {
        public function getLockStatus() {
                return $this->isAcquired;
        }
+
+       /**
+        * Adds a common log entry for this locking API using t3lib_div::sysLog().
+        * Example: 25-02-08 17:58 - cms: Locking [simple::0aeafd2a67a6bb8b9543fb9ea25ecbe2]: Acquired
+        *
+        * @param       string          $message: The message to be logged
+        * @param       integer         $severity: Severity - 0 is info (default), 1 is notice, 2 is warning, 3 is error, 4 is fatal error
+        * @return      void
+        */
+       public function sysLog($message, $severity=0) {
+               t3lib_div::sysLog('Locking ['.$this->method.'::'.$this->id.']: '.trim($message), 'cms', $severity);
+       }
 }
 
 
index 0fa3bac..23c11b4 100755 (executable)
@@ -2727,13 +2727,14 @@ require_once (PATH_t3lib.'class.t3lib_lock.php');
         * Lock the page generation process
         * The lock is used to queue page requests until this page is successfully stored in the cache.
         *
-        * @param       object          Reference to a locking object
+        * @param       t3lib_lock      Reference to a locking object
         * @param       string          String to identify the lock in the system
         * @return      boolean         Returns true if the lock could be obtained, false otherwise (= process had to wait for existing lock to be released)
         * @see releasePageGenerationLock()
         */
        function acquirePageGenerationLock(&$lockObj, $key)     {
                if ($this->no_cache || $this->headerNoCache()) {
+                       $lockObj->sysLog('Page is not cached, no locking required');
                        return true;    // No locking is needed if caching is disabled
                }
 
@@ -2748,9 +2749,12 @@ require_once (PATH_t3lib.'class.t3lib_lock.php');
                                        // true = Page could get locked without blocking
                                        // false = Page could get locked but process was blocked before
                                $success = $lockObj->acquire();
+                               if ($lockObj->getLockStatus()) {
+                                       $lockObj->sysLog('Acquired lock');
+                               }
                        }
                } catch (Exception $e) {
-                       t3lib_div::sysLog('Locking failed: '.$e->getMessage(), 'cms', 3);
+                       $lockObj->sysLog('Failed to acquire lock: '.$e->getMessage(), 3);
                        $success = false;       // If locking fails, return with false and continue without locking
                }
 
@@ -2760,21 +2764,21 @@ require_once (PATH_t3lib.'class.t3lib_lock.php');
        /**
         * Release the page generation lock
         *
-        * @param       object          Reference to a locking object
+        * @param       t3lib_lock      Reference to a locking object
         * @return      boolean         Returns true on success, false otherwise
         * @see acquirePageGenerationLock()
         */
        function releasePageGenerationLock(&$lockObj)   {
-               if ($this->no_cache || $this->headerNoCache()) {
-                       return true;    // No locking is needed if caching is disabled
-               }
-
                $success = false;
-               if (is_object($lockObj)) {
+                       // If lock object is set and was aquired (may also happen if no_cache was enabled during runtime), release it:
+               if (is_object($lockObj) && $lockObj instanceof t3lib_lock && $lockObj->getLockStatus()) {
                        $success = $lockObj->release();
+                       $lockObj->sysLog('Released lock');
                        unset($lockObj);
+                       // Otherwise, if caching is disabled, no locking is required:
+               } elseif ($this->no_cache || $this->headerNoCache()) {
+                       $success = true; 
                }
-
                return $success;
        }