Initial commit

This commit is contained in:
2025-12-17 16:47:48 +00:00
commit 13813f3363
4964 changed files with 1079753 additions and 0 deletions

View File

@@ -0,0 +1,358 @@
#pragma once
#include <set>
#include <vector>
#include "RunningAverage.hpp"
#include "Declarations.hpp"
#include "Debug.hpp"
#include "threadsafe.hpp"
#include "boost.hpp"
#include "CEvent.hpp"
#include "atomic.hpp"
#include "boost/thread.hpp"
#include "boost/function.hpp"
#include "boost/shared_ptr.hpp"
#include "boost/scoped_ptr.hpp"
#include "boost/noncopyable.hpp"
#include "boost/intrusive/list.hpp"
#ifdef _WIN32
#undef min
#undef max
#endif
LOGGROUP(TaskSchedulerInit)
LOGGROUP(TaskSchedulerRun)
LOGGROUP(TaskSchedulerFindJob)
namespace Aya
{
/// A singleton object responsible for scheduling the execution TaskScheduler.Jobs.
class TaskScheduler
{
struct SleepingTag;
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<SleepingTag>> SleepingHook;
struct WaitingTag;
typedef boost::intrusive::list_base_hook<boost::intrusive::tag<WaitingTag>> WaitingHook;
public:
class Thread;
typedef std::vector<boost::shared_ptr<Thread>> Threads;
class Job;
bool DataModel30fpsThrottle;
Time lastCyclcTimestamp;
bool cyclicExecutiveWaitForNextFrame;
int nonCyclicJobsToDo;
int cyclicExecutiveLoopId;
class AyaBaseClass Arbiter
{
protected:
ActivityMeter<2> activityMeter;
public:
virtual std::string arbiterName() = 0;
virtual bool areExclusive(Job* job1, Job* job2) = 0;
virtual bool isThrottled() = 0;
virtual void preStep(TaskScheduler::Job* job)
{
activityMeter.increment();
}
virtual void postStep(TaskScheduler::Job* job)
{
activityMeter.decrement();
}
double getAverageActivity()
{
return activityMeter.averageValue();
}
virtual Arbiter* getSyncronizationArbiter()
{
return this;
};
virtual int getNumPlayers() const
{
return 1;
}
};
typedef enum
{
Done, // The job will be removed from the TaskScheduler
Stepped, // Another step will be scheduled
} StepResult;
static TaskScheduler& singleton();
typedef enum
{
LastError,
AccumulatedError,
FIFO
} PriorityMethod;
static PriorityMethod priorityMethod;
#ifdef AYA_TEST_BUILD
static int findJobFPS;
static bool updateJobPriorityOnWake;
#endif
double threadAffinityPreference;
typedef enum
{
PerCore4 = 104,
PerCore3 = 103,
PerCore2 = 102,
PerCore1 = 101,
Auto = 0,
Threads1 = 1,
Threads2 = 2,
Threads3 = 3,
Threads4 = 4,
Threads8 = 8,
Threads16 = 16
} ThreadPoolConfig;
bool shouldDropThread() const;
void dropThread(Thread* thread);
size_t getThreadCount()
{
return threadCount;
}
void setThreadCount(ThreadPoolConfig threadConfig);
void disableThreads(int count, Threads& threads);
void enableThreads(Threads& threads);
void add(boost::shared_ptr<TaskScheduler::Job> job);
void reschedule(boost::shared_ptr<TaskScheduler::Job> job);
void remove(boost::shared_ptr<TaskScheduler::Job> job)
{
remove(job, false, NULL);
}
// This version of remove might lead to deadlocks in some cases, so try not to use it
void removeBlocking(boost::shared_ptr<TaskScheduler::Job> job)
{
remove(job, true, NULL);
}
// This version of remove calls back several times a second while waiting.
// You might use this to process events in order to avoid deadlocks.
void removeBlocking(boost::shared_ptr<TaskScheduler::Job> job, boost::function<void()> callbackPing)
{
remove(job, true, callbackPing);
}
void getJobsInfo(std::vector<boost::shared_ptr<const Job>>& result);
void getJobsByName(const std::string& name, std::vector<boost::shared_ptr<const Job>>& result);
// Performance counters
double numSleepingJobs() const
{
return sleepingJobCount.value();
}
double numWaitingJobs() const
{
return waitingJobCount.value();
}
double numRunningJobs() const
{
return averageRunningJobCount.value();
}
double threadAffinity() const
{
return averageThreadAffinity.value();
}
size_t threadPoolSize() const
{
return threads.size();
}
double schedulerRate() const
{
return schedulerDutyCycle.rate();
}
double getSchedulerDutyCyclePerThread() const;
double getErrorCalculationRate() const
{
return errorCalculationPerSec.rate();
}
double getSortFrequency() const
{
return sortFrequency.rate();
}
Aya::atomic<int> taskCount;
void printDiagnostics(bool aggregateJobs);
void printJobs();
void setJobsExtendedStatsWindow(double seconds); // set seconds to 0.0 to disable.
void cancelCyclicExecutive();
bool isCyclicExecutive()
{
return cyclicExecutiveEnabled;
}
void releaseCyclicExecutive(TaskScheduler::Job* job);
private:
// ** Here for thread saftey. **
struct CyclicExecutiveJob
{
boost::shared_ptr<TaskScheduler::Job> job;
bool cyclicExecutiveExecuted;
bool isRunning;
CyclicExecutiveJob(const boost::shared_ptr<TaskScheduler::Job>& j)
{
job = j;
cyclicExecutiveExecuted = false;
isRunning = false;
}
// Allows for using std::find.
bool operator==(const boost::shared_ptr<TaskScheduler::Job>& j)
{
return job == j;
}
bool operator==(const TaskScheduler::Job& j)
{
return job.get() == &j;
}
};
TaskScheduler();
~TaskScheduler();
void endAllThreads();
void sampleRunningJobCount();
static bool jobCompare(const CyclicExecutiveJob& jobA, const CyclicExecutiveJob& jobB);
void remove(boost::shared_ptr<TaskScheduler::Job> job, bool joinJob, boost::function<void()> callbackPing);
void remove(const boost::shared_ptr<TaskScheduler::Job>& job, boost::shared_ptr<CEvent> joinEvent);
void scheduleJob(Job& job);
static bool areExclusive(Job* job1, Job* job2, const shared_ptr<Arbiter>& arbiterHint);
bool conflictsWithScheduledJob(Job* item) const;
void incrementThreadCount();
void decrementThreadCount();
Aya::mutex mutex;
typedef std::set<shared_ptr<Job>> AllJobs;
AllJobs allJobs;
typedef boost::intrusive::list<Job, boost::intrusive::base_hook<SleepingHook>> SleepingJobs;
SleepingJobs sleepingJobs;
bool cyclicExecutiveEnabled;
typedef std::vector<CyclicExecutiveJob> CyclicExecutiveJobs;
CyclicExecutiveJobs cyclicExecutiveJobs;
typedef boost::intrusive::list<Job, boost::intrusive::base_hook<WaitingHook>> WaitingJobs;
WaitingJobs waitingJobs;
shared_ptr<Job> nextScheduledJob;
void wakeSleepingJobs();
void enqueueWaitingJob(Job& job);
Time::Interval getShortestSleepTime() const;
boost::shared_ptr<Job> findJobToRun(boost::shared_ptr<Thread> requestingThread);
boost::shared_ptr<Job> findJobToRunNonCyclicJobs(boost::shared_ptr<Thread> requestingThread, Aya::Time now);
int numNonCyclicJobsWithWork();
void checkStillWaitingNextFrame(Time now);
RunningAverage<int> sleepingJobCount;
RunningAverage<int> waitingJobCount;
RunningAverage<int> averageRunningJobCount;
RunningAverageDutyCycle<Time::Precise> schedulerDutyCycle; // time spent scheduling jobs
RunningAverage<double> averageThreadAffinity;
RunningAverageTimeInterval<> errorCalculationPerSec;
RunningAverageTimeInterval<> sortFrequency;
Aya::atomic<int> runningJobCount;
Time nextWakeTime;
Time lastSortTime;
Threads threads;
size_t desiredThreadCount;
CEvent sampleRunningJobCountEvent;
boost::scoped_ptr<boost::thread> runningJobCounterThread;
Aya::atomic<int> threadCount;
static Aya::thread_specific_reference<TaskScheduler::Job> currentJob;
static void static_init();
};
// A simple arbiter that prevents all members of it to execute concurrently
class ExclusiveArbiter
: public TaskScheduler::Arbiter
, boost::noncopyable
{
public:
virtual bool areExclusive(TaskScheduler::Job* job1, TaskScheduler::Job* job2);
virtual std::string arbiterName()
{
return "ExclusiveArbiter";
}
virtual bool isThrottled()
{
return false;
}
static ExclusiveArbiter singleton;
};
class SimpleThrottlingArbiter : public TaskScheduler::Arbiter
{
mutable bool throttled;
Aya::atomic<int> updatingThrottle;
static Aya::atomic<int> arbiterCount;
public:
static bool isThrottlingEnabled;
SimpleThrottlingArbiter()
: throttled(false)
, updatingThrottle(0)
{
++arbiterCount;
}
~SimpleThrottlingArbiter()
{
--arbiterCount;
}
virtual bool isThrottled()
{
if (!isThrottlingEnabled)
return false;
long count = arbiterCount;
if (count <= 1)
return false;
if (updatingThrottle.swap(1) == 0)
{
double cutoff = ((double)Aya::TaskScheduler::singleton().getThreadCount()) / (double)count;
// hysteresis
if (throttled)
{
throttled = getAverageActivity() >= cutoff;
}
else
{
throttled = getAverageActivity() >= 1.1 * cutoff;
}
--updatingThrottle;
}
return throttled;
}
};
} // namespace Aya