Code:
/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / clr / src / BCL / System / Threading / SpinLock.cs / 1305376 / SpinLock.cs
#pragma warning disable 0420 // ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ // // SpinLock.cs // A spin lock is a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop ("spins") // repeatedly checking until the lock becomes available. As the thread remains active performing a non-useful task, // the use of such a lock is a kind of busy waiting and consumes CPU resources without performing real work. // //[....] // // =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=- using System; using System.Diagnostics; using System.Security.Permissions; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using System.Runtime.ConstrainedExecution; using System.Diagnostics.Contracts; namespace System.Threading { ////// Provides a mutual exclusion lock primitive where a thread trying to acquire the lock waits in a loop /// repeatedly checking until the lock becomes available. /// ////// [ComVisible(false)] [HostProtection(Synchronization = true, ExternalThreading = true)] [DebuggerTypeProxy(typeof(SystemThreading_SpinLockDebugView))] [DebuggerDisplay("IsHeld = {IsHeld}")] public struct SpinLock { // The current ownership state is a single signed int. There are two modes: // // 1) Ownership tracking enabled: the high bit is 0, and the remaining bits // store the managed thread ID of the current owner. When the 31 low bits // are 0, the lock is available. // 2) Performance mode: when the high bit is 1, lock availability is indicated by the low bit. // When the low bit is 1 -- the lock is held; 0 -- the lock is available. // // There are several masks and constants below for convenience. private volatile int m_owner; // The multiplier factor for the each spinning iteration // This number has been chosen after trying different numbers on different CPUs (4, 8 and 16 ) and this provided the best results private const int SPINNING_FACTOR = 100; // After how many yields, call Sleep(1) private const int SLEEP_ONE_FREQUENCY = 40; // After how many yields, call Sleep(0) private const int SLEEP_ZERO_FREQUENCY = 10; // After how many yields, check the timeout private const int TIMEOUT_CHECK_FREQUENCY = 10; // Thr thread tracking disabled mask private const int LOCK_ID_DISABLE_MASK = unchecked((int)0x80000000); //1000 0000 0000 0000 0000 0000 0000 0000 //the lock is held by some thread, but we don't know which private const int LOCK_ANONYMOUS_OWNED = 0x1; //0000 0000 0000 0000 0000 0000 0000 0001 // Waiters mask if the thread tracking is disabled private const int WAITERS_MASK = ~(LOCK_ID_DISABLE_MASK | 1); //0111 1111 1111 1111 1111 1111 1111 1110 // If the thread is unowned if: // m_owner zero and the threa tracking is enabled // m_owner & LOCK_ANONYMOUS_OWNED = zero and the thread tracking is disabled private const int LOCK_UNOWNED = 0; // The maximum number of waiters (only used if the thread tracking is disabled) // The actual maximum waiters count is this number divided by two because each waiter increments the waiters count by 2 // The waiters count is calculated by m_owner & WAITERS_MASK 01111....110 private static int MAXIMUM_WAITERS = WAITERS_MASK; ////// Spin locks can be used for leaf-level locks where the object allocation implied by using a ///, in size or due to garbage collection pressure, is overly /// expensive. Avoiding blocking is another reason that a spin lock can be useful, however if you expect /// any significant amount of blocking, you are probably best not using spin locks due to excessive /// spinning. Spinning can be beneficial when locks are fine grained and large in number (for example, a /// lock per node in a linked list) as well as when lock hold times are always extremely short. In /// general, while holding a spin lock, one should avoid blocking, calling anything that itself may /// block, holding more than one spin lock at once, making dynamically dispatched calls (interface and /// virtuals), making statically dispatched calls into any code one doesn't own, or allocating memory. /// /// ///should only be used when it's been determined that doing so will improve an /// application's performance. It's also important to note that is a value type, /// for performance reasons. As such, one must be very careful not to accidentally copy a SpinLock /// instance, as the two instances (the original and the copy) would then be completely independent of /// one another, which would likely lead to erroneous behavior of the application. If a SpinLock instance /// must be passed around, it should be passed by reference rather than by value. /// /// Do not store ///instances in readonly fields. /// /// All members of ///are thread-safe and may be used from multiple threads /// concurrently. /// /// Initializes a new instance of the ////// structure with the option to track thread IDs to improve debugging. /// /// The default constructor for /// Whether to capture and use thread IDs for debugging /// purposes. public SpinLock(bool enableThreadOwnerTracking) { m_owner = LOCK_UNOWNED; if (!enableThreadOwnerTracking) { m_owner |= LOCK_ID_DISABLE_MASK; Contract.Assert(!IsThreadOwnerTrackingEnabled, "property should be false by now"); } } ///tracks thread ownership. /// /// Initializes a new instance of the ////// structure with the option to track thread IDs to improve debugging. /// /// The default constructor for ///tracks thread ownership. /// /// Acquires the lock in a reliable manner, such that even if an exception occurs within the method /// call, ///can be examined reliably to determine whether the lock was /// acquired. /// /// /// True if the lock is acquired; otherwise, false.is a non-reentrant lock, meaning that if a thread holds the lock, it is /// not allowed to enter the lock again. If thread ownership tracking is enabled (whether it's /// enabled is available through ), an exception will be /// thrown when a thread tries to re-enter a lock it already holds. However, if thread ownership /// tracking is disabled, attempting to enter a lock already held will result in deadlock. /// must be initialized to false prior to calling this method. /// /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. /// ////// The public void Enter(ref bool lockTaken) { if (lockTaken) { lockTaken = false; throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException")); } // Fast path to acquire the lock if the lock is released // If the thread tracking enabled set the new owner to the current thread id // Id not, set the anonymous bit lock int observedOwner = m_owner; int newOwner = 0; bool threadTrackingEnabled = (m_owner & LOCK_ID_DISABLE_MASK) == 0; if (threadTrackingEnabled) { if (observedOwner == LOCK_UNOWNED) newOwner = Thread.CurrentThread.ManagedThreadId; } else if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { newOwner = observedOwner | LOCK_ANONYMOUS_OWNED; // set the lock bit } if (newOwner != 0) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif #if PFX_LEGACY_3_5 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner) { lockTaken = true; return; } #else if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { // Fast path succeeded return; } #endif #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } //Fast path failed, try slow path ContinueTryEnter(Timeout.Infinite, ref lockTaken); } ///argument must be initialized to false prior to calling Enter. /// /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within /// the method call, ///can be examined reliably to determine whether the /// lock was acquired. /// /// Unlike /// True if the lock is acquired; otherwise, false., TryEnter will not block waiting for the lock to be available. If the /// lock is not available when TryEnter is called, it will return immediately without any further /// spinning. /// must be initialized to false prior to calling this method. /// /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. /// ////// The public void TryEnter(ref bool lockTaken) { TryEnter(0, ref lockTaken); } ///argument must be initialized to false prior to calling TryEnter. /// /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within /// the method call, ///can be examined reliably to determine whether the /// lock was acquired. /// /// Unlike /// A, TryEnter will not block indefinitely waiting for the lock to be /// available. It will block until either the lock is available or until the /// has expired. /// that represents the number of milliseconds /// to wait, or a that represents -1 milliseconds to wait indefinitely. /// /// True if the lock is acquired; otherwise, false. must be initialized to false prior to calling this method. /// /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. /// ////// The ///argument must be initialized to false prior to calling TryEnter. /// public void TryEnter(TimeSpan timeout, ref bool lockTaken) { // Validate the timeout Int64 totalMilliseconds = (Int64)timeout.TotalMilliseconds; if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) { throw new System.ArgumentOutOfRangeException( "timeout", timeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); } // Call reliable enter with the int-based timeout milliseconds TryEnter((int)timeout.TotalMilliseconds, ref lockTaken); } /// is a negative /// number other than -1 milliseconds, which represents an infinite time-out -or- timeout is greater /// than milliseconds. /// /// Attempts to acquire the lock in a reliable manner, such that even if an exception occurs within /// the method call, ///can be examined reliably to determine whether the /// lock was acquired. /// /// Unlike /// The number of milliseconds to wait, or, TryEnter will not block indefinitely waiting for the lock to be /// available. It will block until either the lock is available or until the has expired. /// (-1) to wait indefinitely. /// True if the lock is acquired; otherwise, false. must be initialized to false prior to calling this method. /// /// Thread ownership tracking is enabled, and the current thread has already acquired this lock. /// ////// The ///argument must be initialized to false prior to calling TryEnter. /// public void TryEnter(int millisecondsTimeout, ref bool lockTaken) { // validate input if (lockTaken) { lockTaken = false; throw new System.ArgumentException(Environment.GetResourceString("SpinLock_TryReliableEnter_ArgumentException")); } if (millisecondsTimeout < -1) { throw new ArgumentOutOfRangeException( "millisecondsTimeout", millisecondsTimeout, Environment.GetResourceString("SpinLock_TryEnter_ArgumentOutOfRange")); } // Fast path to acquire the lock if the lock is released // If the thread tracking enabled set the new owner to the current thread id // Id not, set the anonymous bit lock int observedOwner = m_owner; int newOwner = 0; if (IsThreadOwnerTrackingEnabled) { if (observedOwner == LOCK_UNOWNED) newOwner = Thread.CurrentThread.ManagedThreadId; } else if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { newOwner = observedOwner | LOCK_ANONYMOUS_OWNED; } if (newOwner != 0) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif #if PFX_LEGACY_3_5 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner) { lockTaken = true; return; } #else if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } #endif #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } // Fast path failed, try slow path ContinueTryEnter(millisecondsTimeout, ref lockTaken); } /// is /// a negative number other than -1, which represents an infinite time-out. /// Try acquire the lock with long path, this is usually called after the first path in Enter and /// TryEnter failed The reason for short path is to make it inline in the run time which improves the /// performance. This method assumed that the parameter are validated in Enter ir TryENter method /// /// The timeout milliseconds /// The lockTaken param private void ContinueTryEnter(int millisecondsTimeout, ref bool lockTaken) { long startTicks = 0; if (millisecondsTimeout != Timeout.Infinite && millisecondsTimeout != 0) { startTicks = DateTime.UtcNow.Ticks; } #if !FEATURE_PAL && !FEATURE_CORECLR // PAL doesn't support eventing, and we don't compile CDS providers for Coreclr if (CdsSyncEtwBCLProvider.Log.IsEnabled()) { CdsSyncEtwBCLProvider.Log.SpinLock_FastPathFailed(m_owner); } #endif if (IsThreadOwnerTrackingEnabled) { // Slow path for enabled thread tracking mode ContinueTryEnterWithThreadTracking(millisecondsTimeout, startTicks, ref lockTaken); return; } // then thread tracking is disabled // In this case there are three ways to acquire the lock // 1- the first way the thread either tries to get the lock if it's free or updates the waiters, if the turn >= the processors count then go to 3 else go to 2 // 2- In this step the waiter threads spins and tries to acquire the lock, the number of spin iterations and spin count is dependent on the thread turn // the late the thread arrives the more it spins and less frequent it check the lock avilability // Also the spins count is increaes each iteration // If the spins iterations finished and failed to acquire the lock, go to step 3 // 3- This is the yielding step, there are two ways of yielding Thread.Yield and Sleep(1) // If the timeout is expired in after step 1, we need to decrement the waiters count before returning int observedOwner; //***Step 1, take the lock or update the waiters // try to acquire the lock directly if possoble or update the waiters count SpinWait spinner = new SpinWait(); while (true) { observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif #if PFX_LEGACY_3_5 if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner) == observedOwner) { lockTaken = true; return; } #else if (Interlocked.CompareExchange(ref m_owner, observedOwner | 1, observedOwner, ref lockTaken) == observedOwner) { return; } #endif #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } else //failed to acquire the lock,then try to update the waiters. If the waiters count reached the maximum, jsut break the loop to avoid overflow if ((observedOwner & WAITERS_MASK) == MAXIMUM_WAITERS || Interlocked.CompareExchange(ref m_owner, observedOwner + 2, observedOwner) == observedOwner) break; spinner.SpinOnce(); } // Check the timeout. if (millisecondsTimeout == 0 || (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout))) { DecrementWaiters(); return; } //***Step 2. Spinning //lock acquired failed and waiters updated int turn = ((observedOwner + 2) & WAITERS_MASK) / 2; int processorCount = PlatformHelper.ProcessorCount; if (turn < processorCount) { int processFactor = 1; for (int i = 1; i <= turn * SPINNING_FACTOR; i++) { Thread.SpinWait((turn + i) * SPINNING_FACTOR * processFactor); if (processFactor < processorCount) processFactor++; observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Contract.Assert((newOwner & WAITERS_MASK) >= 0); #if PFX_LEGACY_3_5 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner) { lockTaken = true; return; } #else if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } #endif #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } } } // Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout)) { DecrementWaiters(); return; } //*** Step 3, Yielding //Sleep(1) every 50 yields int yieldsoFar = 0; while (true) { observedOwner = m_owner; if ((observedOwner & LOCK_ANONYMOUS_OWNED) == LOCK_UNOWNED) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif int newOwner = (observedOwner & WAITERS_MASK) == 0 ? // Gets the number of waiters, if zero observedOwner | 1 // don't decrement it. just set the lock bit, it is zzero because a previous call of Exit(false) ehich corrupted the waiters : (observedOwner - 2) | 1; // otherwise decrement the waiters and set the lock bit Contract.Assert((newOwner & WAITERS_MASK) >= 0); #if PFX_LEGACY_3_5 if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner) == observedOwner) { lockTaken = true; return; } #else if (Interlocked.CompareExchange(ref m_owner, newOwner, observedOwner, ref lockTaken) == observedOwner) { return; } #endif #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } if (yieldsoFar % SLEEP_ONE_FREQUENCY == 0) { Thread.Sleep(1); } else if (yieldsoFar % SLEEP_ZERO_FREQUENCY == 0) { Thread.Sleep(0); } else { #if PFX_LEGACY_3_5 Platform.Yield(); #else Thread.Yield(); #endif } if (yieldsoFar % TIMEOUT_CHECK_FREQUENCY == 0) { //Check the timeout. if (millisecondsTimeout != Timeout.Infinite && TimeoutExpired(startTicks, millisecondsTimeout)) { DecrementWaiters(); return; } } yieldsoFar++; } } ////// decrements the waiters, in case of the timeout is expired /// private void DecrementWaiters() { SpinWait spinner = new SpinWait(); while (true) { int observedOwner = m_owner; if ((observedOwner & WAITERS_MASK) == 0) return; // don't decrement the waiters if it's corrupted by previous call of Exit(false) if (Interlocked.CompareExchange(ref m_owner, observedOwner - 2, observedOwner) == observedOwner) { Contract.Assert(!IsThreadOwnerTrackingEnabled); // Make sure the waiters never be negative which will cause the thread tracking bit to be flipped break; } spinner.SpinOnce(); } } ////// ContinueTryEnter for the thread tracking mode enabled /// private void ContinueTryEnterWithThreadTracking(int millisecondsTimeout, long startTicks, ref bool lockTaken) { Contract.Assert(IsThreadOwnerTrackingEnabled); int lockUnowned = 0; // We are using thread IDs to mark ownership. Snap the thread ID and check for recursion. // We also must or the ID enablement bit, to ensure we propagate when we CAS it in. int m_newOwner = Thread.CurrentThread.ManagedThreadId; if (m_owner == m_newOwner) { // We don't allow lock recursion. throw new LockRecursionException(Environment.GetResourceString("SpinLock_TryEnter_LockRecursionException")); } SpinWait spinner = new SpinWait(); // Loop until the lock has been successfully acquired or, if specified, the timeout expires. do { // We failed to get the lock, either from the fast route or the last iteration // and the timeout hasn't expired; spin once and try again. spinner.SpinOnce(); // Test before trying to CAS, to avoid acquiring the line exclusively unnecessarily. if (m_owner == lockUnowned) { #if !FEATURE_CORECLR Thread.BeginCriticalRegion(); #endif #if PFX_LEGACY_3_5 if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned) == lockUnowned) { lockTaken = true; return; } #else if (Interlocked.CompareExchange(ref m_owner, m_newOwner, lockUnowned, ref lockTaken) == lockUnowned) { return; } #endif #if !FEATURE_CORECLR // The thread failed to get the lock, so we don't need to remain in a critical region. Thread.EndCriticalRegion(); #endif } // Check the timeout. We only RDTSC if the next spin will yield, to amortize the cost. if (millisecondsTimeout == 0 || (millisecondsTimeout != Timeout.Infinite && spinner.NextSpinWillYield && TimeoutExpired(startTicks, millisecondsTimeout))) { return; } } while (true); } ////// Helper function to validate the timeout /// /// The start time in ticks /// The orginal wait time ///True if expired, false otherwise private static bool TimeoutExpired(long startTicks, int originalWaitTime) { Contract.Assert(originalWaitTime != Timeout.Infinite); long elapsedTicks = DateTime.UtcNow.Ticks - startTicks; return (elapsedTicks >= (originalWaitTime * TimeSpan.TicksPerMillisecond)); } ////// Releases the lock. /// ////// The default overload of ///provides the same behavior as if calling using true as the argument. /// /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock. /// [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public void Exit() { Exit(true); } ////// Releases the lock. /// /// /// A Boolean value that indicates whether a memory fence should be issued in order to immediately /// publish the exit operation to other threads. /// ////// Calling ///with the argument set to /// true will improve the fairness of the lock at the expense of some performance. The default /// overload behaves as if specifying true for . /// /// Thread ownership tracking is enabled, and the current thread is not the owner of this lock. /// [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] public void Exit(bool useMemoryBarrier) { if (IsThreadOwnerTrackingEnabled && !IsHeldByCurrentThread) { throw new System.Threading.SynchronizationLockException( Environment.GetResourceString("SpinLock_Exit_SynchronizationLockException")); } if (useMemoryBarrier) { if (IsThreadOwnerTrackingEnabled) Interlocked.Exchange(ref m_owner, LOCK_UNOWNED); else Interlocked.Decrement(ref m_owner); } else { if (IsThreadOwnerTrackingEnabled) m_owner = LOCK_UNOWNED; else { int tmpOwner = m_owner; Contract.Assert((tmpOwner & LOCK_ANONYMOUS_OWNED) == LOCK_ANONYMOUS_OWNED, "The lock is not held."); m_owner = tmpOwner - 1; } } #if !FEATURE_CORECLR Thread.EndCriticalRegion(); #endif } ////// Gets whether the lock is currently held by any thread. /// public bool IsHeld { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] get { if (IsThreadOwnerTrackingEnabled) return m_owner != LOCK_UNOWNED; return (m_owner & LOCK_ANONYMOUS_OWNED) != LOCK_UNOWNED; } } ////// Gets whether the lock is currently held by any thread. /// ////// Gets whether the lock is held by the current thread. /// ////// If the lock was initialized to track owner threads, this will return whether the lock is acquired /// by the current thread. It is invalid to use this property when the lock was initialized to not /// track thread ownership. /// ////// Thread ownership tracking is disabled. /// public bool IsHeldByCurrentThread { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] get { if (!IsThreadOwnerTrackingEnabled) { throw new InvalidOperationException(Environment.GetResourceString("SpinLock_IsHeldByCurrentThread")); } return ((m_owner & (~LOCK_ID_DISABLE_MASK)) == Thread.CurrentThread.ManagedThreadId); } } ///Gets whether thread ownership tracking is enabled for this instance. public bool IsThreadOwnerTrackingEnabled { [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] get { return (m_owner & LOCK_ID_DISABLE_MASK) == 0; } } #region Debugger proxy class ////// Internal class used by debug type proxy attribute to display the owner thread ID /// internal class SystemThreading_SpinLockDebugView { // SpinLock object private SpinLock m_spinLock; ////// SystemThreading_SpinLockDebugView constructor /// /// The SpinLock to be proxied. public SystemThreading_SpinLockDebugView(SpinLock spinLock) { // Note that this makes a copy of the SpinLock (struct). It doesn't hold a reference to it. m_spinLock = spinLock; } ////// Checks if the lock is held by the current thread or not /// public bool? IsHeldByCurrentThread { get { try { return m_spinLock.IsHeldByCurrentThread; } catch (InvalidOperationException) { return null; } } } ////// Gets the current owner thread, zero if it is released /// public int? OwnerThreadID { get { if (m_spinLock.IsThreadOwnerTrackingEnabled) { return m_spinLock.m_owner; } else { return null; } } } ////// Gets whether the lock is currently held by any thread or not. /// public bool IsHeld { get { return m_spinLock.IsHeld; } } } #endregion } } #pragma warning restore 0420 // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- UidManager.cs
- HtmlTernaryTree.cs
- NumericUpDownAccelerationCollection.cs
- COAUTHINFO.cs
- BackgroundFormatInfo.cs
- RtfFormatStack.cs
- _NestedSingleAsyncResult.cs
- Page.cs
- ProfileGroupSettings.cs
- PersonalizationStateQuery.cs
- followingquery.cs
- ConfigXmlElement.cs
- TraceRecord.cs
- AppDomainFactory.cs
- UnsafeNativeMethodsTablet.cs
- FileDialogPermission.cs
- DependencyObjectValidator.cs
- SessionEndingCancelEventArgs.cs
- IdentityReference.cs
- IPPacketInformation.cs
- Int32Converter.cs
- ProcessInfo.cs
- DelegatingTypeDescriptionProvider.cs
- GeometryCombineModeValidation.cs
- XomlCompilerError.cs
- ProxyWebPartManager.cs
- CacheChildrenQuery.cs
- Int64Storage.cs
- ImageListUtils.cs
- HitTestWithGeometryDrawingContextWalker.cs
- QilGenerator.cs
- ObjectDataProvider.cs
- ChannelOptions.cs
- ObjectSecurity.cs
- Hash.cs
- ActivationArguments.cs
- StrokeNodeOperations2.cs
- SecurityDescriptor.cs
- SqlInfoMessageEvent.cs
- Overlapped.cs
- AssertUtility.cs
- DirectionalLight.cs
- SerializationEventsCache.cs
- CodeExpressionStatement.cs
- ReachPageContentSerializerAsync.cs
- ComponentResourceKeyConverter.cs
- ScriptControlManager.cs
- SpoolingTaskBase.cs
- Focus.cs
- SqlDependencyUtils.cs
- MultipartIdentifier.cs
- GroupStyle.cs
- IdnMapping.cs
- SmiContext.cs
- MdImport.cs
- FileSecurity.cs
- RewritingValidator.cs
- DirectoryNotFoundException.cs
- ELinqQueryState.cs
- OutputWindow.cs
- CipherData.cs
- XmlFormatWriterGenerator.cs
- WS2007HttpBinding.cs
- AndCondition.cs
- Image.cs
- ToolStripCollectionEditor.cs
- RuntimeEnvironment.cs
- ScriptManager.cs
- ParseChildrenAsPropertiesAttribute.cs
- ConfigXmlText.cs
- EditableTreeList.cs
- InputReportEventArgs.cs
- MemoryPressure.cs
- ArraySegment.cs
- ExceptionValidationRule.cs
- VersionedStream.cs
- DynamicFilterExpression.cs
- DictionaryEntry.cs
- WebScriptMetadataMessageEncodingBindingElement.cs
- CqlLexer.cs
- XmlSchemaAttributeGroupRef.cs
- SelectionItemProviderWrapper.cs
- SpoolingTask.cs
- SkewTransform.cs
- InstanceCreationEditor.cs
- WebBrowser.cs
- ByteStorage.cs
- ToolStripOverflowButton.cs
- WebPartVerbCollection.cs
- figurelengthconverter.cs
- DrawingContextWalker.cs
- SystemIPv4InterfaceProperties.cs
- _IPv4Address.cs
- RequestCacheValidator.cs
- InteropBitmapSource.cs
- EnumValidator.cs
- HttpValueCollection.cs
- TextEditorTables.cs
- SyndicationItem.cs
- DataError.cs