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);
}