Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2680352
PhutilLock.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
5 KB
Referenced Files
None
Subscribers
None
PhutilLock.php
View Options
<?php
/**
* Base class for locks, like file locks.
*
* libphutil provides a concrete lock in @{class:PhutilFileLock}.
*
* $lock->lock();
* do_contentious_things();
* $lock->unlock();
*
* If the lock can't be acquired because it is already held,
* @{class:PhutilLockException} is thrown. Other exceptions indicate
* permanent failure unrelated to locking.
*
* When extending this class, you should call @{method:getLock} to look up
* an existing lock object, and @{method:registerLock} when objects are
* constructed to register for automatic unlock on shutdown.
*
* @task impl Lock Implementation
* @task registry Lock Registry
* @task construct Constructing Locks
* @task status Determining Lock Status
* @task lock Locking
* @task internal Internals
*/
abstract
class
PhutilLock
extends
Phobject
{
private
static
$registeredShutdownFunction
=
false
;
private
static
$locks
=
array
(
)
;
private
$locked
=
false
;
private
$profilerID
;
private
$name
;
/* -( Constructing Locks )------------------------------------------------- */
/**
* Build a new lock, given a lock name. The name should be globally unique
* across all locks.
*
* @param string $name Globally unique lock name.
* @task construct
*/
protected
function
__construct
(
$name
)
{
$this
->
name
=
$name
;
}
/* -( Lock Implementation )------------------------------------------------ */
/**
* Acquires the lock, or throws @{class:PhutilLockException} if it fails.
*
* @param float $wait Seconds to block waiting for the lock.
* @return void
* @task impl
*/
abstract
protected
function
doLock
(
$wait
)
;
/**
* Releases the lock.
*
* @return void
* @task impl
*/
abstract
protected
function
doUnlock
(
)
;
/* -( Lock Registry )------------------------------------------------------ */
/**
* Returns a globally unique name for this lock.
*
* @return string Globally unique lock name, across all locks.
* @task registry
*/
final
public
function
getName
(
)
{
return
$this
->
name
;
}
/**
* Get a named lock, if it has been registered.
*
* @param string $name Lock name.
* @task registry
*/
protected
static
function
getLock
(
$name
)
{
return
idx
(
self
::
$locks
,
$name
)
;
}
/**
* Register a lock for cleanup when the process exits.
*
* @param PhutilLock $lock Lock to register.
* @task registry
*/
protected
static
function
registerLock
(
PhutilLock
$lock
)
{
if
(
!
self
::
$registeredShutdownFunction
)
{
register_shutdown_function
(
array
(
__CLASS__
,
'unlockAll'
)
)
;
self
::
$registeredShutdownFunction
=
true
;
}
$name
=
$lock
->
getName
(
)
;
if
(
self
::
getLock
(
$name
)
)
{
throw
new
Exception
(
pht
(
"Lock '%s' is already registered!"
,
$name
)
)
;
}
self
::
$locks
[
$name
]
=
$lock
;
}
/* -( Determining Lock Status )-------------------------------------------- */
/**
* Determine if the lock is currently held.
*
* @return bool True if the lock is held.
*
* @task status
*/
final
public
function
isLocked
(
)
{
return
$this
->
locked
;
}
/* -( Locking )------------------------------------------------------------ */
/**
* Acquire the lock. If lock acquisition fails because the lock is held by
* another process, throws @{class:PhutilLockException}. Other exceptions
* indicate that lock acquisition has failed for reasons unrelated to locking.
*
* If the lock is already held by this process, this method throws. You can
* test the lock status with @{method:isLocked}.
*
* @param float $wait (optional) Seconds to block waiting for the lock. By
* default, do not block.
* @return $this
*
* @task lock
*/
final
public
function
lock
(
$wait
=
0
)
{
if
(
$this
->
locked
)
{
$name
=
$this
->
getName
(
)
;
throw
new
Exception
(
pht
(
"Lock '%s' has already been locked by this process."
,
$name
)
)
;
}
$profiler
=
PhutilServiceProfiler
::
getInstance
(
)
;
$profiler_id
=
$profiler
->
beginServiceCall
(
array
(
'type'
=>
'lock'
,
'name'
=>
$this
->
getName
(
)
,
)
)
;
try
{
$this
->
doLock
(
(float)
$wait
)
;
}
catch
(
Exception
$ex
)
{
$profiler
->
endServiceCall
(
$profiler_id
,
array
(
'lock'
=>
false
,
)
)
;
throw
$ex
;
}
$this
->
profilerID
=
$profiler_id
;
$this
->
locked
=
true
;
return
$this
;
}
/**
* Release the lock. Throws an exception on failure, e.g. if the lock is not
* currently held.
*
* @return $this
*
* @task lock
*/
final
public
function
unlock
(
)
{
if
(
!
$this
->
locked
)
{
$name
=
$this
->
getName
(
)
;
throw
new
Exception
(
pht
(
"Lock '%s' is not locked by this process!"
,
$name
)
)
;
}
$this
->
doUnlock
(
)
;
$profiler
=
PhutilServiceProfiler
::
getInstance
(
)
;
$profiler
->
endServiceCall
(
$this
->
profilerID
,
array
(
'lock'
=>
true
,
)
)
;
$this
->
profilerID
=
null
;
$this
->
locked
=
false
;
return
$this
;
}
/* -( Internals )---------------------------------------------------------- */
/**
* On shutdown, we release all the locks. You should not call this method
* directly. Use @{method:unlock} to release individual locks.
*
* @return void
*
* @task internal
*/
public
static
function
unlockAll
(
)
{
foreach
(
self
::
$locks
as
$key
=>
$lock
)
{
if
(
$lock
->
locked
)
{
$lock
->
unlock
(
)
;
}
}
}
}
File Metadata
Details
Attached
Mime Type
text/x-php
Expires
Thu, Dec 19, 17:00 (22 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1014827
Default Alt Text
PhutilLock.php (5 KB)
Attached To
Mode
rARC Arcanist
Attached
Detach File
Event Timeline
Log In to Comment