/// A simple read/write lock for use with the Unity Job System.
///
/// The RW-lock makes the following assumptions:
/// - Only the main thread will call the methods on this lock.
/// - If jobs are to use locked data, you should call <see cref="Read"/> or <see cref="Write"/> on the lock and pass the returned JobHandle as a dependency the job, and then call <see cref="WriteLockAsync.UnlockAfter"/> on the lock object, with the newly scheduled job's handle.
/// - When taking a Read lock, you should only read data, but if you take a Write lock you may modify data.
/// - On the main thread, multiple synchronous write locks may be nested.
///
/// You do not need to care about dependencies when calling the <see cref="ReadSync"/> and <see cref="WriteSync"/> methods. That's handled automatically for you.
/// var readLock = AstarPath.active.LockGraphDataForReading();
/// var handle = new MyJob {
/// // ...
/// }.Schedule(readLock.dependency);
/// readLock.UnlockAfter(handle);
/// </code>
/// </summary>
publicclassRWLock{
JobHandlelastWrite;
JobHandlelastRead;
#ifENABLE_UNITY_COLLECTIONS_CHECKS
intheldSyncLocks;
boolpendingAsync;
#ifDEBUG_RWLOCK
stringpendingStackTrace;
#endif
voidCheckPendingAsync(){
#ifDEBUG_RWLOCK
if(pendingAsync)thrownewSystem.InvalidOperationException("An async lock was previously aquired, but UnlockAfter was never called on it. The lock was aquired at\n"+pendingStackTrace+"\n\n");
#else
if(pendingAsync)thrownewSystem.InvalidOperationException("An async lock was previously aquired, but UnlockAfter was never called on it.");
#endif
}
#endif
voidAddPendingSync(){
#ifENABLE_UNITY_COLLECTIONS_CHECKS
CheckPendingAsync();
#ifDEBUG_RWLOCK
pendingStackTrace=System.Environment.StackTrace;
#endif
heldSyncLocks++;
#endif
}
voidRemovePendingSync(){
#ifENABLE_UNITY_COLLECTIONS_CHECKS
if(heldSyncLocks<=0)thrownewSystem.InvalidOperationException("Tried to unlock a lock which was not locked. Did you call Unlock twice?");
heldSyncLocks--;
#endif
}
voidAddPendingAsync(){
#ifENABLE_UNITY_COLLECTIONS_CHECKS
CheckPendingAsync();
#ifDEBUG_RWLOCK
if(heldSyncWriteLocks>0)thrownewSystem.InvalidOperationException("A synchronous lock is already being held. You cannot lock it asynchronously at the same time. The sync lock was aquired at\n"+pendingStackTrace+"\n\n");
pendingStackTrace=System.Environment.StackTrace;
#else
if(heldSyncLocks>0)thrownewSystem.InvalidOperationException("A synchronous lock is already being held. You cannot lock it asynchronously at the same time.");
#endif
pendingAsync=true;
#endif
}
voidRemovePendingAsync(){
#ifENABLE_UNITY_COLLECTIONS_CHECKS
pendingAsync=false;
#endif
}
/// <summary>
/// Aquire a read lock on the main thread.
/// This method will block until all pending write locks have been released.
/// </summary>
publicLockSyncReadSync(){
AddPendingSync();
lastWrite.Complete();
lastWrite=default;// Setting this to default will avoid a call into unity's c++ parts next time we call Complete (improves perf slightly)
returnnewLockSync(this);
}
/// <summary>
/// Aquire a read lock on the main thread.
/// This method will not block until all asynchronous write locks have been released, instead you should make sure to add the returned JobHandle as a dependency to any jobs that use the locked data.
///
/// If a synchronous write lock is currently held, this method will throw an exception.
///
/// <code>
/// var readLock = AstarPath.active.LockGraphDataForReading();
/// var handle = new MyJob {
/// // ...
/// }.Schedule(readLock.dependency);
/// readLock.UnlockAfter(handle);
/// </code>
/// </summary>
publicReadLockAsyncRead(){
AddPendingAsync();
returnnewReadLockAsync(this,lastWrite);
}
/// <summary>
/// Aquire a write lock on the main thread.
/// This method will block until all pending read and write locks have been released.
/// </summary>
publicLockSyncWriteSync(){
AddPendingSync();
lastWrite.Complete();
lastWrite=default;// Setting this to default will avoid a call into unity's c++ parts next time we call Complete (improves perf slightly)
lastRead.Complete();
returnnewLockSync(this);
}
/// <summary>
/// Aquire a write lock on the main thread.
/// This method will not block until all asynchronous read and write locks have been released, instead you should make sure to add the returned JobHandle as a dependency to any jobs that use the locked data.
///
/// If a synchronous write lock is currently held, this method will throw an exception.
///
/// <code>
/// var readLock = AstarPath.active.LockGraphDataForReading();