Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to properly use PHP5 semaphores?

Tags:

php

semaphore

I have this function that tries to read some values from cache. But if value does not exists it should call alternative source API and save new value into the cache. However, server is very overloaded and almost each time when value does not exists more then one requests are created (a lot of API calls) and each of them will store new vale into cache. However, what I want is to be able to call API many times, but only one process/request to be able to store it in cache:

function fetch_cache($key, $alternativeSource) {
    $redis = new Redis();
    $redis->pconnect(ENV_REDIS_HOST);
    $value = $redis->get($key);

    if( $value === NULL ) {
        $value = file_get_contents($alternativeSource);

        // here goes part that I need help with
        $semaphore = sem_get(6000, 1); // does this need to be called each time this function is called?
        if( $semaphore === FALSE ) {
            // This means I have failed to create semaphore?
        }

        if( sem_aquire($semaphore, true) ) {
            // we have aquired semaphore so here
            $redis->set($key, $value);
            sem_release($semaphore); // releasing lock
        }

        // This must be call because I have called sem_get()?
        sem_remove($semaphore);
    }

    return $value;
}

Is this proper use of semaphore in PHP5?

like image 474
clzola Avatar asked Oct 11 '16 12:10

clzola


1 Answers

Short answer

  1. You don't need to create and remove semaphores within the fetch_cache function. Put sem_get() into an initialization method (such as __construct).
  2. 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.
  3. Use sem_acquire() to acquire locks, and sem_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_USAGEsemaphore (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() will 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.

like image 123
Ruslan Osmanov Avatar answered Sep 20 '22 12:09

Ruslan Osmanov