You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
8.8 KiB

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Threading;
  6. using System.Threading.Tasks;
  7. #if NET3
  8. using Net3_Proxy;
  9. #endif
  10. namespace IPA.Utilities.Async
  11. {
  12. /// <summary>
  13. /// Utilities for inter-thread synchronization. All Locker method acquire their object immediately,
  14. /// and should only be used with <see langword="using"/> to automatically release them.
  15. /// </summary>
  16. /// <example>
  17. /// <para>
  18. /// The canonical usage of *all* of the member functions is as follows, substituting <see cref="Lock(Mutex)"/>
  19. /// with whichever member you want to use, according to your lock type.
  20. /// </para>
  21. /// <code>
  22. /// using var _locker = Synchronization.Lock(mutex);
  23. /// </code>
  24. /// </example>
  25. public static class Synchronization
  26. {
  27. #region Locker structs
  28. /// <summary>
  29. /// A locker for a <see cref="Mutex"/> that automatically releases when it is disposed.
  30. /// Create this with <see cref="Lock(Mutex)"/>.
  31. /// </summary>
  32. /// <seealso cref="Synchronization"/>
  33. /// <seealso cref="Lock(Mutex)"/>
  34. public struct MutexLocker : IDisposable
  35. {
  36. private readonly Mutex mutex;
  37. internal MutexLocker(Mutex mutex)
  38. {
  39. this.mutex = mutex;
  40. mutex.WaitOne(); // wait and acquire mutex
  41. }
  42. void IDisposable.Dispose() => mutex.ReleaseMutex(); // release mutex
  43. }
  44. /// <summary>
  45. /// A locker for a <see cref="Semaphore"/> that automatically releases when it is disposed.
  46. /// Create this with <see cref="Lock(Semaphore)"/>.
  47. /// </summary>
  48. /// <seealso cref="Synchronization"/>
  49. /// <seealso cref="Lock(Semaphore)"/>
  50. public struct SemaphoreLocker : IDisposable
  51. {
  52. private readonly Semaphore sem;
  53. internal SemaphoreLocker(Semaphore sem)
  54. {
  55. this.sem = sem;
  56. sem.WaitOne();
  57. }
  58. void IDisposable.Dispose() => sem.Release();
  59. }
  60. /// <summary>
  61. /// A locker for a <see cref="SemaphoreSlim"/> that automatically releases when it is disposed.
  62. /// Create this with <see cref="Lock(SemaphoreSlim)"/>.
  63. /// </summary>
  64. /// <seealso cref="Synchronization"/>
  65. /// <seealso cref="Lock(SemaphoreSlim)"/>
  66. public struct SemaphoreSlimLocker : IDisposable
  67. {
  68. private readonly SemaphoreSlim sem;
  69. internal SemaphoreSlimLocker(SemaphoreSlim sem)
  70. {
  71. this.sem = sem;
  72. sem.Wait();
  73. }
  74. void IDisposable.Dispose() => sem.Release();
  75. }
  76. #if NET4
  77. /// <summary>
  78. /// A locker for a <see cref="SemaphoreSlim"/> that was created asynchronously and automatically releases
  79. /// when it is disposed. Create this with <see cref="LockAsync(SemaphoreSlim)"/>.
  80. /// </summary>
  81. /// <seealso cref="Synchronization"/>
  82. /// <seealso cref="LockAsync(SemaphoreSlim)"/>
  83. public struct SemaphoreSlimAsyncLocker : IDisposable
  84. {
  85. private readonly SemaphoreSlim sem;
  86. internal SemaphoreSlimAsyncLocker(SemaphoreSlim sem) => this.sem = sem;
  87. internal Task Lock() => sem.WaitAsync();
  88. void IDisposable.Dispose() => sem.Release();
  89. }
  90. #endif
  91. /// <summary>
  92. /// A locker for a write lock on a <see cref="ReaderWriterLockSlim"/> that automatically releases when
  93. /// it is disposed. Create this with <see cref="LockWrite(ReaderWriterLockSlim)"/>.
  94. /// </summary>
  95. /// <seealso cref="Synchronization"/>
  96. /// <seealso cref="LockWrite(ReaderWriterLockSlim)"/>
  97. public struct ReaderWriterLockSlimWriteLocker : IDisposable
  98. {
  99. private readonly ReaderWriterLockSlim rwl;
  100. internal ReaderWriterLockSlimWriteLocker(ReaderWriterLockSlim lck)
  101. {
  102. rwl = lck;
  103. rwl.EnterWriteLock();
  104. }
  105. void IDisposable.Dispose() => rwl.ExitWriteLock();
  106. }
  107. /// <summary>
  108. /// A locker for a read lock on a <see cref="ReaderWriterLockSlim"/> that automatically releases when
  109. /// it is disposed. Create this with <see cref="LockRead(ReaderWriterLockSlim)"/>.
  110. /// </summary>
  111. /// <seealso cref="Synchronization"/>
  112. /// <seealso cref="LockRead(ReaderWriterLockSlim)"/>
  113. public struct ReaderWriterLockSlimReadLocker : IDisposable
  114. {
  115. private readonly ReaderWriterLockSlim rwl;
  116. internal ReaderWriterLockSlimReadLocker(ReaderWriterLockSlim lck)
  117. {
  118. rwl = lck;
  119. rwl.EnterReadLock();
  120. }
  121. void IDisposable.Dispose() => rwl.ExitReadLock();
  122. }
  123. /// <summary>
  124. /// A locker for an upgradable read lock on a <see cref="ReaderWriterLockSlim"/> that automatically releases
  125. /// when it is disposed. Create this with <see cref="LockReadUpgradable(ReaderWriterLockSlim)"/>.
  126. /// </summary>
  127. /// <seealso cref="Synchronization"/>
  128. /// <seealso cref="LockReadUpgradable(ReaderWriterLockSlim)"/>
  129. public struct ReaderWriterLockSlimUpgradableReadLocker : IDisposable
  130. {
  131. private readonly ReaderWriterLockSlim rwl;
  132. internal ReaderWriterLockSlimUpgradableReadLocker(ReaderWriterLockSlim lck)
  133. {
  134. rwl = lck;
  135. rwl.EnterUpgradeableReadLock();
  136. }
  137. /// <summary>
  138. /// Creates a locker for a write lock on the <see cref="ReaderWriterLockSlim"/> associated with this locker,
  139. /// upgrading the current thread's lock.
  140. /// </summary>
  141. /// <returns>a locker for the new write lock</returns>
  142. /// <seealso cref="Synchronization"/>
  143. public ReaderWriterLockSlimWriteLocker Upgrade() => new ReaderWriterLockSlimWriteLocker(rwl);
  144. void IDisposable.Dispose() => rwl.ExitUpgradeableReadLock();
  145. }
  146. #endregion
  147. #region Accessors
  148. /// <summary>
  149. /// Creates a locker for a mutex.
  150. /// </summary>
  151. /// <param name="mut">the mutex to acquire</param>
  152. /// <returns>the locker to use with <see langword="using"/></returns>
  153. public static MutexLocker Lock(Mutex mut) => new MutexLocker(mut);
  154. /// <summary>
  155. /// Creates a locker for a semaphore.
  156. /// </summary>
  157. /// <param name="sem">the semaphore to acquire</param>
  158. /// <returns>the locker to use with <see langword="using"/></returns>
  159. public static SemaphoreLocker Lock(Semaphore sem) => new SemaphoreLocker(sem);
  160. /// <summary>
  161. /// Creates a locker for a slim semaphore.
  162. /// </summary>
  163. /// <param name="sem">the slim semaphore to acquire</param>
  164. /// <returns>the locker to use with <see langword="using"/></returns>
  165. public static SemaphoreSlimLocker Lock(SemaphoreSlim sem) => new SemaphoreSlimLocker(sem);
  166. #if NET4 // TODO: make this work on NET3 too
  167. /// <summary>
  168. /// Creates a locker for a slim semaphore asynchronously.
  169. /// </summary>
  170. /// <param name="sem">the slim semaphore to acquire async</param>
  171. /// <returns>the locker to use with <see langword="using"/></returns>
  172. public static async Task<SemaphoreSlimAsyncLocker> LockAsync(SemaphoreSlim sem)
  173. {
  174. var locker = new SemaphoreSlimAsyncLocker(sem);
  175. await locker.Lock();
  176. return locker;
  177. }
  178. #endif
  179. /// <summary>
  180. /// Creates a locker for a write lock <see cref="ReaderWriterLockSlim"/>.
  181. /// </summary>
  182. /// <param name="rwl">the lock to acquire in write mode</param>
  183. /// <returns>the locker to use with <see langword="using"/></returns>
  184. public static ReaderWriterLockSlimWriteLocker LockWrite(ReaderWriterLockSlim rwl) => new ReaderWriterLockSlimWriteLocker(rwl);
  185. /// <summary>
  186. /// Creates a locker for a read lock on a <see cref="ReaderWriterLockSlim"/>.
  187. /// </summary>
  188. /// <param name="rwl">the lock to acquire in read mode</param>
  189. /// <returns>the locker to use with <see langword="using"/></returns>
  190. public static ReaderWriterLockSlimReadLocker LockRead(ReaderWriterLockSlim rwl) => new ReaderWriterLockSlimReadLocker(rwl);
  191. /// <summary>
  192. /// Creates a locker for an upgradable read lock on a <see cref="ReaderWriterLockSlim"/>.
  193. /// </summary>
  194. /// <param name="rwl">the lock to acquire in upgradable read mode</param>
  195. /// <returns>the locker to use with <see langword="using"/></returns>
  196. public static ReaderWriterLockSlimUpgradableReadLocker LockReadUpgradable(ReaderWriterLockSlim rwl) => new ReaderWriterLockSlimUpgradableReadLocker(rwl);
  197. #endregion
  198. }
  199. }