You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
239 lines
8.0 KiB
239 lines
8.0 KiB
//========= Copyright 1996-2005, Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose:
|
|
//=============================================================================//
|
|
#include "stdafx.h"
|
|
#include "scheduledfunction.h"
|
|
|
|
// memdbgon must be the last include file in a .cpp file!!!
|
|
#include "tier0/memdbgon.h"
|
|
|
|
namespace GCSDK
|
|
{
|
|
|
|
|
|
IGCScheduledFunction::~IGCScheduledFunction()
|
|
{
|
|
GScheduledFunctionMgr().Cancel( this );
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
CGlobalScheduledFunction::CGlobalScheduledFunction() :
|
|
m_pfn( NULL )
|
|
{}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CGlobalScheduledFunction::ScheduleMS( func_t pfn, uint32 nDelayMS )
|
|
{
|
|
m_pfn = pfn;
|
|
GScheduledFunctionMgr().ScheduleMS( this, nDelayMS );
|
|
}
|
|
|
|
void CGlobalScheduledFunction::ScheduleSecond( func_t pfn, uint32 nDelaySecond )
|
|
{
|
|
m_pfn = pfn;
|
|
GScheduledFunctionMgr().ScheduleSecond( this, nDelaySecond );
|
|
}
|
|
|
|
void CGlobalScheduledFunction::ScheduleMinute( func_t pfn, uint32 nDelayMinute )
|
|
{
|
|
m_pfn = pfn;
|
|
GScheduledFunctionMgr().ScheduleMinute( this, nDelayMinute );
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CGlobalScheduledFunction::Cancel()
|
|
{
|
|
GScheduledFunctionMgr().Cancel( this );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CGlobalScheduledFunction::OnEvent()
|
|
{
|
|
m_pfn();
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
CScheduledFunctionMgr::CScheduledFunctionMgr()
|
|
{
|
|
const uint32 knSecond = 1000000;
|
|
//frame rate resolution - 30s at 20Hz
|
|
m_Resolutions[ 0 ].Init( m_ScheduleList, 600, k_cMicroSecPerShellFrame );
|
|
//second resolution - 15 minutes
|
|
m_Resolutions[ 1 ].Init( m_ScheduleList, 15 * 60, knSecond );
|
|
//minute resolution - 5 hours
|
|
m_Resolutions[ 2 ].Init( m_ScheduleList, 5 * 60, 60 * knSecond );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CScheduledFunctionMgr::InitStartingTime()
|
|
{
|
|
uint64 nCurrTime = CJobTime::LJobTimeCur();
|
|
for( uint32 nCurrBucket = 0; nCurrBucket < ARRAYSIZE( m_Resolutions ); nCurrBucket++ )
|
|
{
|
|
m_Resolutions[ nCurrBucket ].m_nAbsLastScheduleBucket = m_Resolutions[ nCurrBucket ].GetAbsScheduleBucketIndex( nCurrTime );
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
void CScheduledFunctionMgr::InternalSchedule( uint32 nResolution, IGCScheduledFunction* pEvent, uint32 nMSDelay )
|
|
{
|
|
//if the event is already registered, deregister it, double registration would be a very bad thing
|
|
if( pEvent->BIsScheduled() )
|
|
{
|
|
Cancel( pEvent );
|
|
}
|
|
|
|
//determine which bucket this belongs in
|
|
uint32 nAbsBucket = m_Resolutions[ nResolution ].GetAbsScheduleBucketIndex( CJobTime::LJobTimeCur() + ( uint64 )nMSDelay * 1000 );
|
|
//so we can remove it, and deal with wrapping
|
|
pEvent->m_nAbsScheduleBucket = nAbsBucket;
|
|
|
|
//add it to our list
|
|
uint32 nInsertAfter = m_Resolutions[ nResolution ].m_pBuckets[ nAbsBucket % m_Resolutions[ nResolution ].m_nNumBuckets ];
|
|
pEvent->m_nLLIndex = m_ScheduleList.InsertAfter( nInsertAfter, pEvent );
|
|
}
|
|
|
|
void CScheduledFunctionMgr::ScheduleMS( IGCScheduledFunction* pEvent, uint32 nMSDelay )
|
|
{
|
|
InternalSchedule( 0, pEvent, nMSDelay );
|
|
}
|
|
|
|
void CScheduledFunctionMgr::ScheduleSecond( IGCScheduledFunction* pEvent, uint32 nSDelay )
|
|
{
|
|
InternalSchedule( 1, pEvent, nSDelay * 1000 );
|
|
}
|
|
|
|
void CScheduledFunctionMgr::ScheduleMinute( IGCScheduledFunction* pEvent, uint32 nMinuteDelay )
|
|
{
|
|
InternalSchedule( 2, pEvent, nMinuteDelay * 1000 * 60 );
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CScheduledFunctionMgr::Cancel( IGCScheduledFunction* pEvent )
|
|
{
|
|
//ignore it if not already registered
|
|
if( pEvent->m_nAbsScheduleBucket == IGCScheduledFunction::knInvalidBucket )
|
|
return;
|
|
|
|
if( m_ScheduleList.IsValidIndex( pEvent->m_nLLIndex ) )
|
|
{
|
|
m_ScheduleList.Remove( pEvent->m_nLLIndex );
|
|
}
|
|
else
|
|
{
|
|
AssertMsg( false, "Warning: Ecountered a remove request for a scheduled event but was unable to find it in the bucket that it was supposed to be in" );
|
|
}
|
|
|
|
pEvent->m_nAbsScheduleBucket = IGCScheduledFunction::knInvalidBucket;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CScheduledFunctionMgr::RunFunctions()
|
|
{
|
|
VPROF_BUDGET( "CGCBase::CallScheduledEvents", VPROF_BUDGETGROUP_STEAM );
|
|
|
|
uint64 nCurrTime = CJobTime::LJobTimeCur();
|
|
for( uint32 nCurrBucket = 0; nCurrBucket < ARRAYSIZE( m_Resolutions ); nCurrBucket++ )
|
|
{
|
|
m_Resolutions[ nCurrBucket ].RunFunctions( m_ScheduleList, nCurrTime );
|
|
}
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------
|
|
CScheduledFunctionMgr& GScheduledFunctionMgr()
|
|
{
|
|
static CScheduledFunctionMgr s_Singleton;
|
|
return s_Singleton;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
CScheduledFunctionMgr::CScheduleBucket::CScheduleBucket() :
|
|
m_pBuckets( NULL ),
|
|
m_nNumBuckets( 0 ),
|
|
m_nMicroSPerBucket( 0 ),
|
|
m_nAbsLastScheduleBucket( 0 )
|
|
{}
|
|
|
|
CScheduledFunctionMgr::CScheduleBucket::~CScheduleBucket()
|
|
{
|
|
delete [] m_pBuckets;
|
|
}
|
|
|
|
void CScheduledFunctionMgr::CScheduleBucket::Init( TScheduleList& MasterList, uint32 nNumBuckets, uint32 nMicroSPerBucket )
|
|
{
|
|
//init should never happen more than once
|
|
AssertMsg( !m_pBuckets, "Error: Schedule buckets should never be initialized multiple times" );
|
|
|
|
m_pBuckets = new uint32[ nNumBuckets ];
|
|
m_nNumBuckets = nNumBuckets;
|
|
m_nMicroSPerBucket = nMicroSPerBucket;
|
|
|
|
for( uint32 nCurrBucket = 0; nCurrBucket < nNumBuckets; nCurrBucket++ )
|
|
{
|
|
m_pBuckets[ nCurrBucket ] = MasterList.AddToTail( NULL );
|
|
}
|
|
}
|
|
|
|
void CScheduledFunctionMgr::CScheduleBucket::RunFunctions( TScheduleList& MasterList, uint64 nMicroSTime )
|
|
{
|
|
//determine the absolute bucket index of our current frame that we want to run to
|
|
uint32 nCurrFrameBucket = GetAbsScheduleBucketIndex( nMicroSTime );
|
|
|
|
//note that we include the starting bucket, but not the ending bucket. This addresses two issues: it avoids checking buckets multiple times, and since we may not have completely spent
|
|
//all the time for the frame, events will not fire early
|
|
for( uint32 nAbsBucket = m_nAbsLastScheduleBucket; nAbsBucket < nCurrFrameBucket; nAbsBucket++ )
|
|
{
|
|
//map this to a relative bucket
|
|
uint32 nStartIndex = m_pBuckets[ nAbsBucket % m_nNumBuckets ];
|
|
AssertMsg2( nStartIndex != MasterList.InvalidIndex(), "Error: Detected list corruption when accessing list bucket %d (%d micro s per bucket)", nAbsBucket % m_nNumBuckets, m_nMicroSPerBucket );
|
|
for( uint32 nCurrEvent = MasterList.Next( nStartIndex ); nCurrEvent != MasterList.InvalidIndex(); )
|
|
{
|
|
//make sure to advance to the next item immediately since we'll be removing this element from our list
|
|
uint32 nEventToRemove = nCurrEvent;
|
|
IGCScheduledFunction* pEvent = MasterList[ nCurrEvent ];
|
|
nCurrEvent = MasterList.Next( nCurrEvent );
|
|
|
|
//see if we have hit the end of our bucket
|
|
if( !pEvent )
|
|
{
|
|
break;
|
|
}
|
|
|
|
//skip any event that happens in the future (can occur since we wrap the frame list over itself to map to an actual bucket)
|
|
if( pEvent->m_nAbsScheduleBucket > nAbsBucket )
|
|
{
|
|
//just skip over this element
|
|
continue;
|
|
}
|
|
|
|
//sanity check that this bucket matches exactly. We'll process stale ones, but this warns us that we missed an event and cycled through the bucket, causing it be very delayed
|
|
AssertMsg2( pEvent->m_nAbsScheduleBucket == nAbsBucket, "Warning: Encountered a scheduled event that was intended to be fired on bucket %d but wasn't until bucket %d", pEvent->m_nAbsScheduleBucket, nAbsBucket );
|
|
|
|
//mark the object has having been removed
|
|
pEvent->m_nAbsScheduleBucket = IGCScheduledFunction::knInvalidBucket;
|
|
pEvent->m_nLLIndex = MasterList.InvalidIndex();
|
|
|
|
//remove it from our list
|
|
MasterList.Remove( nEventToRemove );
|
|
|
|
//call into the object
|
|
pEvent->OnEvent();
|
|
//NOTE: You cannot use the object any more, it may have destroyed itself!!!!
|
|
pEvent = NULL;
|
|
}
|
|
}
|
|
|
|
//and update the event range we need to process
|
|
m_nAbsLastScheduleBucket = nCurrFrameBucket;
|
|
}
|
|
|
|
|
|
|
|
} //namespace GCSDK
|
|
|