feat: implement multi-threaded JobScheduler with worker threads and dependency management
This commit is contained in:
@@ -144,61 +144,12 @@ internal class WorkerThread : IDisposable
|
||||
continue;
|
||||
}
|
||||
|
||||
// Try to acquire a reference count for the job. This ensures that the job won't be removed while we're processing it.
|
||||
// This is critical that if thread A reads the job, but suddenly os scheduler delay the thread for just a moment, and thread B completes the job and removes it from the system, when thread A resumes, it might be accessing invalid memory.
|
||||
// By acquiring a reference count, we ensure that even if the job is completed while we're processing it, it won't be removed until we're done.
|
||||
|
||||
var rcSpin = new SpinWait();
|
||||
var rcAcquired = false;
|
||||
int rc;
|
||||
|
||||
while (true)
|
||||
var currentState = Volatile.Read(ref jobInfo.state);
|
||||
if (JobUtility.GetState(currentState) == JobState.Scheduled)
|
||||
{
|
||||
_scheduler.GetJobInfoReference(handle, out var currentExist);
|
||||
if (!currentExist)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var stateVal = Volatile.Read(ref jobInfo.state);
|
||||
var state = JobUtility.GetState(stateVal);
|
||||
|
||||
if (state == JobState.Completed || state == JobState.Invalid)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
var newState = stateVal + JobUtility.RC_ONE;
|
||||
if (state == JobState.Scheduled)
|
||||
{
|
||||
newState = (newState & ~JobUtility.STATE_MASK) | JobUtility.JOBSTATE_RUNNING;
|
||||
}
|
||||
|
||||
// Attempt to acquire a reference count by incrementing the state value. If the state has changed since we read it, we need to retry.
|
||||
if (Interlocked.CompareExchange(ref jobInfo.state, newState, stateVal) == stateVal)
|
||||
{
|
||||
_scheduler.GetJobInfoReference(handle, out currentExist);
|
||||
if (!currentExist)
|
||||
{
|
||||
rc = JobUtility.ReleaseRC(ref jobInfo.state);
|
||||
if (rc == 0)
|
||||
{
|
||||
_scheduler.MarkJobComplete(handle);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
rcAcquired = true;
|
||||
break;
|
||||
}
|
||||
|
||||
rcSpin.SpinOnce(-1);
|
||||
}
|
||||
|
||||
if (!rcAcquired)
|
||||
{
|
||||
continue;
|
||||
// Try to flag as running, but don't loop if we fail. Someone else might have already flagged it.
|
||||
var newState = (currentState & ~JobUtility.STATE_MASK) | JobUtility.JOBSTATE_RUNNING;
|
||||
Interlocked.CompareExchange(ref jobInfo.state, newState, currentState);
|
||||
}
|
||||
|
||||
if (jobInfo.pExecutionFunc != null)
|
||||
@@ -214,7 +165,7 @@ internal class WorkerThread : IDisposable
|
||||
jobInfo.pExecutionFunc(jobInfo.dataID, jobInfo.dataGeneration, ref jobInfo.jobRanges, in ctx);
|
||||
}
|
||||
|
||||
rc = JobUtility.ReleaseRC(ref jobInfo.state);
|
||||
var rc = JobUtility.ReleaseRC(ref jobInfo.state);
|
||||
if (rc == 0)
|
||||
{
|
||||
_scheduler.MarkJobComplete(handle);
|
||||
|
||||
Reference in New Issue
Block a user