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 |
|
|
|
|