summaryrefslogtreecommitdiff
path: root/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs
diff options
context:
space:
mode:
authorchai <215380520@qq.com>2024-05-23 10:08:29 +0800
committerchai <215380520@qq.com>2024-05-23 10:08:29 +0800
commit8722a9920c1f6119bf6e769cba270e63097f8e25 (patch)
tree2eaf9865de7fb1404546de4a4296553d8f68cc3b /Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs
parent3ba4020b69e5971bb0df7ee08b31d10ea4d01937 (diff)
+ astar project
Diffstat (limited to 'Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs')
-rw-r--r--Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs215
1 files changed, 215 insertions, 0 deletions
diff --git a/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs
new file mode 100644
index 0000000..0cdd360
--- /dev/null
+++ b/Other/AstarPathfindingDemo/Packages/com.arongranberg.astar/Utilities/RWLock.cs
@@ -0,0 +1,215 @@
+// #define DEBUG_RWLOCK
+using Unity.Jobs;
+
+namespace Pathfinding.Jobs {
+ /// <summary>
+ /// 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.
+ ///
+ /// See: https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock
+ ///
+ /// <code>
+ /// var readLock = AstarPath.active.LockGraphDataForReading();
+ /// var handle = new MyJob {
+ /// // ...
+ /// }.Schedule(readLock.dependency);
+ /// readLock.UnlockAfter(handle);
+ /// </code>
+ /// </summary>
+ public class RWLock {
+ JobHandle lastWrite;
+ JobHandle lastRead;
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ int heldSyncLocks;
+ bool pendingAsync;
+#if DEBUG_RWLOCK
+ string pendingStackTrace;
+#endif
+
+ void CheckPendingAsync () {
+#if DEBUG_RWLOCK
+ if (pendingAsync) throw new System.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) throw new System.InvalidOperationException("An async lock was previously aquired, but UnlockAfter was never called on it.");
+#endif
+ }
+#endif
+
+ void AddPendingSync () {
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ CheckPendingAsync();
+#if DEBUG_RWLOCK
+ pendingStackTrace = System.Environment.StackTrace;
+#endif
+ heldSyncLocks++;
+#endif
+ }
+
+ void RemovePendingSync () {
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ if (heldSyncLocks <= 0) throw new System.InvalidOperationException("Tried to unlock a lock which was not locked. Did you call Unlock twice?");
+ heldSyncLocks--;
+#endif
+ }
+
+ void AddPendingAsync () {
+#if ENABLE_UNITY_COLLECTIONS_CHECKS
+ CheckPendingAsync();
+#if DEBUG_RWLOCK
+ if (heldSyncWriteLocks > 0) throw new System.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) throw new System.InvalidOperationException("A synchronous lock is already being held. You cannot lock it asynchronously at the same time.");
+#endif
+ pendingAsync = true;
+#endif
+ }
+
+ void RemovePendingAsync () {
+#if ENABLE_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>
+ public LockSync ReadSync () {
+ 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)
+ return new LockSync(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>
+ public ReadLockAsync Read () {
+ AddPendingAsync();
+ return new ReadLockAsync(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>
+ public LockSync WriteSync () {
+ 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();
+ return new LockSync(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();
+ /// var handle = new MyJob {
+ /// // ...
+ /// }.Schedule(readLock.dependency);
+ /// readLock.UnlockAfter(handle);
+ /// </code>
+ /// </summary>
+ public WriteLockAsync Write () {
+ AddPendingAsync();
+ return new WriteLockAsync(this, JobHandle.CombineDependencies(lastRead, lastWrite));
+ }
+
+ public readonly struct CombinedReadLockAsync {
+ readonly RWLock lock1;
+ readonly RWLock lock2;
+ public readonly JobHandle dependency;
+
+ public CombinedReadLockAsync(ReadLockAsync lock1, ReadLockAsync lock2) {
+ this.lock1 = lock1.inner;
+ this.lock2 = lock2.inner;
+ dependency = JobHandle.CombineDependencies(lock1.dependency, lock2.dependency);
+ }
+
+ /// <summary>Release the lock after the given job has completed</summary>
+ public void UnlockAfter (JobHandle handle) {
+ if (lock1 != null) {
+ lock1.RemovePendingAsync();
+ lock1.lastRead = JobHandle.CombineDependencies(lock1.lastRead, handle);
+ }
+ if (lock2 != null) {
+ lock2.RemovePendingAsync();
+ lock2.lastRead = JobHandle.CombineDependencies(lock2.lastRead, handle);
+ }
+ }
+ }
+
+ public readonly struct ReadLockAsync {
+ internal readonly RWLock inner;
+ public readonly JobHandle dependency;
+
+ public ReadLockAsync(RWLock inner, JobHandle dependency) {
+ this.inner = inner;
+ this.dependency = dependency;
+ }
+
+ /// <summary>Release the lock after the given job has completed</summary>
+ public void UnlockAfter (JobHandle handle) {
+ if (inner != null) {
+ inner.RemovePendingAsync();
+ inner.lastRead = JobHandle.CombineDependencies(inner.lastRead, handle);
+ }
+ }
+ }
+
+ public readonly struct WriteLockAsync {
+ readonly RWLock inner;
+ public readonly JobHandle dependency;
+
+ public WriteLockAsync(RWLock inner, JobHandle dependency) {
+ this.inner = inner;
+ this.dependency = dependency;
+ }
+
+ /// <summary>Release the lock after the given job has completed</summary>
+ public void UnlockAfter (JobHandle handle) {
+ if (inner != null) {
+ inner.RemovePendingAsync();
+ inner.lastWrite = handle;
+ }
+ }
+ }
+
+ public readonly struct LockSync {
+ readonly RWLock inner;
+
+ public LockSync(RWLock inner) {
+ this.inner = inner;
+ }
+
+ /// <summary>Release the lock</summary>
+ public void Unlock () {
+ if (inner != null) inner.RemovePendingSync();
+ }
+ }
+ }
+}