示例#1
0
 public static void Release(CctorHandle cctor)
 {
     using (LockHolder.Hold(s_cctorGlobalLock))
     {
         Cctor[] cctors     = cctor.Array;
         int     cctorIndex = cctor.Index;
         if (0 == Interlocked.Decrement(ref cctors[cctorIndex]._refCount))
         {
             if (cctors[cctorIndex].Exception == null)
             {
                 cctors[cctorIndex] = default;
                 s_count--;
             }
         }
     }
 }
 public static void Release(CctorHandle cctor)
 {
     s_cctorGlobalLock.Acquire();
     try
     {
         Cctor[] cctors     = cctor.Array;
         int     cctorIndex = cctor.Index;
         if (0 == Interlocked.Decrement(ref cctors[cctorIndex]._refCount))
         {
             if (cctors[cctorIndex].Exception == null)
             {
                 cctors[cctorIndex] = new Cctor();
                 s_count--;
             }
         }
     }
     finally
     {
         s_cctorGlobalLock.Release();
     }
 }
示例#3
0
            public static int MarkThreadAsBlocked(int managedThreadId, CctorHandle blockedOn)
            {
#if DEBUG
                const int Grow = 2;
#else
                const int Grow = 10;
#endif
                using (LockHolder.Hold(s_cctorGlobalLock))
                {
                    if (s_blockingRecords == null)
                    {
                        s_blockingRecords = new BlockingRecord[Grow];
                    }
                    int found;
                    for (found = 0; found < s_nextBlockingRecordIndex; found++)
                    {
                        if (s_blockingRecords[found].ManagedThreadId == managedThreadId)
                        {
                            break;
                        }
                    }
                    if (found == s_nextBlockingRecordIndex)
                    {
                        if (s_nextBlockingRecordIndex == s_blockingRecords.Length)
                        {
                            BlockingRecord[] newBlockingRecords = new BlockingRecord[s_blockingRecords.Length + Grow];
                            for (int i = 0; i < s_blockingRecords.Length; i++)
                            {
                                newBlockingRecords[i] = s_blockingRecords[i];
                            }
                            s_blockingRecords = newBlockingRecords;
                        }
                        s_blockingRecords[s_nextBlockingRecordIndex].ManagedThreadId = managedThreadId;
                        s_nextBlockingRecordIndex++;
                    }
                    s_blockingRecords[found].BlockedOn = blockedOn;
                    return(found);
                }
            }
            public static int MarkThreadAsBlocked(int managedThreadId, CctorHandle blockedOn)
            {
#if DEBUG
                const int Grow = 2;
#else
                const int Grow = 10;
#endif
                lock (s_cctorGlobalLock)
                {
                    if (s_blockingRecords == null)
                        s_blockingRecords = new BlockingRecord[Grow];
                    int found;
                    for (found = 0; found < s_nextBlockingRecordIndex; found++)
                    {
                        if (s_blockingRecords[found].ManagedThreadId == managedThreadId)
                            break;
                    }
                    if (found == s_nextBlockingRecordIndex)
                    {
                        if (s_nextBlockingRecordIndex == s_blockingRecords.Length)
                        {
                            BlockingRecord[] newBlockingRecords = new BlockingRecord[s_blockingRecords.Length + Grow];
                            for (int i = 0; i < s_blockingRecords.Length; i++)
                            {
                                newBlockingRecords[i] = s_blockingRecords[i];
                            }
                            s_blockingRecords = newBlockingRecords;
                        }
                        s_blockingRecords[s_nextBlockingRecordIndex].ManagedThreadId = managedThreadId;
                        s_nextBlockingRecordIndex++;
                    }
                    s_blockingRecords[found].BlockedOn = blockedOn;
                    return found;
                }
            }
 public static void Release(CctorHandle cctor)
 {
     s_cctorGlobalLock.Acquire();
     try
     {
         Cctor[] cctors = cctor.Array;
         int cctorIndex = cctor.Index;
         if (0 == Interlocked.Decrement(ref cctors[cctorIndex]._refCount))
         {
             if (cctors[cctorIndex].Exception == null)
             {
                 cctors[cctorIndex] = new Cctor();
                 s_count--;
             }
         }
     }
     finally
     {
         s_cctorGlobalLock.Release();
     }
 }
        //=========================================================================================================
        // Return value:
        //   true   - lock acquired. 
        //   false  - deadlock detected. Lock not acquired.
        //=========================================================================================================
        private static bool DeadlockAwareAcquire(CctorHandle cctor, IntPtr pfnCctor)
        {
            const int WaitIntervalSeedInMS = 1;      // seed with 1ms and double every time through the loop
            const int WaitIntervalLimitInMS = WaitIntervalSeedInMS << 7; // limit of 128ms

            int waitIntervalInMS = WaitIntervalSeedInMS;

            int cctorIndex = cctor.Index;
            Cctor[] cctors = cctor.Array;
            Lock lck = cctors[cctorIndex].Lock;
            if (lck.IsAcquired)
                return false;     // Thread recursively triggered the same cctor.

            if (lck.TryAcquire(waitIntervalInMS))
                return true;

            // We couldn't acquire the lock. See if this .cctor is involved in a cross-thread deadlock.  If so, break 
            // the deadlock by breaking the guarantee - we'll skip running the .cctor and let the caller take his chances.
            int currentManagedThreadId = CurrentManagedThreadId;
            int unmarkCookie = -1;
            try
            {
                // We'll spin in a forever-loop of checking for a deadlock state, then waiting a short time, then 
                // checking for a deadlock state again, and so on. This is because the BlockedRecord info has a built-in 
                // lag time - threads don't report themselves as blocking until they've been blocked for a non-trivial 
                // amount of time. 
                //
                // If the threads are deadlocked for any reason other a class constructor cycling, this loop will never 
                // terminate - this is by design. If the user code inside the class constructors were to 
                // deadlock themselves, then that's a bug in user code.
                for (;;)
                {
                    lock (s_cctorGlobalLock)
                    {
                        // Ask the guy who holds the cctor lock we're trying to acquire who he's waiting for. Keep 
                        // walking down that chain until we either discover a cycle or reach a non-blocking state. Note 
                        // that reaching a non-blocking state is not proof that we've avoided a deadlock due to the 
                        // BlockingRecord reporting lag.
                        CctorHandle cctorWalk = cctor;
                        int chainStepCount = 0;
                        for (; chainStepCount < Cctor.Count; chainStepCount++)
                        {
                            int cctorWalkIndex = cctorWalk.Index;
                            Cctor[] cctorWalkArray = cctorWalk.Array;

                            int holdingThread = cctorWalkArray[cctorWalkIndex].HoldingThread;
                            if (holdingThread == currentManagedThreadId)
                            {
                                // Deadlock detected.  We will break the guarantee and return without running the .cctor.
                                DebugLog("A class constructor was skipped due to class constructor cycle. cctor={0}, thread={1}",
                                    pfnCctor, currentManagedThreadId);

                                // We are maintaining an invariant that the BlockingRecords never show a cycle because,
                                // before we add a record, we first check for a cycle.  As a result, once we've said 
                                // we're waiting, we are committed to waiting and will not need to skip running this 
                                // .cctor.
                                Debug.Assert(unmarkCookie == -1);
                                return false;
                            }

                            if (holdingThread == ManagedThreadIdNone)
                            {
                                // No one appears to be holding this cctor lock. Give the current thread some more time 
                                // to acquire the lock. 
                                break;
                            }

                            cctorWalk = BlockingRecord.GetCctorThatThreadIsBlockedOn(holdingThread);
                            if (cctorWalk.Array == null)
                            {
                                // The final thread in the chain appears to be blocked on nothing. Give the current 
                                // thread some more time to acquire the lock.
                                break;
                            }
                        }

                        // We don't allow cycles in the BlockingRecords, so we must always enumerate at most each entry,
                        // but never more.
                        Debug.Assert(chainStepCount < Cctor.Count);

                        // We have not discovered a deadlock, so let's register the fact that we're waiting on another 
                        // thread and continue to wait.  It is important that we only signal that we are blocked after 
                        // we check for a deadlock because, otherwise, we give all threads involved in the deadlock the 
                        // opportunity to break it themselves and that leads to "ping-ponging" between the cctors
                        // involved in the cycle, allowing intermediate cctor results to be observed.
                        //
                        // The invariant here is that we never 'publish' a BlockingRecord that forms a cycle.  So it is
                        // important that the look-for-cycle-and-then-publish-wait-status operation be atomic with 
                        // respect to other updates to the BlockingRecords.
                        if (unmarkCookie == -1)
                        {
                            NoisyLog("Mark thread blocked, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);

                            unmarkCookie = BlockingRecord.MarkThreadAsBlocked(currentManagedThreadId, cctor);
                        }
                    } // _cctorGlobalLock scope

                    if (waitIntervalInMS < WaitIntervalLimitInMS)
                        waitIntervalInMS *= 2;

                    // We didn't find a cycle yet, try to take the lock again.
                    if (lck.TryAcquire(waitIntervalInMS))
                        return true;
                } // infinite loop
            }
            finally
            {
                if (unmarkCookie != -1)
                {
                    NoisyLog("Unmark thread blocked, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);
                    BlockingRecord.UnmarkThreadAsBlocked(unmarkCookie);
                }
            }
        }
示例#7
0
        public static unsafe void EnsureClassConstructorRun(StaticClassConstructionContext *pContext)
        {
            IntPtr pfnCctor = pContext->cctorMethodAddress;

            NoisyLog("EnsureClassConstructorRun, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId);

            // If we were called from MRT, this check is redundant but harmless. This is in case someone within classlib
            // (cough, Reflection) needs to call this explicitly.
            if (pContext->initialized == 1)
            {
                NoisyLog("Cctor already run, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId);
                return;
            }

            CctorHandle cctor = Cctor.GetCctor(pContext);

            Cctor[] cctors     = cctor.Array;
            int     cctorIndex = cctor.Index;

            try
            {
                Lock cctorLock = cctors[cctorIndex].Lock;
                if (DeadlockAwareAcquire(cctor, pfnCctor))
                {
                    int currentManagedThreadId = CurrentManagedThreadId;
                    try
                    {
                        NoisyLog("Acquired cctor lock, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);

                        cctors[cctorIndex].HoldingThread = currentManagedThreadId;
                        if (pContext->initialized == 0)  // Check again in case some thread raced us while we were acquiring the lock.
                        {
                            TypeInitializationException priorException = cctors[cctorIndex].Exception;
                            if (priorException != null)
                            {
                                throw priorException;
                            }
                            try
                            {
                                NoisyLog("Calling cctor, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);

                                ((delegate * < void >)pfnCctor)();

                                // Insert a memory barrier here to order any writes executed as part of static class
                                // construction above with respect to the initialized flag update we're about to make
                                // below. This is important since the fast path for checking the cctor uses a normal read
                                // and doesn't come here so without the barrier it could observe initialized == 1 but
                                // still see uninitialized static fields on the class.
                                Interlocked.MemoryBarrier();

                                NoisyLog("Set type inited, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);

                                pContext->initialized = 1;
                            }
                            catch (Exception e)
                            {
                                TypeInitializationException wrappedException = new TypeInitializationException(null, SR.TypeInitialization_Type_NoTypeAvailable, e);
                                cctors[cctorIndex].Exception = wrappedException;
                                throw wrappedException;
                            }
                        }
                    }
                    finally
                    {
                        cctors[cctorIndex].HoldingThread = ManagedThreadIdNone;
                        NoisyLog("Releasing cctor lock, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);

                        cctorLock.Release();
                    }
                }
                else
                {
                    // Cctor cycle resulted in a deadlock. We will break the guarantee and return without running the
                    // .cctor.
                }
            }
            finally
            {
                Cctor.Release(cctor);
            }
            NoisyLog("EnsureClassConstructorRun complete, cctor={0}, thread={1}", pfnCctor, CurrentManagedThreadId);
        }
示例#8
0
        //=========================================================================================================
        // Return value:
        //   true   - lock acquired.
        //   false  - deadlock detected. Lock not acquired.
        //=========================================================================================================
        private static bool DeadlockAwareAcquire(CctorHandle cctor, IntPtr pfnCctor)
        {
            const int WaitIntervalSeedInMS  = 1;                         // seed with 1ms and double every time through the loop
            const int WaitIntervalLimitInMS = WaitIntervalSeedInMS << 7; // limit of 128ms

            int waitIntervalInMS = WaitIntervalSeedInMS;

            int cctorIndex = cctor.Index;

            Cctor[] cctors = cctor.Array;
            Lock    lck    = cctors[cctorIndex].Lock;

            if (lck.IsAcquired)
            {
                return(false);     // Thread recursively triggered the same cctor.
            }
            if (lck.TryAcquire(waitIntervalInMS))
            {
                return(true);
            }

            // We couldn't acquire the lock. See if this .cctor is involved in a cross-thread deadlock.  If so, break
            // the deadlock by breaking the guarantee - we'll skip running the .cctor and let the caller take his chances.
            int currentManagedThreadId = CurrentManagedThreadId;
            int unmarkCookie           = -1;

            try
            {
                // We'll spin in a forever-loop of checking for a deadlock state, then waiting a short time, then
                // checking for a deadlock state again, and so on. This is because the BlockedRecord info has a built-in
                // lag time - threads don't report themselves as blocking until they've been blocked for a non-trivial
                // amount of time.
                //
                // If the threads are deadlocked for any reason other a class constructor cycling, this loop will never
                // terminate - this is by design. If the user code inside the class constructors were to
                // deadlock themselves, then that's a bug in user code.
                for (;;)
                {
                    using (LockHolder.Hold(s_cctorGlobalLock))
                    {
                        // Ask the guy who holds the cctor lock we're trying to acquire who he's waiting for. Keep
                        // walking down that chain until we either discover a cycle or reach a non-blocking state. Note
                        // that reaching a non-blocking state is not proof that we've avoided a deadlock due to the
                        // BlockingRecord reporting lag.
                        CctorHandle cctorWalk      = cctor;
                        int         chainStepCount = 0;
                        for (; chainStepCount < Cctor.Count; chainStepCount++)
                        {
                            int     cctorWalkIndex = cctorWalk.Index;
                            Cctor[] cctorWalkArray = cctorWalk.Array;

                            int holdingThread = cctorWalkArray[cctorWalkIndex].HoldingThread;
                            if (holdingThread == currentManagedThreadId)
                            {
                                // Deadlock detected.  We will break the guarantee and return without running the .cctor.
                                DebugLog("A class constructor was skipped due to class constructor cycle. cctor={0}, thread={1}",
                                         pfnCctor, currentManagedThreadId);

                                // We are maintaining an invariant that the BlockingRecords never show a cycle because,
                                // before we add a record, we first check for a cycle.  As a result, once we've said
                                // we're waiting, we are committed to waiting and will not need to skip running this
                                // .cctor.
                                Debug.Assert(unmarkCookie == -1);
                                return(false);
                            }

                            if (holdingThread == ManagedThreadIdNone)
                            {
                                // No one appears to be holding this cctor lock. Give the current thread some more time
                                // to acquire the lock.
                                break;
                            }

                            cctorWalk = BlockingRecord.GetCctorThatThreadIsBlockedOn(holdingThread);
                            if (cctorWalk.Array == null)
                            {
                                // The final thread in the chain appears to be blocked on nothing. Give the current
                                // thread some more time to acquire the lock.
                                break;
                            }
                        }

                        // We don't allow cycles in the BlockingRecords, so we must always enumerate at most each entry,
                        // but never more.
                        Debug.Assert(chainStepCount < Cctor.Count);

                        // We have not discovered a deadlock, so let's register the fact that we're waiting on another
                        // thread and continue to wait.  It is important that we only signal that we are blocked after
                        // we check for a deadlock because, otherwise, we give all threads involved in the deadlock the
                        // opportunity to break it themselves and that leads to "ping-ponging" between the cctors
                        // involved in the cycle, allowing intermediate cctor results to be observed.
                        //
                        // The invariant here is that we never 'publish' a BlockingRecord that forms a cycle.  So it is
                        // important that the look-for-cycle-and-then-publish-wait-status operation be atomic with
                        // respect to other updates to the BlockingRecords.
                        if (unmarkCookie == -1)
                        {
                            NoisyLog("Mark thread blocked, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);

                            unmarkCookie = BlockingRecord.MarkThreadAsBlocked(currentManagedThreadId, cctor);
                        }
                    } // _cctorGlobalLock scope

                    if (waitIntervalInMS < WaitIntervalLimitInMS)
                    {
                        waitIntervalInMS *= 2;
                    }

                    // We didn't find a cycle yet, try to take the lock again.
                    if (lck.TryAcquire(waitIntervalInMS))
                    {
                        return(true);
                    }
                } // infinite loop
            }
            finally
            {
                if (unmarkCookie != -1)
                {
                    NoisyLog("Unmark thread blocked, cctor={0}, thread={1}", pfnCctor, currentManagedThreadId);
                    BlockingRecord.UnmarkThreadAsBlocked(unmarkCookie);
                }
            }
        }
示例#9
0
 public static void Release(CctorHandle cctor)
 {
     using (LockHolder.Hold(s_cctorGlobalLock))
     {
         Cctor[] cctors = cctor.Array;
         int cctorIndex = cctor.Index;
         if (0 == Interlocked.Decrement(ref cctors[cctorIndex]._refCount))
         {
             if (cctors[cctorIndex].Exception == null)
             {
                 cctors[cctorIndex] = new Cctor();
                 s_count--;
             }
         }
     }
 }