Categories
PHP Programming

PHP Dark Arts: Semaphores

Note:  The full example code can be downloaded here.

Dijkstra contributed many important things to computer science, and among them was the semaphore.  A semaphore is a protected variable or abstract data type that is used for controlling access to some resource.  Semaphores can be used to control access to almost anything.  For example, lets say that I have multiple instances of a program running and this program counts how many rows are in table ‘X’ every 15 seconds.  We don’t want the program instances clobbering the database all at once, so we use a semaphore to control the access (I realize this is a silly idea, but it’s to illustrate a point).  Before a program instance can access the database, it must first acquire the semaphore.  Once acquired, it may run it’s query, and then release the semaphore so another program instance can use it.  In this way, access to the database is strictly controlled.

Semaphores have drawbacks of course.  The most obvious is that it’s easy to enter a state of deadlock.  Let’s say in the previous example, one program instance never releases the semaphore.  What happens then?  Everything comes to a screeching halt.  That’s why it’s important to be extremely careful when using a semaphore.  If you acquire it, be sure to release it.

PHP’s Semaphore Implementation

Using semaphores in PHP is actually very straight forward.  There are only 4 semaphore functions:

  • sem_acquire() – Attempt to acquire control of a semaphore.
  • sem_get() – Creates (or gets if already present) a semaphore.
  • sem_release() – Releases the a semaphore if it is already acquired.
  • sem_remove() – Removes (deletes) a semaphore.

So how do they all work together?  First, you call sem_get() to fetch the identifier for the semaphore.  After that, one of your processes will call sem_acquire() to try and acquire the semaphore.  If it’s currently unavailable, sem_acquire() will block until the semaphore is released by another process.  Once the semaphore is acquired, you may access the resource that you are controlling with it.  After you are done with the resource, call sem_release() so that another process can acquire the semaphore.  When all is said and done, and you’ve made sure that none of your processes require the semaphore anymore, you can call sem_remove() to remove the semaphore completely.

Getting and Removing

The first step in using a semaphore is calling sem_get().

1
2
3
4
5
6
$key = 123321;
$maxAcquire = 1;
$permissions =0666;
$autoRelease = 1;
 
$semaphore = sem_get($key, $maxAcquire, $permissions, $autoRelease);

The parameters for this function are pretty straight forward, but here is a break down just in case.

  • $key – A unique integer so that the semaphore is easily identifiable.
  • $maxAcquire – How many process can acquire the semaphore at once?
  • $permissionsUnix style permissions on the semaphore.
  • $autoRelease – Do you want the semaphore to release automatically if the request shuts down?

Each process needs to call sem_get() using the same parameters, otherwise it will get a different semaphore.  And if you want to remove your semaphore?  Just make sure you have a valid semaphore resource and then call sem_remove().

1
2
3
4
5
if(sem_remove($semaphore)) {
     echo "Semaphore removed. \n";
} else {
     echo "Failed to remove semaphore. \n";
}

Acquiring and Releasing

Once you’ve created (or retrieved) a semaphore, you can then start using it by calling sem_acquire().  Both sem_acquire() and sem_release() only take one parameter: a semaphore resource.

1
2
3
4
$semaphore = sem_get($key, $maxAcquire, $permissions, $autoRelease);
sem_acquire($semaphore);  //blocking
echo "hello world!";
sem_release($semaphore);

Now that you understand how to use PHP’s implementation of semaphores, we should try them out fully.

Controlling Access to Standard Input (STDIN) Using Semaphores

When learning about semaphores, a classic example is to control access to standard input.  There honestly isn’t much to the example, so I’ll just give you a quick breakdown of what happens and then let the code do the talking.

  1. Set the semaphore properties.
  2. Get the semaphore.
  3. Start a loop, and try to acquire the semaphore.
  4. Once acquired, access standard input.
  5. Once input is received store it, then release the semaphore
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//Semaphore properties
$key = 123456;
$max = 1;
$permissions = 0666;
$autoRelease = 1;
 
//Open a new or get an existing semaphore
$semaphore = sem_get($key, $max, $permissions, $autoRelease);
if(!$semaphore) {
     echo "Failed on sem_get().\n";
     exit;
}
 
//Try to aquire the semaphore.
for($i = 0; $i < 2; $i++) {
     echo "\nAttempting to acquire semaphore...\n";
     sem_acquire($semaphore);
 
     echo "Aquired.\n";
     echo "Enter some text: ";
     $handler = fopen("php://stdin", "r");
     $text = fgets($handler);
 
     fclose($handler);
     sem_release($semaphore);
 
     echo "Got: $text \n";
}

Running the Example


First, you should download the example here.  After that open up two terminal windows and run the following in each:

{code} php semaphore.php {/code}

Did you like this article?  You’ll probably like these other PHP Dark Arts articles too.