summaryrefslogtreecommitdiff
path: root/gcsdk/scheduledfunction.cpp
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /gcsdk/scheduledfunction.cpp
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'gcsdk/scheduledfunction.cpp')
-rw-r--r--gcsdk/scheduledfunction.cpp239
1 files changed, 239 insertions, 0 deletions
diff --git a/gcsdk/scheduledfunction.cpp b/gcsdk/scheduledfunction.cpp
new file mode 100644
index 0000000..26bcb85
--- /dev/null
+++ b/gcsdk/scheduledfunction.cpp
@@ -0,0 +1,239 @@
+//========= 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
+