Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F78410424
PhutilLock.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Aug 20, 17:00
Size
5 KB
Mime Type
text/x-php
Expires
Thu, Aug 22, 17:00 (2 d)
Engine
blob
Format
Raw Data
Handle
20028801
Attached To
rPHU libphutil
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
{
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 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 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 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 to register.
* @task registry
*/
protected
static
function
registerLock
(
PhutilLock
$lock
)
{
if
(!
self
::
$registeredShutdownFunction
)
{
register_shutdown_function
(
array
(
'PhutilLock'
,
'unlockAll'
));
self
::
$registeredShutdownFunction
=
true
;
}
$name
=
$lock
->
getName
();
if
(
self
::
getLock
(
$name
))
{
throw
new
Exception
(
"Lock '{$name}' is already registered!"
);
}
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 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
(
"Lock '{$name}' has already been locked by this process."
);
}
$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
(
"Lock '{$name} is not locked by this process!"
);
}
$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
();
}
}
}
}
Event Timeline
Log In to Comment