/*++

Copyright (C) 1996-2001 Microsoft Corporation

Module Name:

    MERGERTHROTTLING.H

Abstract:

    CMergerThrottling clas

History:

	30-Nov-00   sanjes    Created.

--*/

#ifndef _MERGERTHROTTLING_H_
#define _MERGERTHROTTLING_H_

// Enables debug messages for additional info.
#ifdef DBG
//#define __DEBUG_MERGER_THROTTLING
#endif

// Defaults - can be overridden from the registry
#define	DEFAULT_THROTTLE_THRESHOLD			10
#define	DEFAULT_THROTTLE_RELEASE_THRESHOLD	4

// Forward Class Definitions
class CInternalMerger;
class CWmiMergerRecord;

//	This class encapsulates the throttling behavior which will be used by the internal
//	merger in order to control delivery of parent and child objects.

class CMergerThrottling
{
	// Following members are used for throttling incoming objects so that our
	// parent and child objects don't get wildly out of control.  Note that we
	// use separate throttling events, since the decision to throttle is made in
	// a critsec, but the actual throttling occurs outside.  This can have the
	// unpleasant side effect of a race condition in which for example, the parent
	// decides to throttle, steps out of the critsec, and a context switch occurs,
	// in which the child gets a large number of objects, releases the throttle, but
	// then causes child throttling to occur, resetting the event.  If the parent
	// thread switches in at this point, we're hosed, since we will now wait on the
	// parent and the child.

	HANDLE	m_hParentThrottlingEvent;
	HANDLE	m_hChildThrottlingEvent;

	DWORD	m_dwNumChildObjects;
	DWORD	m_dwNumParentObjects;
	DWORD	m_dwNumThrottledThreads;

	// Contains the time of the last ping from a parent or child.
	// Used to calculate whether or not we timeout
	DWORD	m_dwLastParentPing;
	DWORD	m_dwLastChildPing;

	// These should NEVER both be TRUE
	bool	m_bParentThrottled;
	bool	m_bChildThrottled;

	// Stop us from throttling if one side or the other is done
	bool	m_bParentDone;
	bool	m_bChildDone;

	// This controls the point where we determine that we need to perform throttling
	// Once parent or children are > m_dwThrottlingThreshold objects apart, one or the
	// other will be throttled
	DWORD	m_dwThrottlingThreshold;

	// This controls the threshold where we will release currently throttled threads
	// Once we are throttled, we will remain throttled until parent or children are <
	// m_dwReleaseThreshold objects out of sync with each other.
	DWORD	m_dwReleaseThreshold;

	// This controls the amount of memory we will allow Indicates to process before
	// forcing them to send objects further down the line
	DWORD	m_dwBatchingThreshold;

	// This controls the timeout value we wait for.  If we timeout and a provider has
	// not pinged us in the specified timeout, then we will cancel the merger with
	// a provider timed out error.
	DWORD m_dwProviderDeliveryTimeout;

	// We will expose this for other synchronization activities
	CCritSec	m_cs;

	// Helper functions to control throttling
	bool ShouldThrottle( bool bParent );
	HRESULT PrepareThrottle( bool bParent, HANDLE* phEvent );
	bool VerifyTimeout( DWORD dwLastTick, long lNumArbThrottledThreads, DWORD* pdwAdjust );

public:

	CMergerThrottling();
	~CMergerThrottling();

	// Two stage initialization
	HRESULT	Initialize( void );

	// Returns TRUE if throttling occurred
	HRESULT Throttle( bool bParent, CWmiMergerRecord* pMergerRecord );

	// Returns TRUE if we released throttled threads.
	bool ReleaseThrottle( bool bForce = false );

	// Informs us that we are in fact, done with Child and/or Parent
	void SetChildrenDone( void );
	void SetParentDone( void );
	void Cancel( void );

	// Helpers to control the number of current parent and child objects
	// which we will then use to make decisions as to whether or not
	// we should block a thread or not
	DWORD AdjustNumParentObjects( long lNumParentObjects )
		{ return ( m_dwNumParentObjects += lNumParentObjects ); }
	DWORD AdjustNumChildObjects( long lNumChildObjects )
		{ return ( m_dwNumChildObjects += lNumChildObjects ); }

	// Access to our critical section
	void Enter( void ) { m_cs.Enter(); }
	void Leave( void ) { m_cs.Leave(); }

	// Adjusts ping times
	DWORD Ping( bool bParent, CWmiMerger* pWmiMerger );

	CCritSec* GetCritSec( void ) { return &m_cs; }

	// Checks batch size against our limit
	bool IsCompleteBatch( long lBatchSize ) { return lBatchSize >= m_dwBatchingThreshold; }


};

#endif