eZ Publish  [4.0]
ezmutex.php
Go to the documentation of this file.
00001 <?php
00002 //
00003 // Definition of eZMutex class
00004 //
00005 // Created on: <24-Apr-2007 18:59:49 hovik>
00006 //
00007 // ## BEGIN COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00008 // SOFTWARE NAME: eZ Publish
00009 // SOFTWARE RELEASE: 4.0.x
00010 // COPYRIGHT NOTICE: Copyright (C) 1999-2008 eZ Systems AS
00011 // SOFTWARE LICENSE: GNU General Public License v2.0
00012 // NOTICE: >
00013 //   This program is free software; you can redistribute it and/or
00014 //   modify it under the terms of version 2.0  of the GNU General
00015 //   Public License as published by the Free Software Foundation.
00016 //
00017 //   This program is distributed in the hope that it will be useful,
00018 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 //   GNU General Public License for more details.
00021 //
00022 //   You should have received a copy of version 2.0 of the GNU General
00023 //   Public License along with this program; if not, write to the Free
00024 //   Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
00025 //   MA 02110-1301, USA.
00026 //
00027 //
00028 // ## END COPYRIGHT, LICENSE AND WARRANTY NOTICE ##
00029 //
00030 
00031 /*! \file ezmutex.php
00032 */
00033 
00034 /*!
00035   \class eZMutex ezmutex.php
00036   \brief The class eZMutex provides a file based mutex. The mutex works across processes.
00037 
00038 */
00039 
00040 //include_once( 'lib/ezutils/classes/ezsys.php' );
00041 //include_once( 'lib/ezfile/classes/ezfile.php' );
00042 
00043 class eZMutex
00044 {
00045     const STEAL_STRING = '_eZMutex_Steal';
00046 
00047     /*!
00048      Constructor. Creates a mutex object for
00049      mutext <name>. The mutex is file based, and a
00050      mutex is valid across PHP processes.
00051 
00052      \param mutex name
00053     */
00054     function eZMutex( $name )
00055     {
00056         //include_once( 'lib/ezutils/classes/ezdir.php' );
00057         $this->Name = md5( $name );
00058         $mutexPath = eZDir::path( array( eZSys::cacheDirectory(),
00059                                          'ezmutex' ) );
00060         eZDir::mkdir( $mutexPath, false, true );
00061         $this->FileName = eZDir::path( array( $mutexPath,
00062                                               $this->Name ) );
00063         $this->MetaFileName = eZDir::path( array( $mutexPath,
00064                                                   $this->Name . '_meta' ) );
00065     }
00066 
00067     /*!
00068      \private
00069      Get file pointer
00070     */
00071     function fp()
00072     {
00073         if ( !isset( $GLOBALS['eZMutex_FP_' . $this->FileName] ) ||
00074              $GLOBALS['eZMutex_FP_' . $this->FileName] === false )
00075         {
00076             $GLOBALS['eZMutex_FP_' . $this->FileName] = fopen( $this->FileName, 'w' );
00077             if ( $GLOBALS['eZMutex_FP_' . $this->FileName] === false )
00078             {
00079                 eZDebug::writeError( 'Failed to open file: ' . $this->FileName );
00080             }
00081         }
00082         return $GLOBALS['eZMutex_FP_' . $this->FileName];
00083     }
00084 
00085     /*!
00086      Test if mutex is locked.
00087 
00088      \return true if mutex is locked
00089     */
00090     function test()
00091     {
00092         if ( $fp = $this->fp() )
00093         {
00094             if ( flock( $fp, LOCK_EX | LOCK_NB ) )
00095             {
00096                 flock( $fp, LOCK_UN );
00097                 return false;
00098             }
00099         }
00100         return true;
00101     }
00102 
00103     /*!
00104      Lock. Blocks untill the mutex becomes available
00105 
00106      \return true if lock is successfull.
00107              false if it fails to require a lock.
00108     */
00109     function lock()
00110     {
00111         if ( $fp = $this->fp() )
00112         {
00113             if ( flock( $fp, LOCK_EX ) )
00114             {
00115                 $this->clearMeta();
00116                 $this->setMeta( 'timestamp', time() );
00117                 return true;
00118             }
00119         }
00120         return false;
00121     }
00122 
00123     /*!
00124      Set metadata
00125 
00126      \param key
00127      \param value
00128     */
00129     function setMeta( $key, $value )
00130     {
00131         $tmpFile = $this->MetaFileName . substr( md5( mt_rand() ), 0, 8 );
00132         $content = array();
00133         if ( file_exists( $this->MetaFileName ) )
00134         {
00135             $content = unserialize( eZFile::getContents( $this->MetaFileName ) );
00136         }
00137         $content[$key] = $value;
00138         eZFile::create( $tmpFile, false, serialize( $content) );
00139         eZFile::rename( $tmpFile, $this->MetaFileName );
00140     }
00141 
00142     /*!
00143      Read meta data
00144 
00145      \param key
00146 
00147      \return value, null if no value is associated with the key.
00148     */
00149     function meta( $key )
00150     {
00151         $content = array();
00152         if ( file_exists( $this->MetaFileName ) )
00153         {
00154             $content = unserialize( eZFile::getContents( $this->MetaFileName ) );
00155         }
00156         return isset( $content[$key] ) ? $content[$key] : null;
00157     }
00158 
00159     /*!
00160      Clear the meta data
00161     */
00162     function clearMeta()
00163     {
00164         $tmpFile = $this->MetaFileName . substr( md5( mt_rand() ), 0, 8 );
00165         $content = array();
00166         eZFile::create( $tmpFile, false, serialize( $content) );
00167         eZFile::rename( $tmpFile, $this->MetaFileName );
00168     }
00169 
00170     /*!
00171      Unlock. Unlocks the mutex.
00172 
00173      \return true if the unlock is successfull.
00174     */
00175     function unlock()
00176     {
00177         if ( $fp = $this->fp() )
00178         {
00179             fclose( $fp );
00180             @unlink( $this->MetaFileName );
00181             @unlink( $this->FileName );
00182             $GLOBALS['eZMutex_FP_' . $this->FileName] = false;
00183         }
00184         return false;
00185     }
00186 
00187     /*!
00188      Get the timestamp of when the mutex was locked
00189 
00190      \return GMT timestamp of when the mutex was created.
00191              false if no lock exists.
00192     */
00193     function lockTS()
00194     {
00195         return $this->test() ? $this->meta( 'timestamp' ) : false;
00196     }
00197 
00198     /*!
00199      Steal. The function will aquire a lock on the mutex when it's stolen.
00200      <code>
00201      $myMutex = new eZMutex( 'myMutex' );
00202      if ( $myMutex->steal() )
00203      {
00204          // protected code goes here
00205          $myMutex->unlock();
00206      }
00207      </code>
00208 
00209      \param force. If this is set to true, the process will steal the mutex, even if other processes are in the
00210                    process of stealing it as well.
00211 
00212      \return false If the process is not able to steal the mutex.
00213              true if the mutex is successfully stolen.
00214     */
00215     function steal( $force = false )
00216     {
00217         $stealMutex = new eZMutex( $this->Name . eZMutex::STEAL_STRING );
00218         if ( !$force )
00219         {
00220             // Aquire a steal mutex, and steal the mutex.
00221             if ( $stealMutex->test() )
00222             {
00223                 return false;
00224             }
00225             if ( $stealMutex->lock() )
00226             {
00227                 $stealMutex->setMeta( 'pid', getmypid() );
00228                 if ( $this->lock() )
00229                 {
00230                     // sleep for 1 second in case lock has only been granted beacause a larger
00231                     // cleanup is in progress.
00232                     sleep( 1 );
00233                     $stealMutex->unlock();
00234                     return true;
00235                 }
00236             }
00237         }
00238         else
00239         {
00240             $stealPid = $stealMutex->meta( 'pid' );
00241             if ( is_numeric( $stealPid ) &&
00242                  $stealPid != 0 &&
00243                  function_exists( 'posix_kill' ) )
00244             {
00245                 eZDebug::writeNotice( 'Killing steal mutex process: ' . $stealPid );
00246                 posix_kill( $stealPid, 9 );
00247             }
00248 
00249             // If other steal mutex exists, kill it, and create your own.
00250             $this->unlock();
00251             return $this->lock();
00252         }
00253         return false;
00254     }
00255 
00256     public $Name;
00257     public $FileName;
00258 }
00259 
00260 ?>