System V Semaphores
and Semaphore Arrays


A System V semaphore is an object that manages one or more shared resources. Unlike most synchronization classes, System V semaphores and semaphore arrays can be used between processes. System V semaphores and semaphore arrays are primarily used for synchronization of resources shared by multiple processes.

This section covers System V semaphores and System V semaphore arrays. Several examples of each are provided.

If two processes try to create and initialize a semaphore or array with the same key, one succeeds and the other attaches to the existing semaphore or array without altering the initial value. Platform<ToolKit> for UNIX ensures that no race conditions occur and that creation and initialization occur atomically.

This section includes several examples of working with System V semaphores and semaphore arrays.

Obtaining and Releasing Multiple Resources

You can obtain or release semaphores in arbitrary quantities using obtain() and release() . A process uses obtain() to obtain a specified number of resources from a semaphore. If the counter is greater than or equal to the requested number, the counter decrements by the number requested. The function returns immediately. However, if the counter is less than the requested number, the process suspends until the specified number of resources become available.

A process uses release() to release a specified number of resources to a semaphore. The counter increments by the requested number and the function returns immediately.

In the following example, both the parent and child processes try to obtain a single resource. Because the parent process obtains the resource first, the resource is blocked from the child until the parent releases the resource.

Example <ospace/unix/examples/sysvsem1.cpp>
#include <iostream>
#include <ospace/unix.h>

int
function()
  {
  os_sysv_semaphore semaphore( os_key( 10 ) );
  cout << "Child is trying to obtain resource..." << endl;
  semaphore.obtain (1);
  cout << "Child obtains resource!" << endl;
  return 0;
  }

int
main()
  {
  os_unix_toolkit initialize;

  // Create with initial value 1.
  os_sysv_semaphore semaphore( os_key( 10 ), O_CREAT, 1, 0600 );

  cout << "Initial semaphore value = " << semaphore.value() << endl;
  cout << "Parent is trying to obtain resource..." << endl;
  semaphore.obtain( 1 );

  cout << "Parent obtains resource!" << endl;
  os_process child( function ); // Spawn child.

  cout << "Sleeping for one second" << endl;
  os_this_process::sleep (1);

  cout << "Parent is releasing resource" << endl;
  semaphore.release( 1 );
  os_this_process::wait_for_child( child );

  cout << "Removing semaphore" << endl;
  semaphore.remove();
  return 0;
  }

Initial semaphore value = 1
Parent is trying to obtain resource...
Parent obtains resource!
Child is trying to obtain resource...
Sleeping for one second
Parent is releasing resource
Child obtains resource!
Removing semaphore

Alternative Acquisition Interfaces

Several inline member functions are available that behave in the same manner as obtain(1) , and release(1) . These function equivalents are more descriptive than the terms "obtain" and "release" and better illustrate common semaphore use cases. Using these function equivalents, a programmer can write code that is easier to read and understand.

The following functions are identical in meaning to obtain(1) .

The following functions are identical in meaning to release(1) .

Obtaining and Releasing a Single Resource

In the following example, the parent process creates a semaphore with initial value zero and then attempts to obtain two resources. The parent process is blocked until both children processes release a resource.

Example <ospace/unix/examples/sysvsem2.cpp>
#include <iostream>
#include <ospace/unix.h>

int
function()
  {
  os_sysv_semaphore semaphore( os_key( 10 ) );
  os_this_process::sleep( 1 );
  cout << "Signaling semaphore..." << endl;
  semaphore.signal();
  return 0;
  }

int
main()
  {
  os_unix_toolkit initialize;

  os_sysv_semaphore semaphore( os_key( 10 ), O_CREAT, 0, 0600 );
  os_process child1( function );
  os_process child2( function );

  cout << "Waiting for both children to signal the semaphore..."
    << endl;
  semaphore.wait(); // Wait for one signal.
  semaphore.wait(); // Wait for one signal.

  cout << "Both children signaled the semaphore..." << endl;
  semaphore.remove();
  return 0;
  }

Waiting for both children to signal the semaphore...
Signaling semaphore...
Signaling semaphore...
Both children signaled the semaphore...

Undoing an Obtain or Release of Multiple Resources

Use the O_UNDO flag to classify obtain() or release() as a process that can be undone. Using this flag helps avoid problems when a process abnormally terminates after obtaining a resource. If you use an O_UNDO flag for an obtain() operation, you must use an O_UNDO flag on the corresponding release() operation.

The O_UNDO flag automatically reverses any operation that is not completed when a process terminates. For example, if a resource was obtained, it is released. Similarly, if a resource was released, it is obtained.

In the following example, two child processes compete for a single resource. The first child obtains the resource and then terminates before releasing it. Because the obtain() process uses the O_UNDO flag, the resource is released so the second child can obtain it.

Example <ospace/unix/examples/sysvsem3.cpp>
#include <iostream>
#include <ospace/unix.h>

int
child1()
  {
  cout << "child1:" << endl;
  os_sysv_semaphore semaphore( os_key( 10 ) );
  semaphore.obtain( 1, O_UNDO );

  cout << "  obtained semaphore, now terminating..." << endl;
  os_this_process::terminate(); // Will release resource at this point.
  return 0;
  }

int
child2()
  {
  cout << "child2:" << endl;
  os_sysv_semaphore semaphore( os_key( 10 ) );
  semaphore.obtain( 1, O_UNDO );

  cout << "  obtained semaphore, now releasing..." << endl;
  semaphore.release( 1, O_UNDO );
  return 0;
  }

int
main()
  {
  os_unix_toolkit initialize;

  os_sysv_semaphore semaphore( os_key( 10 ), O_CREAT, 1, 0600 );
  cout << "Begin: semaphore's initial value is ";
  cout << semaphore.value() << endl;

  os_process child_process1( child1 );
  os_this_process::wait_for_child( child_process1 );
  os_process child_process2( child2 );
  os_this_process::wait_for_child( child_process2 );

  cout << "Done: semaphore's final value is ";
  cout << semaphore.value() << endl;
  semaphore.remove();
  return 0;
  }

Begin: semaphore's initial value is 1
child1:
  obtained semaphore, now terminating...
child2:
  obtained semaphore, now releasing...
Done: semaphore's final value is 1

Creating a Non-Blocking Operation

Use the O_NONBLOCK option to specify an operation as non-blocking. If an operation results in a block, the operation throws an os_unix_toolkit_error and returns immediately.

Example <ospace/unix/examples/sysvsem4.cpp>
#include <ospace/std/iostream>
#include <ospace/unix.h>
#include <sys/errno.h>

int
child()
  {
  os_sysv_semaphore semaphore( os_key( 40 ) );

  bool waiting = true;
  while( waiting )
    {
    try
      {
      cout << "Obtaining..." << endl;
      semaphore.wait( O_NONBLOCK );
      waiting = false;
      }
    catch ( os_unix_toolkit_error& error )
      {
      if ( error.native() != EAGAIN )
        throw error;
      cout << "Failed: sleeping..." << endl;
      os_this_process::sleep (1);
      }
    }
  cout << "Succeeded: DONE" << endl;
  return 0;
  }

int
main()
  {
  os_unix_toolkit init_unix;

  try
    {
    os_sysv_semaphore semaphore( os_key( 40 ), O_CREAT, 0, 0600 );
    os_process child_process( child );
    os_this_process::sleep( 3 );
    semaphore.signal();
    os_this_process::wait_for_child( child_process );
    semaphore.remove();
    }
  catch ( os_unix_toolkit_error& error )
    {
    cout << "Caught exception:\n\t " << error << endl;
    }
  return 0;
  }

Obtaining...
Failed: sleeping...
Obtaining...
Failed: sleeping...
Obtaining...
Failed: sleeping...
Obtaining...
Failed: sleeping...
Obtaining...
Succeeded: DONE

To specify an operation that both is non-blocking and can be undone, bitwise OR O_NONBLOCK and O_UNDO together.

Performing Multiple Semaphore Operations Atomically

In the following example, the parent process creates an array of two semaphores. The parent process then sets each semaphore to an initial value and obtains the resource of semaphore(1) . The parent process then spawns two child processes to execute functions child1 , and child2 .

The first child process attempts to atomically obtain both semaphores and blocks because semaphore(1) is held by the parent. Since a child's request is atomically executed, neither semaphore is altered until both can be obtained.

The second child process obtains the resource of semaphore(0) and then releases it. When the parent finally releases the resource of semaphore(1) , the atomic request of the first child process succeeds.

Example <ospace/unix/examples/semarr1.cpp>
#include <iostream>
#include <ospace/unix.h>

int
child1()
  {
  os_sysv_semaphore_array sem_array( os_key( 20 ) );
  sem_array.record( true ); // Place semaphore array into record mode.
  sem_array.semaphore( 0 ).obtain();
  sem_array.semaphore( 1 ).obtain();
  cout << "child1: attempt to obtain resources 0 and 1" << endl;
  sem_array.execute(); // Execute recorded functions as atomic action.
  sem_array.record( false );
  cout << "child1: obtained resources 0 and 1" << endl;
  sem_array.semaphore( 0 ).release(); // Release resources.
  sem_array.semaphore( 1 ).release();
  return 0;
  }

int
child2()
  {
  os_sysv_semaphore_array sem_array( os_key( 20 ) );
  cout << "child2: attempt to obtain resource 0" << endl;
  sem_array.semaphore( 0 ).obtain();
  cout << "child2: obtained resource 0" << endl;
  sem_array.semaphore( 0 ).release();
  cout << "child2: released resource 0" << endl;
  return 0;
  }

int
main()
  {
  os_unix_toolkit initialize;

  // Create array of two semaphores.
  os_sysv_semaphore_array sem_array( os_key( 20 ), O_CREAT, 2, 0600 );
  sem_array.semaphore( 0 ).value( 1 ); // Set initial value.
  sem_array.semaphore( 1 ).value( 1 ); // Set initial value.

  // Prevent child1 from obtaining both.
  sem_array.semaphore( 1 ).obtain( 1 );
  cout << "parent: obtained resource 1" << endl;
  os_process child_process1( child1 );
  os_this_process::sleep( 1 );
  os_process child_process2( child2 );
  cout << "parent: waiting for child2, then releasing resource 1 ...";
  cout << endl;
  os_this_process::wait_for_child( child_process2 );
  sem_array.semaphore( 1 ).release( 1 ); // Allow child1 to obtain both.
  os_this_process::wait_for_child( child_process1 );
  sem_array.remove(); // Remove semaphore array from system.
  return 0;
  }

parent: obtained resource 1
child1: attempt to obtain resources 0 and 1
child2: attempt to obtain resource 0
child2: obtained resource 0
child2: released resource 0
parent: waiting for child2, then releasing resource 1 ...
child1: obtained resources 0 and 1

Reporting Semaphore Status

The following example sends several access messages to an os_semaphore_array and obtains a variety of information.

Example <ospace/unix/examples/semarr2.cpp>
#include <iostream>
#include <ospace/security.h>
#include <ospace/unix.h>

int
main()
  {
  os_security_toolkit init_security;
  os_unix_toolkit init_unix;
  os_sysv_semaphore_array sem( os_key( 20 ), O_CREAT, 2, 0600 );

  cout << sem << endl;
  cout << "  size:             " << sem.size() << endl;
  cout << "  last_op_time:     " << sem.last_op_time() << endl;
  cout << "  last_change_time: " << sem.last_change_time() << endl;
  cout << "  owner_user:       " << os_user( sem.owner_user() ) << endl;
  cout << "  owner_group:      " << os_group( sem.owner_group() );
  cout << endl;
  cout << "  creator_user:     " << os_user( sem.creator_user() );
  cout << endl;
  cout << "  creator_group:    " << os_group( sem.creator_group() );
  cout << endl;
  cout << "  mode:             " << os_mode( sem.mode() ) << endl;
  cout << "  key:              " << sem.key() << endl;

  sem.remove();
  return 0;
  }

os_sysv_semaphore_array( 917504 )
  size:             2
  last_op_time:     Wed Dec 31 18:00:00 1969
  last_change_time: Wed Sep 27 15:49:42 1995
  owner_user:       os_user( tarrman, 105 )
  owner_group:      os_group( cavemen, 21 )
  creator_user:     os_user( tarrman, 105 )
  creator_group:    os_group( cavemen, 21 )
  mode:             -rw-------
  key:              os_key( 20 )

Copyright©1994-2026 Recursion Software LLC
All Rights Reserved - For use by licensed users only.