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