Skip to content

Instantly share code, notes, and snippets.

@nevkontakte
Created July 23, 2020 21:02
Show Gist options
  • Save nevkontakte/e8f8e0fe631c51ad035c04394270698c to your computer and use it in GitHub Desktop.
Save nevkontakte/e8f8e0fe631c51ad035c04394270698c to your computer and use it in GitHub Desktop.
<?php
/**
* Simple mutex implementation
*
* This class implements sime kind of mutex using PHP's session file lock feature.
* To be short, PHP forces serial access for processes trying wokr with the same session file.
* This is the most simple mutex imitation (flock() is a strange function with unpredictable bugs),
* but this method has some disadvantages and limitations which you should be aware of:
* 1) Using such mutexes together with native PHP sessions can cause unexpected behaviour and possible
* session DATA LOSS!
* 2) It allows not more than one locked mutex at the same time, because only one active session may exist.
* 3) There are one known bug: when two parallel requests are handled by the same script and PHP runs as
* Apache's mod_php this requests will be executed in ENTIRELY SERIAL way, not only in block which are
* protected with mutex. I have no idea why does it happen.
* 4) This class works only if sessions are handled by native "files" module.
* So, this class should be used only in simple and small scripts. In large projects consider using
* something like semaphores module (http://en.php.net/manual/en/book.sem.php)
*
* @author Alek$ <aleks@aradmin.org.ru> http://nevkontakte.org.ru
* @license New BSD License (http://www.opensource.org/licenses/bsd-license.php)
*/
class PHP_Mutex
{
private static $mutex = null;
private $locked = false;
private $old_session = "";
/**
* Lock mutex $id
*
* @param string $id Mutex name.
*/
public static function lock($id)
{
self::init();
self::$mutex->getLock($id);
}
/**
* Unlock mutex $id. Note that it must be the same as used in previous lock() call.
*
* @param string $id Mutex name.
*/
public static function unlock($id)
{
self::init();
self::$mutex->releaseLock($id);
}
private function __construct($man = false)
{
if(session_module_name() !== "files")
{
trigger_error('PHP_Mutex relies on "files" session handler module, but '.session_module_name().' is used', E_USER_WARNING);
}
if(session_id() !== "")
{
trigger_error('Possible session conflict with PHP_Mutex: already started session detected', E_USER_WARNING);
$this->old_session = session_id();
}
}
private function getLock($id)
{
if($this->locked === true)
{
trigger_error("Tried to lock mutex $id, but mutex ".session_id()." is already locked and only one locked mutex can exist at the same time", E_USER_ERROR);
}
$this->locked = true;
session_id($id);
session_start();
}
private function releaseLock($id)
{
if($this->locked == false)
{
trigger_error("Mutex $id isn't locked", E_USER_WARNING);
return;
}
if($this->locked == true && $id !== session_id())
{
trigger_error("Tried to unlock mutex $id, but mutex ".session_id()." is currently locked", E_USER_WARNING);
return;
}
if($this->old_session !== "")
{
session_id($this->old_session);
session_start();
}
else
{
session_destroy();
session_write_close();
flush();
}
}
private static function init()
{
if(self::$mutex == null)
{
self::$mutex = new PHP_Mutex();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment