local/semaphore.c

///////////////////////////////////////////////////////////////////////////////
// Filename: semaphore.c
///////////////////////////////////////////////////////////////////////////////
// Purpose: show how UNIX semaphores work
///////////////////////////////////////////////////////////////////////////////
// History:
// ========
//
// Date     Time     Name      Description   
// -------- -------- --------  ------------------------------------------------
// 96/02/06 00:32:54 muellerg: created
//
///////////////////////////////////////////////////////////////////////////////


// Feature test switches ///////////////////////////// Feature test switches //
    /* NONE */



// System headers /////////////////////////////////////////// System headers //

#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
 


// Local headers ///////////////////////////////////////////// Local headers //

#include "../common.h"



// Macros /////////////////////////////////////////////////////////// Macros //
    /* NONE */



// File scope objects /////////////////////////////////// File scope objects //

const int N = 10;               // empty buffer slots at the beginning

const int produceitems = 100;   // how much items to produce altogether

const int SEM_MODE = 0600;      // only processes of same user have access to
                                // semaphores

// External variables, functions, and classes ///////////// External objects //
    /* NONE */



// Signal catching functions ///////////////////// Signal catching functions //
    /* NONE */



// Structures, unions, and class definitions /////////////////// Definitions //
    /* NONE */



// Functions and class implementation /// Functions and class implementation //

/*
 * do a "P" or "down" operation on the given semaphore
 */

void down(int semid)
{
    // initialize values

    struct sembuf op;
    op.sem_num =  0;         // member number (we only have number 0)
    op.sem_op  = -1;         // dec by one
    op.sem_flg =  0;         // no flags

    if( semop(semid, &op, 1) == -1)
        error.system("semop error");
}


/*
 * do a "V" or "UP" operation on the given semaphore
 */

void up(int semid)
{
    // initialize values

    struct sembuf op;
    op.sem_num = 0;         // member number (we only have number 0)
    op.sem_op  = 1;         // inc by one
    op.sem_flg = 0;         // no flags

    if( semop(semid, &op, 1) == -1)
        error.system("semop error");
}



// Main /////////////////////////////////////////////////////////////// Main //

/* This example program implements a solution for the producer/consumer
 * problem using semaphores. The principle idea of this code is from
 * [Tan92, page 43]. The code only shows the synchronization, not
 * the exchange of data, as this complicates the example. Shared memory,
 * files, or other IPC facilities might be used to actually transfer 
 * the produced "items".
 */

int
main(int argc, char *argv[])
{
    error.set_program_name(argv[0]);    

    // semaphore ids

    int mutexid;    // controls access to critical region   
    int emptyid;    // counts empty buffer slots
    int fullid;     // counts full buffer slots

    // create semaphores

    mutexid = semget(IPC_PRIVATE, 1, SEM_MODE);
    emptyid = semget(IPC_PRIVATE, 1, SEM_MODE);
    fullid  = semget(IPC_PRIVATE, 1, SEM_MODE);

    if( (mutexid == -1) || (emptyid == -1) || (fullid == -1))
        error.system("semget error");

    // initialise semaphores:
    // mutex = 1
    // empty = N
    // full = 0

#ifdef linux
    semun initial_value;
#else
    union semun {
        int val;
        struct semid_ds *buf;
        ushort *array;
    } initial_value;
#endif

    // mutexid

    initial_value.val = 1;
    if( semctl(mutexid, 0, SETVAL, initial_value) == -1)
        error.system("semctl error");


    // emptyid

    initial_value.val = N;
    if( semctl(emptyid, 0, SETVAL, initial_value) == -1)
        error.system("semctl error");

    // fullid

    initial_value.val = 0;
    if( semctl(fullid, 0, SETVAL, initial_value) == -1)
        error.system("semctl error");


    // use two processes for producer and consumer

    pid_t child;

    if( (child = fork() ) < 0)
        error.system("fork error");
    
    if(child)   
    {
        // child acts as producer
    
        for(int i=1; i <= produceitems; i++)
        {
            // object item

            // produce new item
            // produce(&item)       
            cout << "producer: item " << i << " produced" << endl << flush;
    
            // decrement empty count
            down(emptyid);
            
            //  enter critical region
            down(mutexid);

            // put item into buffer         
            // enter(item)
            cout << "producer: item " << i << " put into buffer" 
                 << endl << flush;

            // leave critical region            
            up(mutexid);

            // increment count of full slots
            up(fullid);
        }       
    }
    else
    {
        // parent acts as consumer

        // expect produceitems to be generated

        for(int i=1; i <= produceitems; i++)
        {
            // decrement full count
            down(fullid);
            
            //  enter critical region
            down(mutexid);

            // remove item from buffer
            cout << "consumer: item " << i << " removed from buffer" 
                 << endl << flush;

            // leave critical region            
            up(mutexid);

            // increment count of empty slots
            up(emptyid);

            // do something with the item
            // consume(item)
            cout << "consumer: item " << i << " consumed" << endl << flush;
        }       

        // destroy semaphores after last access to semaphores

#ifdef linux
        union semun nop;
#else
        int nop=0;
#endif

        if(semctl(mutexid, 0, IPC_RMID, nop) == -1)
            error.system("semctl error");

        if(semctl(fullid, 0, IPC_RMID, nop) == -1)
            error.system("semctl error");

        if(semctl(emptyid, 0, IPC_RMID, nop) == -1)
            error.system("semctl error");
    }

    return(EXIT_SUCCESS);
}