How to properly use PHP5 semaphores?
Short answer
- You don't need to create and remove semaphores within the
fetch_cache
function. Putsem_get()
into an initialization method (such as__construct
). - You should remove semaphores with
sem_remove()
, but in a cleanup method (such as__destruct
). Or, you might want to keep them even longer - depends on the logic of your application. - Use
sem_acquire()
to acquire locks, andsem_release()
to release them.
Description
sem_get()
Creates a set of three semaphores.
The underlying C function semget
is not atomic. There is a possibility of race condition when two processes trying to call semget
. Therefore, semget
should be called in some initialization process. The PHP extension overcomes this issue by means of three semaphores:
Semaphore 0 a.k.a. SYSVSEM_SEM
Is initialized to sem_get
's $max_acquire
and decremented as processes acquires it.
The first process that called sem_get
fetches the value of SYSVSEM_USAGE
semaphore (see below). For the first process, it equals to 1
, because the extension sets it to 1
with atomic semop
function right after semget
. And if this is really the first process, the extension assigns SYSVSEM_SEM
semaphore value to $max_acquire
.
Semaphore 1 a.k.a. SYSVSEM_USAGE
The number of processes using the semaphore.
Semaphore 2 a.k.a. SYSVSEM_SETVAL
Plays a role of a lock for internal SETVAL
and GETVAL
operations (see man 2 semctl
). For example, it is set to 1
while the extension sets SYSVSEM_SEM
to $max_acquire
, then is reset back to zero.
Finally, sem_get
wraps a structure (containing the semaphore set ID, key and other information) into a PHP resource and returns it.
So you should call it in some initialization process, when you're only preparing to work with semaphores.
sem_acquire()
This is where the $max_acquire
goes into play.
SYSVSEM_SEM
's value (let's call it semval
) is initially equal to $max_acquire
. semop()
blocks until semval
becomes greater than or equal to 1
. Then 1
is substracted from semval
.
If $max_acquire = 1
, then semval
becomes zero after the first call, and the next calls to sem_acquire()
fill block until semval
is restored by sem_release()
call.
Call it when you need to acquire the next "lock" from the available set ($max_acquire
).
sem_release()
Does pretty much the same as sem_acquire()
, except it increments SYSVSEM_SEM
's value.
Call it when you need to no longer need the "lock" acquired previously with sem_acquire()
.
sem_remove()
Immediately removes the semaphore set, awakening allprocesses blocked in semop
on the set (from IPC_RMID
section, SEMCTL(2) man page).
So this is effectively the same as removing a semaphore with ipcrm
command.