eZ Publish  [trunk]
ezmutex.php
Go to the documentation of this file.
00001 <?php
00002 /**
00003  * File containing the eZMutex class.
00004  *
00005  * @copyright Copyright (C) 1999-2012 eZ Systems AS. All rights reserved.
00006  * @license http://www.gnu.org/licenses/gpl-2.0.txt GNU General Public License v2
00007  * @version //autogentag//
00008  * @package lib
00009  */
00010 
00011 /*!
00012   \class eZMutex ezmutex.php
00013   \brief The class eZMutex provides a file based mutex. The mutex works across processes.
00014 
00015 */
00016 
00017 class eZMutex
00018 {
00019     const STEAL_STRING = '_eZMutex_Steal';
00020 
00021     /*!
00022      Constructor. Creates a mutex object for
00023      mutext <name>. The mutex is file based, and a
00024      mutex is valid across PHP processes.
00025 
00026      \param mutex name
00027     */
00028     function eZMutex( $name )
00029     {
00030         $this->Name = md5( $name );
00031         $mutexPath = eZDir::path( array( eZSys::cacheDirectory(),
00032                                          'ezmutex' ) );
00033         eZDir::mkdir( $mutexPath, false, true );
00034         $this->FileName = eZDir::path( array( $mutexPath,
00035                                               $this->Name ) );
00036         $this->MetaFileName = eZDir::path( array( $mutexPath,
00037                                                   $this->Name . '_meta' ) );
00038     }
00039 
00040     /*!
00041      \private
00042      Get file pointer
00043     */
00044     function fp()
00045     {
00046         if ( !isset( $GLOBALS['eZMutex_FP_' . $this->FileName] ) ||
00047              $GLOBALS['eZMutex_FP_' . $this->FileName] === false )
00048         {
00049             $GLOBALS['eZMutex_FP_' . $this->FileName] = fopen( $this->FileName, 'w' );
00050             if ( $GLOBALS['eZMutex_FP_' . $this->FileName] === false )
00051             {
00052                 eZDebug::writeError( 'Failed to open file: ' . $this->FileName );
00053             }
00054         }
00055         return $GLOBALS['eZMutex_FP_' . $this->FileName];
00056     }
00057 
00058     /*!
00059      Test if mutex is locked.
00060 
00061      \return true if mutex is locked
00062     */
00063     function test()
00064     {
00065         if ( $fp = $this->fp() )
00066         {
00067             if ( flock( $fp, LOCK_EX | LOCK_NB ) )
00068             {
00069                 flock( $fp, LOCK_UN );
00070                 return false;
00071             }
00072         }
00073         return true;
00074     }
00075 
00076     /*!
00077      Lock. Blocks untill the mutex becomes available
00078 
00079      \return true if lock is successfull.
00080              false if it fails to require a lock.
00081     */
00082     function lock()
00083     {
00084         if ( $fp = $this->fp() )
00085         {
00086             if ( flock( $fp, LOCK_EX ) )
00087             {
00088                 $this->clearMeta();
00089                 $this->setMeta( 'timestamp', time() );
00090                 return true;
00091             }
00092         }
00093         return false;
00094     }
00095 
00096     /*!
00097      Set metadata
00098 
00099      \param key
00100      \param value
00101     */
00102     function setMeta( $key, $value )
00103     {
00104         $content = array();
00105         if ( file_exists( $this->MetaFileName ) )
00106         {
00107             $content = unserialize( file_get_contents( $this->MetaFileName ) );
00108         }
00109         $content[$key] = $value;
00110         eZFile::create( $this->MetaFileName, false, serialize( $content), true );
00111     }
00112 
00113     /*!
00114      Read meta data
00115 
00116      \param key
00117 
00118      \return value, null if no value is associated with the key.
00119     */
00120     function meta( $key )
00121     {
00122         $content = array();
00123         if ( file_exists( $this->MetaFileName ) )
00124         {
00125             $content = unserialize( file_get_contents( $this->MetaFileName ) );
00126         }
00127         return isset( $content[$key] ) ? $content[$key] : null;
00128     }
00129 
00130     /*!
00131      Clear the meta data
00132     */
00133     function clearMeta()
00134     {
00135         $content = array();
00136         eZFile::create( $this->MetaFileName, false, serialize( $content), true );
00137     }
00138 
00139     /*!
00140      Unlock. Unlocks the mutex.
00141 
00142      \return true if the unlock is successfull.
00143     */
00144     function unlock()
00145     {
00146         if ( $fp = $this->fp() )
00147         {
00148             fclose( $fp );
00149             @unlink( $this->MetaFileName );
00150             @unlink( $this->FileName );
00151             $GLOBALS['eZMutex_FP_' . $this->FileName] = false;
00152         }
00153         return false;
00154     }
00155 
00156     /*!
00157      Get the timestamp of when the mutex was locked
00158 
00159      \return GMT timestamp of when the mutex was created.
00160              false if no lock exists.
00161     */
00162     function lockTS()
00163     {
00164         return $this->test() ? $this->meta( 'timestamp' ) : false;
00165     }
00166 
00167     /*!
00168      Steal. The function will aquire a lock on the mutex when it's stolen.
00169      <code>
00170      $myMutex = new eZMutex( 'myMutex' );
00171      if ( $myMutex->steal() )
00172      {
00173          // protected code goes here
00174          $myMutex->unlock();
00175      }
00176      </code>
00177 
00178      \param force. If this is set to true, the process will steal the mutex, even if other processes are in the
00179                    process of stealing it as well.
00180 
00181      \return false If the process is not able to steal the mutex.
00182              true if the mutex is successfully stolen.
00183     */
00184     function steal( $force = false )
00185     {
00186         $stealMutex = new eZMutex( $this->Name . eZMutex::STEAL_STRING );
00187         if ( !$force )
00188         {
00189             // Aquire a steal mutex, and steal the mutex.
00190             if ( $stealMutex->test() )
00191             {
00192                 return false;
00193             }
00194             if ( $stealMutex->lock() )
00195             {
00196                 $stealMutex->setMeta( 'pid', getmypid() );
00197                 if ( $this->lock() )
00198                 {
00199                     // sleep for 1 second in case lock has only been granted beacause a larger
00200                     // cleanup is in progress.
00201                     sleep( 1 );
00202                     $stealMutex->unlock();
00203                     return true;
00204                 }
00205             }
00206         }
00207         else
00208         {
00209             $stealPid = $stealMutex->meta( 'pid' );
00210             if ( is_numeric( $stealPid ) &&
00211                  $stealPid != 0 &&
00212                  function_exists( 'posix_kill' ) )
00213             {
00214                 eZDebug::writeNotice( 'Killing steal mutex process: ' . $stealPid );
00215                 posix_kill( $stealPid, 9 );
00216             }
00217 
00218             // If other steal mutex exists, kill it, and create your own.
00219             $this->unlock();
00220             return $this->lock();
00221         }
00222         return false;
00223     }
00224 
00225     public $Name;
00226     public $FileName;
00227 }
00228 
00229 ?>