Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Web / Compilation / CompilationLock.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="CompilationLock.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>                                                                
5 //------------------------------------------------------------------------------
6
7 //#define MUTEXINSTRUMENTATION
8
9 namespace System.Web.Compilation {
10
11 using System;
12 using System.Threading;
13 using System.Globalization;
14 using System.Security.Principal;
15 using System.Web.Util;
16 using System.Web.Configuration;
17 using System.Runtime.InteropServices;
18 using System.Web.Management;
19 using System.Runtime.Versioning;
20 using System.Diagnostics;
21 using Debug = System.Web.Util.Debug;
22
23 internal sealed class CompilationMutex : IDisposable {
24
25     private String  _name;
26     private String  _comment;
27 #if MUTEXINSTRUMENTATION
28     // Used to keep track of the stack when the mutex is obtained
29     private string _stackTrace;
30 #endif
31
32     // ROTORTODO: replace unmanaged aspnet_isapi mutex with managed implementation
33 #if !FEATURE_PAL // No unmanaged aspnet_isapi mutex in Coriolis
34     private HandleRef   _mutexHandle;
35
36     // Lock Status is used to drain out all worker threads out of Mutex ownership on
37     // app domain shutdown: -1 locked for good, 0 unlocked, N locked by a worker thread(s)
38     private int     _lockStatus;  
39     private bool    _draining = false;
40 #endif // !FEATURE_PAL
41
42     internal CompilationMutex(String name, String comment) {
43
44 #if !FEATURE_PAL // No unmanaged aspnet_isapi mutex in Coriolis
45
46         // Attempt to get the mutex string from the registry (VSWhidbey 415795)
47         string mutexRandomName = (string) Misc.GetAspNetRegValue("CompilationMutexName",
48             null /*valueName*/, null /*defaultValue*/);
49
50         if (mutexRandomName != null) {
51             // If we were able to use the registry value, use it.  Also, we need to prepend "Global\"
52             // to the mutex name, to make sure it can be shared between a terminal server session
53             // and IIS (VSWhidbey 307523).
54             _name += @"Global\" + name + "-" + mutexRandomName;
55         }
56         else {
57             // If we couldn't get the reg value, don't use it, and prepend "Local\" to the mutex
58             // name to make it local to the session (and hence prevent hijacking)
59             _name += @"Local\" + name;
60         }
61
62         _comment = comment;
63
64         Debug.Trace("Mutex", "Creating Mutex " + MutexDebugName);
65
66         _mutexHandle = new HandleRef(this, UnsafeNativeMethods.InstrumentedMutexCreate(_name));
67
68         if (_mutexHandle.Handle == IntPtr.Zero) {
69             Debug.Trace("Mutex", "Failed to create Mutex " + MutexDebugName);
70
71             throw new InvalidOperationException(SR.GetString(SR.CompilationMutex_Create));
72         }
73
74         Debug.Trace("Mutex", "Successfully created Mutex " + MutexDebugName);
75 #endif // !FEATURE_PAL
76     }
77
78     ~CompilationMutex() {
79         Close();
80     }
81
82     void IDisposable.Dispose() {
83         Close();
84         System.GC.SuppressFinalize(this);
85     }
86
87     internal /*public*/ void Close() {
88
89 #if !FEATURE_PAL // No unmanaged aspnet_isapi mutex in Coriolis
90
91         if (_mutexHandle.Handle != IntPtr.Zero) {
92             UnsafeNativeMethods.InstrumentedMutexDelete(_mutexHandle);
93             _mutexHandle = new HandleRef(this, IntPtr.Zero);
94         }
95 #endif // !FEATURE_PAL
96     }
97
98     [ResourceExposure(ResourceScope.None)]
99     internal /*public*/ void WaitOne() {
100
101 #if !FEATURE_PAL // No unmanaged aspnet_isapi mutex in Coriolis
102
103         if (_mutexHandle.Handle == IntPtr.Zero)
104             throw new InvalidOperationException(SR.GetString(SR.CompilationMutex_Null));
105
106         // check the lock status
107         for (;;) {
108             int lockStatus = _lockStatus;
109
110             if (lockStatus == -1 || _draining)
111                 throw new InvalidOperationException(SR.GetString(SR.CompilationMutex_Drained));
112
113             if (Interlocked.CompareExchange(ref _lockStatus, lockStatus+1, lockStatus) == lockStatus)
114                 break; // got the lock
115         }
116
117         Debug.Trace("Mutex", "Waiting for mutex " + MutexDebugName);
118
119         if (UnsafeNativeMethods.InstrumentedMutexGetLock(_mutexHandle, -1) == -1) {
120             // failed to get the lock
121             Interlocked.Decrement(ref _lockStatus);
122             throw new InvalidOperationException(SR.GetString(SR.CompilationMutex_Failed));
123         }
124
125 #if MUTEXINSTRUMENTATION
126         // Remember the stack trace for debugging purpose
127         _stackTrace = (new StackTrace()).ToString();
128 #endif
129
130         Debug.Trace("Mutex", "Got mutex " + MutexDebugName);
131 #endif // !FEATURE_PAL
132     }
133
134     internal /*public*/ void ReleaseMutex() {
135
136 #if !FEATURE_PAL // No unmanaged aspnet_isapi mutex in Coriolis
137         if (_mutexHandle.Handle == IntPtr.Zero)
138             throw new InvalidOperationException(SR.GetString(SR.CompilationMutex_Null));
139
140         Debug.Trace("Mutex", "Releasing mutex " + MutexDebugName);
141
142 #if MUTEXINSTRUMENTATION
143         // Clear out the stack trace
144         _stackTrace = null;
145 #endif
146
147         if (UnsafeNativeMethods.InstrumentedMutexReleaseLock(_mutexHandle) != 0)
148             Interlocked.Decrement(ref _lockStatus);
149 #endif // !FEATURE_PAL
150     }
151
152
153     private String MutexDebugName {
154         get {
155 #if DBG
156             return (_comment != null) ? _name + " (" + _comment + ")" : _name;
157 #else
158             return _name;
159 #endif
160         }
161     }
162 }
163
164 internal static class CompilationLock {
165
166     private static CompilationMutex _mutex;
167
168     static CompilationLock() {
169
170         // Create the mutex (or just get it if another process created it).
171         // Make the mutex unique per application
172         int hashCode = StringUtil.GetNonRandomizedHashCode("CompilationLock" + HttpRuntime.AppDomainAppId.ToLower(CultureInfo.InvariantCulture));
173
174
175         _mutex = new CompilationMutex(
176                         "CL" + hashCode.ToString("x", CultureInfo.InvariantCulture), 
177                         "CompilationLock for " + HttpRuntime.AppDomainAppVirtualPath);
178     }
179
180     internal static void GetLock(ref bool gotLock) {
181
182         // The idea of this try/finally is to make sure that the statements are always
183         // executed together (VSWhidbey 319154)
184         // This code should be using a constrained execution region.
185         try {
186         }
187         finally {
188             // Always take the BuildManager lock *before* taking the mutex, to avoid possible
189             // deadlock situations (VSWhidbey 530732)
190 #pragma warning disable 0618
191             //@TODO: This overload of Monitor.Enter is obsolete.  Please change this to use Monitor.Enter(ref bool), and remove the pragmas   -- Microsoft
192             Monitor.Enter(BuildManager.TheBuildManager);
193 #pragma warning restore 0618
194             _mutex.WaitOne();
195             gotLock = true;
196         }
197     }
198
199     internal static void ReleaseLock() {
200         _mutex.ReleaseMutex();
201         Monitor.Exit(BuildManager.TheBuildManager);
202     }
203
204 }
205
206 }