The Labs \ Source Viewer \ SSCLI \ System.Threading \ MutexCleanupInfo

  1. // ==++==
  2. //
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. //
  14. // ==--==
  15. /*=============================================================================
  16. **
  17. ** Class: Mutex
  18. **
  19. **
  20. ** Purpose: synchronization primitive that can also be used for interprocess synchronization
  21. **
  22. **
  23. =============================================================================*/
  24. namespace System.Threading
  25. {
  26.     using System;
  27.     using System.Threading;
  28.     using System.Runtime.CompilerServices;
  29.     using System.Security.Permissions;
  30.     using System.IO;
  31.     using Microsoft.Win32;
  32.     using Microsoft.Win32.SafeHandles;
  33.     using System.Runtime.InteropServices;
  34.     using System.Runtime.ConstrainedExecution;
  35.     using System.Runtime.Versioning;
  36.     using System.Security.Principal;
  37.     using System.Security;
  38.    
  39.    
  40.     [HostProtection(Synchronization = true, ExternalThreading = true)]
  41.     [ComVisible(true)]
  42.     public sealed class Mutex : WaitHandle
  43.     {
  44.         static bool dummyBool;
  45.        
  46.        
  47.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  48.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  49.         [ResourceExposure(ResourceScope.None)]
  50.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  51.         public Mutex(bool initiallyOwned, string name, out bool createdNew)
  52.         {
  53.             if (null != name && System.IO.Path.MAX_PATH < name.Length) {
  54.                 throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", name));
  55.             }
  56.             Win32Native.SECURITY_ATTRIBUTES secAttrs = null;
  57.             SafeWaitHandle mutexHandle = null;
  58.             bool newMutex = false;
  59.             RuntimeHelpers.CleanupCode cleanupCode = new RuntimeHelpers.CleanupCode(MutexCleanupCode);
  60.             MutexCleanupInfo cleanupInfo = new MutexCleanupInfo(mutexHandle, false);
  61.             RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(delegate(object userData)
  62.             {
  63.                 // try block
  64.                 RuntimeHelpers.PrepareConstrainedRegions();
  65.                 try {
  66.                 }
  67.                 finally {
  68.                     if (initiallyOwned) {
  69.                         cleanupInfo.inCriticalRegion = true;
  70.                         Thread.BeginThreadAffinity();
  71.                         Thread.BeginCriticalRegion();
  72.                     }
  73.                 }
  74.                
  75.                 int errorCode = 0;
  76.                 RuntimeHelpers.PrepareConstrainedRegions();
  77.                 try {
  78.                 }
  79.                 finally {
  80.                     errorCode = CreateMutexHandle(initiallyOwned, name, secAttrs, out mutexHandle);
  81.                 }
  82.                
  83.                 if (mutexHandle.IsInvalid) {
  84.                     mutexHandle.SetHandleAsInvalid();
  85.                     if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode)
  86.                         throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
  87.                     __Error.WinIOError(errorCode, name);
  88.                 }
  89.                 newMutex = errorCode != Win32Native.ERROR_ALREADY_EXISTS;
  90.                 SetHandleInternal(mutexHandle);
  91.                 mutexHandle.SetAsMutex();
  92.                
  93.                 hasThreadAffinity = true;
  94.                
  95.             }
  96. , cleanupCode, cleanupInfo);
  97.             createdNew = newMutex;
  98.            
  99.         }
  100.        
  101.         [PrePrepareMethod()]
  102.         private void MutexCleanupCode(object userData, bool exceptionThrown)
  103.         {
  104.             MutexCleanupInfo cleanupInfo = (MutexCleanupInfo)userData;
  105.            
  106.             // If hasThreadAffinity isn�t true, we�ve thrown an exception in the above try, and we must free the mutex
  107.             // on this OS thread before ending our thread affninity.
  108.             if (!hasThreadAffinity) {
  109.                 if (cleanupInfo.mutexHandle != null && !cleanupInfo.mutexHandle.IsInvalid) {
  110.                     if (cleanupInfo.inCriticalRegion) {
  111.                         Win32Native.ReleaseMutex(cleanupInfo.mutexHandle);
  112.                     }
  113.                     cleanupInfo.mutexHandle.Dispose();
  114.                    
  115.                 }
  116.                
  117.                 if (cleanupInfo.inCriticalRegion) {
  118.                     Thread.EndCriticalRegion();
  119.                     Thread.EndThreadAffinity();
  120.                 }
  121.             }
  122.         }
  123.        
  124.         internal class MutexCleanupInfo
  125.         {
  126.             internal SafeWaitHandle mutexHandle;
  127.             internal bool inCriticalRegion;
  128.             internal MutexCleanupInfo(SafeWaitHandle mutexHandle, bool inCriticalRegion)
  129.             {
  130.                 this.mutexHandle = mutexHandle;
  131.                 this.inCriticalRegion = inCriticalRegion;
  132.             }
  133.         }
  134.        
  135.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  136.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  137.         [ResourceExposure(ResourceScope.Machine)]
  138.         [ResourceConsumption(ResourceScope.Machine)]
  139.         public Mutex(bool initiallyOwned, string name) : this(initiallyOwned, name, out dummyBool)
  140.         {
  141.         }
  142.        
  143.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  144.         [ResourceExposure(ResourceScope.None)]
  145.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  146.         public Mutex(bool initiallyOwned) : this(initiallyOwned, null, out dummyBool)
  147.         {
  148.         }
  149.        
  150.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  151.         [ResourceExposure(ResourceScope.None)]
  152.         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
  153.         public Mutex() : this(false, null, out dummyBool)
  154.         {
  155.         }
  156.        
  157.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  158.         private Mutex(SafeWaitHandle handle)
  159.         {
  160.             SetHandleInternal(handle);
  161.             handle.SetAsMutex();
  162.             hasThreadAffinity = true;
  163.         }
  164.        
  165.         [SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
  166.         [ResourceExposure(ResourceScope.Machine)]
  167.         [ResourceConsumption(ResourceScope.Machine)]
  168.         public static Mutex OpenExisting(string name)
  169.         {
  170.             if (name == null) {
  171.                 throw new ArgumentNullException("name", Environment.GetResourceString("ArgumentNull_WithParamName"));
  172.             }
  173.            
  174.             if (name.Length == 0) {
  175.                 throw new ArgumentException(Environment.GetResourceString("Argument_EmptyName"), "name");
  176.             }
  177.             if (System.IO.Path.MAX_PATH < name.Length) {
  178.                 throw new ArgumentException(Environment.GetResourceString("Argument_WaitHandleNameTooLong", name));
  179.             }
  180.            
  181.            
  182.             // To allow users to view & edit the ACL's, call OpenMutex
  183.             // with parameters to allow us to view & edit the ACL. This will
  184.             // fail if we don't have permission to view or edit the ACL's.
  185.             // If that happens, ask for less permissions.
  186.             SafeWaitHandle myHandle = Win32Native.OpenMutex(Win32Native.MUTEX_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);
  187.            
  188.             int errorCode = 0;
  189.             if (myHandle.IsInvalid) {
  190.                 errorCode = Marshal.GetLastWin32Error();
  191.                
  192.                 if (Win32Native.ERROR_FILE_NOT_FOUND == errorCode || Win32Native.ERROR_INVALID_NAME == errorCode) {
  193.                     throw new WaitHandleCannotBeOpenedException();
  194.                 }
  195.                
  196.                 if (null != name && 0 != name.Length && Win32Native.ERROR_INVALID_HANDLE == errorCode) {
  197.                     throw new WaitHandleCannotBeOpenedException(Environment.GetResourceString("Threading.WaitHandleCannotBeOpenedException_InvalidHandle", name));
  198.                 }
  199.                
  200.                 // this is for passed through Win32Native Errors
  201.                 __Error.WinIOError(errorCode, name);
  202.             }
  203.            
  204.             return new Mutex(myHandle);
  205.         }
  206.        
  207.         // Note: To call ReleaseMutex, you must have an ACL granting you
  208.         // MUTEX_MODIFY_STATE rights (0x0001). The other interesting value
  209.         // in a Mutex's ACL is MUTEX_ALL_ACCESS (0x1F0001).
  210.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  211.         public void ReleaseMutex()
  212.         {
  213.             if (Win32Native.ReleaseMutex(safeWaitHandle)) {
  214.                 Thread.EndCriticalRegion();
  215.                 Thread.EndThreadAffinity();
  216.             }
  217.             else {
  218.                 throw new ApplicationException(Environment.GetResourceString("Arg_SynchronizationLockException"));
  219.             }
  220.         }
  221.        
  222.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  223.         static int CreateMutexHandle(bool initiallyOwned, string name, Win32Native.SECURITY_ATTRIBUTES securityAttribute, out SafeWaitHandle mutexHandle)
  224.         {
  225.             int errorCode;
  226.             bool fReservedMutexObtained = false;
  227.             bool fAffinity = false;
  228.            
  229.             while (true) {
  230.                 mutexHandle = Win32Native.CreateMutex(securityAttribute, initiallyOwned, name);
  231.                 errorCode = Marshal.GetLastWin32Error();
  232.                 if (!mutexHandle.IsInvalid) {
  233.                     break;
  234.                 }
  235.                
  236.                 if (errorCode == Win32Native.ERROR_ACCESS_DENIED) {
  237.                     // If a mutex with the name already exists, OS will try to open it with FullAccess.
  238.                     // It might fail if we don't have enough access. In that case, we try to open the mutex will modify and synchronize access.
  239.                     //
  240.                    
  241.                     RuntimeHelpers.PrepareConstrainedRegions();
  242.                     try {
  243.                         try {
  244.                         }
  245.                         finally {
  246.                             Thread.BeginThreadAffinity();
  247.                             fAffinity = true;
  248.                         }
  249.                         AcquireReservedMutex(ref fReservedMutexObtained);
  250.                         mutexHandle = Win32Native.OpenMutex(Win32Native.MUTEX_MODIFY_STATE | Win32Native.SYNCHRONIZE, false, name);
  251.                         if (!mutexHandle.IsInvalid) {
  252.                             errorCode = Win32Native.ERROR_ALREADY_EXISTS;
  253.                         }
  254.                         else {
  255.                             errorCode = Marshal.GetLastWin32Error();
  256.                         }
  257.                     }
  258.                     finally {
  259.                         if (fReservedMutexObtained)
  260.                             ReleaseReservedMutex();
  261.                         if (fAffinity)
  262.                             Thread.EndThreadAffinity();
  263.                     }
  264.                    
  265.                     // There could be a race here, the other owner of the mutex can free the mutex,
  266.                     // We need to retry creation in that case.
  267.                     if (errorCode != Win32Native.ERROR_FILE_NOT_FOUND) {
  268.                         if (errorCode == Win32Native.ERROR_SUCCESS) {
  269.                             errorCode = Win32Native.ERROR_ALREADY_EXISTS;
  270.                         }
  271.                         break;
  272.                     }
  273.                 }
  274.                 else {
  275.                     break;
  276.                 }
  277.             }
  278.             return errorCode;
  279.         }
  280.        
  281.        
  282.        
  283.         // Enables workaround for known OS bug at
  284.         // http://support.microsoft.com/default.aspx?scid=kb;en-us;889318
  285.         // One machine-wide mutex serializes all OpenMutex and CloseHandle operations.
  286.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  287.         unsafe static internal void AcquireReservedMutex(ref bool bHandleObtained)
  288.         {
  289.             bHandleObtained = true;
  290.         }
  291.        
  292.         [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
  293.         static internal void ReleaseReservedMutex()
  294.         {
  295.         }
  296.        
  297.     }
  298. }

Developer Fusion