Skip to content

Instantly share code, notes, and snippets.

@Flavelius
Created October 3, 2017 10:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Flavelius/e36980bbbbe735a4eb18c1ff80f775b2 to your computer and use it in GitHub Desktop.
Save Flavelius/e36980bbbbe735a4eb18c1ff80f775b2 to your computer and use it in GitHub Desktop.
Simple Unity3D/Generic Main thread Job Scheduler
using System;
using System.Collections.Generic;
public class JobScheduler
{
public delegate float TimeRetrieverFunc();
readonly int MaxPooledJobs = 10000;
List<Job> jobs = new List<Job>(1024);
Queue<Job> jobPool = new Queue<Job>();
TimeRetrieverFunc getTime;
#if !UNITY_STANDALONE && !UNITY_EDITOR
/// <summary>
/// Creates a new JobScheduler
/// </summary>
/// <param name="timeFunc">Func which returns the current time (must not be null)</param>
/// <param name="maxPoolSize">Size of the pool for inactive task jobs</param>
/// <param name="preFillCount">Number of pregenerated jobs to ease allocations at runtime</param>
public JobScheduler(TimeRetrieverFunc timeFunc, int maxPoolSize = 10000, int preFillCount = 1000)
{
if (timeFunc == null) throw new ArgumentNullException("timeFunc");
getTime = timeFunc;
MaxPooledJobs = maxPoolSize;
PreFillPool(preFillCount);
}
#else
/// <summary>
/// Creates a new JobScheduler
/// </summary>
/// <param name="maxPoolSize">Size of the pool for inactive task jobs</param>
/// <param name="preFillCount">Number of pregenerated jobs to ease allocations at runtime</param>
public JobScheduler(int maxPoolSize = 10000, int preFillCount = 1000)
{
getTime = () => UnityEngine.Time.time;
MaxPooledJobs = maxPoolSize;
PreFillPool(preFillCount);
}
#endif
void PreFillPool(int count)
{
for (var i = 0; i < count; i++)
{
jobPool.Enqueue(new Job(-1, null));
}
}
public int GetPoolCount()
{
return jobPool.Count;
}
public int GetJobCount()
{
return jobs.Count;
}
void Pool(Job job)
{
if (jobPool.Count > MaxPooledJobs) return;
job.Reset();
jobPool.Enqueue(job);
}
Job GetPooledJob(Action task, float scheduledTime)
{
if (jobPool.Count > 0)
{
var job = jobPool.Dequeue();
job.Setup(scheduledTime, task);
return job;
}
return new Job(scheduledTime, task);
}
/// <summary>
/// Schedules a task for later execution, consider caching the passed Action if possible (less allocations)
/// </summary>
/// <param name="task">The task to execute</param>
/// <param name="delay">Timeframe to wait before executing the task</param>
public void Add(Action task, float delay)
{
var job = GetPooledJob(task, getTime() + delay);
var index = jobs.BinarySearch(job);
if (index < 0) index = ~index;
jobs.Insert(index, job);
}
/// <summary>
/// Updates the Scheduler so tasks are executed on the main thread
/// </summary>
public void Update()
{
Job job;
var time = getTime();
while (jobs.Count > 0 && (job = jobs[jobs.Count-1]).ScheduledTime < time)
{
job.Task();
jobs.RemoveAt(jobs.Count-1);
Pool(job);
}
}
class Job : IComparable<Job>
{
public float ScheduledTime;
public Action Task;
public Job(float scheduledTime, Action task)
{
ScheduledTime = scheduledTime;
Task = task;
}
public int CompareTo(Job other)
{
if (ScheduledTime > other.ScheduledTime) return -1;
if (ScheduledTime < other.ScheduledTime) return 1;
return 0;
}
public void Setup(float scheduledTime, Action task)
{
ScheduledTime = scheduledTime;
Task = task;
}
public void Reset()
{
Task = null;
ScheduledTime = -1;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment