//Sample code file: var/ndk/webBuildengine/tmp/viewable_samples/7999585e-fdab-4e7b-b86c-67d55e862138/nks/CondVar.c

//Warning: This code has been marked up for HTML

/***************************************************************************
$name: CondVar.c 
$version: 1.0 
$date_modified: 082499 
$description: Demonstrates a reasonably interesting of the condition variable.
$owner: NKS Product Manager 
Copyright (c) 1999 Novell, Inc. All Rights Reserved.

THIS WORK IS SUBJECT TO U.S. AND INTERNATIONAL COPYRIGHT LAWS AND TREATIES.
USE AND REDISTRIBUTION OF THIS WORK IS SUBJECT TO THE LICENSE AGREEMENT
ACCOMPANYING THE SOFTWARE DEVELOPMENT KIT (SDK) THAT CONTAINS THIS WORK.
PURSUANT TO THE SDK LICENSE AGREEMENT, NOVELL HEREBY GRANTS TO DEVELOPER A
ROYALTY-FREE, NON-EXCLUSIVE LICENSE TO INCLUDE NOVELL'S SAMPLE CODE IN ITS
PRODUCT. NOVELL GRANTS DEVELOPER WORLDWIDE DISTRIBUTION RIGHTS TO MARKET,
DISTRIBUTE, OR SELL NOVELL'S SAMPLE CODE AS A COMPONENT OF DEVELOPER'S
PRODUCTS. NOVELL SHALL HAVE NO OBLIGATIONS TO DEVELOPER OR DEVELOPER'S
CUSTOMERS WITH RESPECT TO THIS CODE.
****************************************************************************/

/*--------------------------------------------------------------------------
Shown here is a simple producer-consumer example. It can also be called a work
crew.

The producer threads keep "producing" work items. Each producer is assumed to
produce only one work item at a time. The work queue can have a maximum of
MAX_ITEM_COUNT work items outstanding. A producer adding an item must check
for an available "slot" and ONLY then add the work item. If no "slots" are
available, the producer must block until at least one is available.

The work crew has a number of consumer threads (or worker threads) which are
available for processing the work items produced by the producers. The number
of consumer threads can be adjusted for the required concurrency. A consumer
thread picks up only one work item at a time and processes it. If no work item
is available, it must wait until at least one work item is available and pick
the first work item for processing.

Assume functions accomplishing meaningful work according to their names,
ProduceOneWorkItem, ProcessWorkItem, AddWorkItem.
--------------------------------------------------------------------------*/
#include <nks/synch.h>

#ifndef TRUE
# define TRUE   1
#endif
#ifndef FALSE
# define FALSE   0
#endif

#define MAX_ITEM_COUNT   100

typedef struct   _WorkItem
{
   struct _WorkItem   *pNextWorkItem;
   // anything else in here...

} WorkItem_t;

typedef struct
{
   NXMutex_t      *pWorkCrewLock;
   NXCond_t         *pProducerCV;
   NXCond_t         *pConsumerCV;
   WorkItem_t      *pWorkQue;
   unsigned int   ItemCount;
} WorkCrewStruct_t;


WorkCrewStruct_t   gWorkCrew;

extern void   ProduceOneWorkItem( void );
extern void   RemoveOneWorkItem( void );
extern void   AddWorkItem( void );
extern void ProcessWorkItem( void );

void InitWorkCrew(void)
{
   gWorkCrew.pWorkCrewLock   = NXMutexAlloc(0, NULL, NULL);
   gWorkCrew.pProducerCV   = NXCondAlloc(NULL);
   gWorkCrew.pConsumerCV   = NXCondAlloc(NULL);
   gWorkCrew.pWorkQue      = NULL;
   gWorkCrew.ItemCount      = 0;
}

void ProducerThreadRoutine( void )
{
   for (;;)
   {
      ProduceOneWorkItem();

/*
** We will now add it to the work queue for processing by the consumers (aka
** worker threads). However, we need to check for a free "slot" in the work
** queue. If there is no free slot, we need to wait until there is one avail-
** able. Note that when we actually wake up, the free slot may be have been
** used up by another producer. This is why the while loop is used.
*/
      NXLock(gWorkCrew.pWorkCrewLock);

      while (gWorkCrew.ItemCount == MAX_ITEM_COUNT)
         NXCondWait(gWorkCrew.pProducerCV, gWorkCrew.pWorkCrewLock);

/*
** There is at least one slot available in the work item queue. Add the work
** item to the queue.
*/
      AddWorkItem();

      gWorkCrew.ItemCount++;
      NXUnlock(gWorkCrew.pWorkCrewLock);

/*
** Wakeup ONE consumer. Note that we produce only one work item here. If we
** produce multiple work items at a time, we may choose to call NXCondBroad-
** cast().
*/
      NXCondSignal(gWorkCrew.pConsumerCV);
   }
}

void ConsumerThreadRoutine( void )
{
   for (;;)
   {
      int   needToSignalFlag;

/*
** Attempt to consume one work item. If nothing is available, block until one
** is available.
*/
      NXLock(gWorkCrew.pWorkCrewLock);

      while (gWorkCrew.ItemCount == 0)
         NXCondWait(gWorkCrew.pConsumerCV, gWorkCrew.pWorkCrewLock);

/*
** There is at least one work item available. Pick up the first work item.
*/
      RemoveOneWorkItem();

/*
** Producers would be waiting only if the item count was the max. We could as
** well blindly do a signal here without checking the count etc. Note that when
** a producer actually wakes up, the free slot may be have been used up by
** another producer. This is why the while() loop is used.
*/

      needToSignalFlag = FALSE;

      if (gWorkCrew.ItemCount-- == MAX_ITEM_COUNT)
         needToSignalFlag = TRUE;

      NXUnlock(gWorkCrew.pWorkCrewLock);

/*
** Wake up one producer. Note that we consume only one work item at a time. If
** we had consumed multiple work items at a time, we would have called NXCond-
** Broadcast() here.
*/
      if (needToSignalFlag)
         NXCondSignal(gWorkCrew.pProducerCV);

      ProcessWorkItem();
   }
}