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.

230 lines
8.9 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
  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. // TODO: add async fun stuff to this
  149. /// <summary>
  150. /// Creates a locker for a mutex.
  151. /// </summary>
  152. /// <param name="mut">the mutex to acquire</param>
  153. /// <returns>the locker to use with <see langword="using"/></returns>
  154. public static MutexLocker Lock(Mutex mut) => new MutexLocker(mut);
  155. /// <summary>
  156. /// Creates a locker for a semaphore.
  157. /// </summary>
  158. /// <param name="sem">the semaphore to acquire</param>
  159. /// <returns>the locker to use with <see langword="using"/></returns>
  160. public static SemaphoreLocker Lock(Semaphore sem) => new SemaphoreLocker(sem);
  161. /// <summary>
  162. /// Creates a locker for a slim semaphore.
  163. /// </summary>
  164. /// <param name="sem">the slim semaphore to acquire</param>
  165. /// <returns>the locker to use with <see langword="using"/></returns>
  166. public static SemaphoreSlimLocker Lock(SemaphoreSlim sem) => new SemaphoreSlimLocker(sem);
  167. #if NET4 // TODO: make this work on NET3 too
  168. /// <summary>
  169. /// Creates a locker for a slim semaphore asynchronously.
  170. /// </summary>
  171. /// <param name="sem">the slim semaphore to acquire async</param>
  172. /// <returns>the locker to use with <see langword="using"/></returns>
  173. public static async Task<SemaphoreSlimAsyncLocker> LockAsync(SemaphoreSlim sem)
  174. {
  175. var locker = new SemaphoreSlimAsyncLocker(sem);
  176. await locker.Lock();
  177. return locker;
  178. }
  179. #endif
  180. /// <summary>
  181. /// Creates a locker for a write lock <see cref="ReaderWriterLockSlim"/>.
  182. /// </summary>
  183. /// <param name="rwl">the lock to acquire in write mode</param>
  184. /// <returns>the locker to use with <see langword="using"/></returns>
  185. public static ReaderWriterLockSlimWriteLocker LockWrite(ReaderWriterLockSlim rwl) => new ReaderWriterLockSlimWriteLocker(rwl);
  186. /// <summary>
  187. /// Creates a locker for a read lock on a <see cref="ReaderWriterLockSlim"/>.
  188. /// </summary>
  189. /// <param name="rwl">the lock to acquire in read mode</param>
  190. /// <returns>the locker to use with <see langword="using"/></returns>
  191. public static ReaderWriterLockSlimReadLocker LockRead(ReaderWriterLockSlim rwl) => new ReaderWriterLockSlimReadLocker(rwl);
  192. /// <summary>
  193. /// Creates a locker for an upgradable read lock on a <see cref="ReaderWriterLockSlim"/>.
  194. /// </summary>
  195. /// <param name="rwl">the lock to acquire in upgradable read mode</param>
  196. /// <returns>the locker to use with <see langword="using"/></returns>
  197. public static ReaderWriterLockSlimUpgradableReadLocker LockReadUpgradable(ReaderWriterLockSlim rwl) => new ReaderWriterLockSlimUpgradableReadLocker(rwl);
  198. #endregion
  199. }
  200. }