mutualExclusion.php 3.47 KB
<?php
/***************************************************************************
 * This gives a method of mutual exclusion for critical Sections within
 * your script. A lock-file will be created for every critical section that
 * must be secured. The file hold information about the lock and is a
 * handle (name) for the lock, so that every script uses the same lock.
 * The lock will be achieved by one of two ways.
 *
 * 1. by using semaphores if they are available within the current php
 * installation.
 * 2. else by using flock, which exists within every php installation.
 *
 * Semaphores are used in favour because flock did not work reliably in
 * threaded environments. That means, if we have no semaphores and are
 * in a threaded environment these functions are also not reliable.
 * In doubt ask your administrator or provider.
 *
 * Author: Georg Steffers <georg@steffers.org
 * Date:   14th Oct. 2007
 ***************************************************************************/

require_once dirname(__FILE__) . '/../config.php';
require_once LIBDIR . 'errException.php';

/**
 * Try to acquire a lock to enter a critical section. If the lock is already
 * acquired the function blocks until the lock id freed again. If more than
 * one process waits for the lock, than it is undefined which process get
 * the lock next. This depends on which process gets CPU time first. All other
 * processes continue waiting.
 *
 * Sideeffects:
 *  If this function succeeds ignore_user_abort is set to TRUE so that the
 *  critical section might not be interruped by a user abort. This can
 *  but shouldn't be changed within the critical section and will be reset
 *  in releaseLock.
 *
 * Arguments:
 *  $lockFile <string>: Path and basename to the lockfile to use.
 *  $csId <string[1]>:  Id of the critical section, will be part of filename.
 *  $msg <string>:      The message to be written to the lockfile.
 *
 * If $msg is NULL, a default message is created from session_id and time
 *
 * Returns:
 *  Array of lock information.
 *   [0] Filehandle to opened lockfile.
 *   [1] Resource id for semaphor 
 *   [2] the old setting of ignore_user_abort.
 */
function acquireLock ($lockFile, $csId, $msg = NULL)
{
	$fName = $lockFile . $csId . '.lck';
	$lock  = NULL;

	setErrExceptionMapping ();

	$lockHandle = fopen ($fName, 'w');

	// if available use semaphores for mutual exclusion because flock
	// doesn't work reliably in threaded environments.
	if (function_exists ('sem_get'))
	{
		$lock = sem_get (ftok ($fName, $csId), 1, 0644, TRUE);

		$state = sem_acquire ($lock);
		while ($state === FALSE)
			$state = sem_acquire ($lock);
	}
	else
	{
		$state = flock ($lock, LOCK_EX);
		while ($state === FALSE)
			$state = flock ($lock, LOCK_EX);
	}

	// Here one could write informations in the lockfile...time, pid, etc.
	if ($msg === NULL)
		$msg = session_id () . '::' . time ();
	fwrite ($lockHandle, $msg . "\n");
	fflush ($lockHandle);

	resetErrExceptionMapping ();

	$userAbort = ignore_user_abort(TRUE);
	return array ($lockHandle, $lock, $userAbort);
}

/**
 * Release a lock previously acquired by acquireLock.
 *
 * Sideeffects:
 *  Sets ignore_user_abort to the value before acquireLock succeds.
 *
 * Arguments:
 *  $lock <array[3]>: The array returned by a successfull acquireLock call.
 */
function releaseLock ($lock)
{
	setErrExceptionMapping ();

	if ($lock[1] !== NULL)
		sem_release ($lock[1]);

	ftruncate ($lock[0], 0);
	fclose ($lock[0]);

	resetErrExceptionMapping ();

	ignore_user_abort($lock[2]);
}

?>