// ==++== // // Copyright (c) Microsoft Corporation. All rights reserved. // // ==--== // #define DEBUG_SWITCH namespace System.Threading { using System.Threading; using System.Runtime.InteropServices; using System; using System.Diagnostics; using System.GCs; using System.Globalization; using System.Collections; using System.Runtime.CompilerServices; using Microsoft.Bartok.Runtime; using Microsoft.Singularity; using Microsoft.Singularity.Channels; using Microsoft.Singularity.X86; using Microsoft.Singularity.V1.Services; using Microsoft.Singularity.V1.Threads; //| [CCtorIsRunDuringStartup] [StructLayout(LayoutKind.Sequential)] [CLSCompliant(false)] [RequiredByBartok] public sealed class Thread { // Singularity specific fields // Need CPU context // Need stack // Bartok specific fields [RequiredByBartok] // Thread-specific alloc heap internal SegregatedFreeList segregatedFreeList; [RequiredByBartok] // Thread-specific bump allocator internal BumpAllocator bumpAllocator; internal AutoResetEvent processGcEvent; private AutoResetEvent autoEvent; private ManualResetEvent joinEvent; internal Thread blockingCctorThread; // Singularity specific fields [AccessedByRuntime("referenced from halforgc.asm")] internal unsafe ThreadContext * context; internal ThreadHandle threadHandle; // Most recently thrown exception object that the thread // did not catch at all (i.e. that propagated to the bottom // of the stack or to a kernel/process boundary without // encountering an appropriate catch clause). private Exception lastUncaughtException; private bool ignoredByJoinAll; // for "service" threads private Object m_ExceptionStateInfo; // Exception info latched to the thread on a thread abort // Bartok specific fields internal Thread nextThread; // Link for linked lists of threads // This array is of length 1 and contains a queue item for this // thread. It allows WaitOne and the scheduler to add this thread // to their queues without allocating memory. internal ThreadQueueItem[] singleQueueItem; // MultiUseWord (object header) fields. internal UIntPtr externalMultiUseObjAllocListHead; internal UIntPtr externalMultiUseObjAllocListTail; // Bartok specific fields internal int threadIndex; private ThreadStart threadStart; private ThreadState threadState; internal int waitingCriticalSectionDepth; internal Object waitingObject; // Not used, but useful for debugging [RequiredByBartok] internal TryAllManager tryAllManager; private static bool closing; private static long totalArrayAllocations; private static long totalArrayBytesAllocated; private static long totalBytes; private static long totalStructBytesAllocated; internal static Thread[] threadTable; private static SpinLock threadTableLock; private static LocalDataStore localDataStore; internal static Thread initialThread; // This is used by the Bartok backend. When Bartok tries to generate // callback stub for delegate, it checks to see if it is // ThreadProc, if it is not, Bartok adds leaveGCSafeState and // enterGCSafeState around the delegate call. [RequiredByBartok] private unsafe delegate uint ThreadProc(void *param); /*========================================================================= ** This manager is responsible for storing the global data that is ** shared amongst all the thread local stores. =========================================================================*/ static private LocalDataStoreMgr m_LocalDataStoreMgr; internal const int maxThreads = 1024; // Must be power of 2 >= 64 private static int threadIndexGenerator; /*========================================================================= ** Creates a new Thread object which will begin execution at ** start.ThreadStart on a new thread when the Start method is called. ** ** Exceptions: ArgumentNullException if start == null. =========================================================================*/ //| public unsafe Thread(ThreadStart start) { Tracing.Log(Tracing.Audit, "Application Thread()"); if (start == null) { throw new ArgumentNullException("start"); } threadIndex = -1; threadState = ThreadState.Unstarted; threadStart = start; // Create the event for the thread to wait upon autoEvent = new AutoResetEvent(false); joinEvent = new ManualResetEvent(false); processGcEvent = new AutoResetEvent(false); singleQueueItem = new ThreadQueueItem [1] { new ThreadQueueItem(this) }; // Find a usable entry in the thread table bool disabled = Processor.DisableInterrupts(); Thread.threadTableLock.Acquire(CurrentThread); try { for (int i = 0; i < threadTable.Length; i++) { int index = (threadIndexGenerator + i) % threadTable.Length; if (threadTable[index] == null) { threadTable[index] = this; this.threadIndex = index; threadIndexGenerator = index + 1; // NB: We call this once, afterwards the GC visitor calls it. break; } } } finally { Thread.threadTableLock.Release(CurrentThread); Processor.RestoreInterrupts(disabled); } VTable.Assert(threadIndex >= 0, "Out of thread slots!"); //MemoryBarrier(); // Must check closing after being insert into table to avoid race condition. if (closing) { threadState = ThreadState.Stopped; joinEvent.Set(); Tracing.Log(Tracing.Warning, "Aborting: Runtime closing."); return; } ThreadHandle handleOnStack; UIntPtr threadContext; if (!ThreadHandle.Create(threadIndex, new ContainerHandle(), out handleOnStack, out threadContext)) { Tracing.Log(Tracing.Warning, "Aborting: ThreadHandle.Create failed."); threadState = ThreadState.Stopped; joinEvent.Set(); return; } this.threadHandle = handleOnStack; this.context = (ThreadContext *) threadContext; this.context->threadIndex = unchecked((ushort) threadIndex); this.context->UpdateAfterGC(this); } /// /// Finalizer is responsible for freeing handle that keeps corresponding /// kernel object live. /// ~Thread() { if (this.threadHandle.id != 0) { ThreadHandle.Dispose(this.threadHandle); this.threadHandle = new ThreadHandle(); } } /*========================================================================= ** Spawns off a new thread which will begin executing at the ThreadStart ** method on the IThreadable interface passed in the constructor. Once the ** thread is dead, it cannot be restarted with another call to Start. ** ** Exceptions: ThreadStateException if the thread has already been started. =========================================================================*/ //| public void Start() { lock ((Object) this) { if (closing) { throw new ThreadStateException("Cannot start thread when closing"); } ThreadState oldState = threadState; if (oldState != ThreadState.Unstarted) { throw new ThreadStateException("Cannot start thread in state "+oldState); } threadState = ThreadState.Running; // Tell the GC that we have created the thread GC.NewThreadNotification(this, false); ThreadHandle.Start(threadHandle); GC.KeepAlive(this); } } // HalInitContext sets ThreadStub as the first process code to be // executed in a new thread context. [AccessedByRuntime("referenced from hal.cpp")] private static unsafe void ThreadStub(int threadIndex) { Transitions.ThreadStart(); GC.ThreadStartNotification(threadIndex); Thread currentThread = threadTable[threadIndex]; if (AddThread(threadIndex)) { Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Entered", (UIntPtr)unchecked((uint)threadIndex)); ThreadStart startFun = currentThread.threadStart; try { startFun(); } catch (Exception e) { DebugStub.WriteLine("Thread {0} failed with exception {1}\n", __arglist(threadIndex, e)); currentThread.lastUncaughtException = e; VTable.Assert(e == null, "Thread "+threadIndex+ " failed with exception "+e); } Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Exiting", (UIntPtr)unchecked((uint)threadIndex)); } currentThread.joinEvent.Set(); RemoveThread(threadIndex); GC.DeadThreadNotification(currentThread); bool disabled = Processor.DisableInterrupts(); // this is a dangerous locking strategy as the transition code // may decide it needs to wake up another thread. Thread.threadTableLock.Acquire(currentThread); try { Transitions.ThreadEnd(threadIndex); // You may not do any calls out of proc after this point! threadTable[threadIndex] = null; Transitions.DeadThreadNotification(threadIndex); } finally { Thread.threadTableLock.Release(currentThread); Processor.RestoreInterrupts(disabled); } } /*==================================================================== * Support for service threads. Service threads should not count * towards keeping a process alive. When all non-service threads * have terminated, the service threads are asked to stop themselves * so the process can terminate gracefully. *===================================================================*/ // The number of non-service threads running in the process private static int threadCount; private static Object notificationTableObject; private static Object[] notificationTable; public delegate void StopServiceNotice(); public void MakeServiceThread(StopServiceNotice notification) { Tracing.Log(Tracing.Audit, "MakeServiceThread {0}", (UIntPtr) threadIndex); if (this != Thread.CurrentThread) { throw new Exception("Only the thread itself may call MakeServiceThread"); } if (notificationTable == null) { int tableSize = threadTable.Length; Interlocked.CompareExchange(ref notificationTableObject, new Object[tableSize], null); notificationTable = (Object[]) notificationTableObject; } Tracing.Log(Tracing.Audit, " previous notification: {0:x8}", Magic.addressOf(notificationTable[threadIndex])); // BUGBUG: Should have been Interlocked.Exchange, but that // doesn't work due to a Bartok codegen bug. Object oldNotification = notificationTable[threadIndex]; while (Interlocked.CompareExchange(ref notificationTable[threadIndex], notification, oldNotification) != oldNotification) { oldNotification = notificationTable[threadIndex]; } if (oldNotification == null) { // We made the thread a service thread for the first time. if (Interlocked.Decrement(ref threadCount) == 0) { NotifyServiceThreads(); } } VTable.Assert(threadCount >= 0); } public void ClearServiceThread(Thread thread) { Tracing.Log(Tracing.Audit, "ClearServiceThread"); if (this != Thread.CurrentThread) { throw new Exception("Only the thread itself may call ClearServiceThread"); } if (notificationTable == null) { return; } if (Interlocked.Exchange(ref notificationTable[threadIndex], null) != null) { // We cleared the notification Interlocked.Increment(ref threadCount); } VTable.Assert(threadCount >= 0); } private static bool AddThread(int index) { Tracing.Log(Tracing.Audit, "AddThread {0} ({1})", (UIntPtr) index, (UIntPtr) threadCount); VTable.Assert(threadCount >= 0); if (Interlocked.Increment(ref threadCount) == 1 && notificationTable != null) { // The thread was started after we started sending out // notifications, so indicate that the thread should not // really be started return false; } else { return true; } } internal static void RemoveThread(int index) { Tracing.Log(Tracing.Audit, "RemoveThread {0} ({1})", (UIntPtr) index, (UIntPtr) threadCount); if (Interlocked.Decrement(ref threadCount) == 0) { NotifyServiceThreads(); } VTable.Assert(threadCount >= 0); } private static void NotifyServiceThreads() { Tracing.Log(Tracing.Audit, "NotifyServiceThreads"); if (notificationTable == null) { return; } for (int i = 0; i < notificationTable.Length; i++) { if (notificationTable[i] != null) { Tracing.Log(Tracing.Audit, " Notifying thread {0}", (UIntPtr) i); ((StopServiceNotice)notificationTable[i])(); } } } /*========================================================================= ** Returns true if the thread has been started and is not dead. =========================================================================*/ //| public bool IsAlive { [NoHeapAllocation] get { return (threadState != ThreadState.Unstarted && threadState != ThreadState.Stopped); } } /*========================================================================= ** Waits for the thread to die. ** ** Exceptions: ThreadStateException if the thread has not been started yet. =========================================================================*/ //| public void Join() { Join(SchedulerTime.MaxValue); } /*========================================================================= ** Waits for the thread to die or for timeout milliseconds to elapse. ** Returns true if the thread died, or false if the wait timed out. ** ** Exceptions: ArgumentException if timeout < 0. ** ThreadStateException if the thread has not been started yet. =========================================================================*/ //| public bool Join(TimeSpan timeout) { if (threadState == ThreadState.Unstarted) { throw new ThreadStateException(); } return joinEvent.WaitOne(timeout); } public bool Join(SchedulerTime timeout) { if (threadState == ThreadState.Unstarted) { throw new ThreadStateException(); } return joinEvent.WaitOne(timeout); } internal static bool JoinAll() { // To avoid races, join all does the following: // 1) Wait for all known peer threads to terminate. // 2) Set the closing flag to disallow creating of new threads. // 3) Wait for any threads that have started in the mean time. for (uint iteration = 0; iteration < 2; iteration++) { for (int i = 0; i < threadTable.Length; i++) { Thread thread = null; bool disabled = Processor.DisableInterrupts(); Thread.threadTableLock.Acquire(CurrentThread); try { thread = threadTable[i]; } finally { Thread.threadTableLock.Release(CurrentThread); Processor.RestoreInterrupts(disabled); } if (thread != null && thread != CurrentThread && thread.threadState != ThreadState.Unstarted && !thread.ignoredByJoinAll) { thread.Join(); } } closing = true; } return true; } /*========================================================================= ** Suspends the current thread for timeout milliseconds. If timeout == 0, ** forces the thread to give up the remainder of its timeslice. ** ** Exceptions: ArgumentException if timeout < 0. =========================================================================*/ public static void Sleep(int milliseconds) { Sleep(TimeSpan.FromMilliseconds(milliseconds)); } //| public static void Sleep(TimeSpan timeout) { ThreadHandle.Sleep(timeout); } /* wait for a length of time proportional to 'iterations'. Each iteration is should only take a few machine instructions. Calling this API is preferable to coding an explicit busy loop because the hardware can be informed that it is busy waiting. */ //| [NoHeapAllocation] public static void SpinWait(int iterations) { for (int i = iterations; i > 0; i--) { // Ensure that the optimizer doesn't remove this NativeNoOp(); } } [Intrinsic] [NoHeapAllocation] public static extern void NativeNoOp(); internal static int GetCurrentProcessIndex() { return ProcessService.GetCurrentProcessId(); } internal bool WaitForMonitor(SchedulerTime timeOut) { return autoEvent.WaitOne(timeOut); } internal bool WaitForEvent(SchedulerTime timeOut) { DebugStub.Break(); return autoEvent.WaitOne(timeOut); } internal bool WaitForEvent(TimeSpan timeout) { DebugStub.Break(); return autoEvent.WaitOne(timeout); } internal static unsafe bool WaitForGCEvent(int currentThreadIndex) { AutoResetEvent are = threadTable[currentThreadIndex].processGcEvent; // BUGBUG: The restoration of the gcState should be taken // care of by the compiler. return are.WaitOneNoGC(); } internal void SignalMonitor() { autoEvent.Set(); } internal void SignalEvent() { DebugStub.Break(); autoEvent.Set(); } [Inline] internal static void SignalGCEvent(int currentThreadIndex, int threadIndex) { SignalGCEvent(threadIndex); } internal static unsafe void SignalGCEvent(int threadIndex) { Thread thread = threadTable[threadIndex]; if (thread == null) { return; } thread.processGcEvent.SetNoGC(); } //| public static Thread CurrentThread { [NoHeapAllocation] [NoStackLinkCheck] get { return Processor.GetCurrentThread(); } } internal ThreadHandle Handle { [NoHeapAllocation] get { return threadHandle; } } [NoStackLinkCheck] [RequiredByBartok] [NoHeapAllocation] private static Thread GetCurrentThreadNative() { return Processor.GetCurrentThread(); } [NoStackLinkCheck] [RequiredByBartok] [NoHeapAllocation] internal static int GetCurrentThreadIndex() { return Processor.GetCurrentThread().threadIndex; } /*========================================================================= ** Return the thread state as a consistent set of bits. This is more ** general then IsAlive or IsBackground. =========================================================================*/ //| public ThreadState ThreadState { [NoHeapAllocation] get { return threadState; } } [NoHeapAllocation] public int GetThreadId() { return threadIndex; } /*========================================================================= ** Allocates an un-named data slot. The slot is allocated on ALL the ** threads. =========================================================================*/ //| public static LocalDataStoreSlot AllocateDataSlot() { return m_LocalDataStoreMgr.AllocateDataSlot(); } /*========================================================================= ** Allocates a named data slot. The slot is allocated on ALL the ** threads. Named data slots are "public" and can be manipulated by ** anyone. =========================================================================*/ //| public static LocalDataStoreSlot AllocateNamedDataSlot(String name) { return m_LocalDataStoreMgr.AllocateNamedDataSlot(name); } /*========================================================================= ** Looks up a named data slot. If the name has not been used, a new slot is ** allocated. Named data slots are "public" and can be manipulated by ** anyone. =========================================================================*/ //| public static LocalDataStoreSlot GetNamedDataSlot(String name) { return m_LocalDataStoreMgr.GetNamedDataSlot(name); } /*========================================================================= ** Frees a named data slot. The slot is allocated on ALL the ** threads. Named data slots are "public" and can be manipulated by ** anyone. =========================================================================*/ //| public static void FreeNamedDataSlot(String name) { m_LocalDataStoreMgr.FreeNamedDataSlot(name); } /*========================================================================= ** Retrieves the value from the specified slot on the current thread. =========================================================================*/ //| public static Object GetData(LocalDataStoreSlot slot) { m_LocalDataStoreMgr.ValidateSlot(slot); if (localDataStore != null) { return localDataStore.GetData(slot); } return null; } /*========================================================================= ** Sets the data in the specified slot on the currently running thread. =========================================================================*/ //| public static void SetData(LocalDataStoreSlot slot, Object data) { // Create new DLS if one hasn't been created for this thread. if (localDataStore == null) { localDataStore = m_LocalDataStoreMgr.CreateLocalDataStore(); } localDataStore.SetData(slot, data); } /*=============================================================*/ internal Object ExceptionState { [NoHeapAllocation] get { return m_ExceptionStateInfo;} [NoHeapAllocation] set { m_ExceptionStateInfo = value;} } // // This is just designed to prevent compiler warnings. // This field is used from native, but we need to prevent the compiler warnings. // #if _DEBUG private void DontTouchThis() { threadStart = null; m_Priority = 0; } #endif /*========================================================================= ** Volatile Read & Write and MemoryBarrier methods. ** Provides the ability to read and write values ensuring that the values ** are read/written each time they are accessed. =========================================================================*/ //| [Intrinsic] [NoHeapAllocation] public static extern byte VolatileRead(ref byte address); //| [Intrinsic] [NoHeapAllocation] public static extern short VolatileRead(ref short address); //| [Intrinsic] [NoHeapAllocation] public static extern int VolatileRead(ref int address); //| [Intrinsic] [NoHeapAllocation] public static extern long VolatileRead(ref long address); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern sbyte VolatileRead(ref sbyte address); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern ushort VolatileRead(ref ushort address); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern uint VolatileRead(ref uint address); //| [Intrinsic] [NoHeapAllocation] public static extern IntPtr VolatileRead(ref IntPtr address); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern UIntPtr VolatileRead(ref UIntPtr address); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern ulong VolatileRead(ref ulong address); //| [Intrinsic] [NoHeapAllocation] public static extern float VolatileRead(ref float address); //| [Intrinsic] [NoHeapAllocation] public static extern double VolatileRead(ref double address); //| [Intrinsic] [NoHeapAllocation] public static extern Object VolatileRead(ref Object address); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref byte address, byte value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref short address, short value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref int address, int value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref long address, long value); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref sbyte address, sbyte value); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref ushort address, ushort value); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref uint address, uint value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref IntPtr address, IntPtr value); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref UIntPtr address, UIntPtr value); //| [CLSCompliant(false)] [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref ulong address, ulong value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref float address, float value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref double address, double value); //| [Intrinsic] [NoHeapAllocation] public static extern void VolatileWrite(ref Object address, Object value); //| [Intrinsic] [NoHeapAllocation] public static extern void MemoryBarrier(); [MethodImplAttribute(MethodImplOptions.InternalCall)] [GCAnnotation(GCOption.NOGC)] [StackBound(12)] [NoHeapAllocation] private static unsafe extern Thread HalGetThread(); [NoHeapAllocation] public static void Yield() { ThreadHandle.Yield(); } [NoHeapAllocation] public bool IsStopped() { return (threadState == ThreadState.Stopped); } [PreInitRefCounts] static unsafe Thread() { threadIndexGenerator = 1; // Enable Thread.CurrentThread as soon as we can! initialThread = Magic.toThread(BootstrapMemory.Allocate(typeof(Thread))); initialThread.threadState = ThreadState.Running; initialThread.threadIndex = 0; // Allocate tables for thread management threadTable = (Thread[]) BootstrapMemory.Allocate(typeof(Thread[]), maxThreads); // Initialize the thread and event tables threadTable[initialThread.threadIndex] = initialThread; initialThread.context = Processor.GetCurrentThreadContext(); initialThread.context->threadIndex = unchecked((ushort) initialThread.threadIndex); initialThread.context->UpdateAfterGC(initialThread); Tracing.Log(Tracing.Debug, "InitialThread={0:x8}", Magic.addressOf(initialThread)); } internal static unsafe void FinishInitializeThread() { int threadIndex = initialThread.threadIndex; // Get the GC ready for initialThread Transitions.RuntimeInitialized(); Transitions.ThreadStart(); initialThread.processGcEvent = new AutoResetEvent(false); initialThread.autoEvent = new AutoResetEvent(false); initialThread.joinEvent = new ManualResetEvent(false); initialThread.singleQueueItem = new ThreadQueueItem [1] { new ThreadQueueItem(initialThread) }; // Use CurrentThread to find our initial handle: VTable.Assert(initialThread == CurrentThread); initialThread.threadHandle = ThreadHandle.CurrentThread(); // Instantiate the static variable that needs to be initialized m_LocalDataStoreMgr = new LocalDataStoreMgr(); AddThread(threadIndex); } public TimeSpan ExecutionTime { get { TimeSpan t = ThreadHandle.GetExecutionTime(threadHandle); GC.KeepAlive(this); return t; } } internal static void VisitBootstrapData(GCs.NonNullReferenceVisitor visitor) { visitor.VisitReferenceFields(Thread.initialThread); visitor.VisitReferenceFields(Thread.threadTable); } internal static unsafe void UpdateAfterGC() { // Update all the thread pointers in the thread contexts for (int i = 0; i < threadTable.Length; i++) { Thread thread = threadTable[i]; if (thread != null) { thread.context->UpdateAfterGC(thread); } } } // Cache for ABI synchronization private SyncHandle[] syncHandles; internal SyncHandle[] GetSyncHandles(int length) { if (syncHandles == null || syncHandles.Length < length) { syncHandles = new SyncHandle[length + 8]; } return syncHandles; } // Caches for Select synchronization // We use stacks, because selectable abstractions might // internally implement HeadMatches using select receive // which is called from within an outer select. // NOTE however that internal selects should never block // (use timeout) private Stack selectBoolsStack; private Stack selectObjectsStack; private Stack selectSyncHandlesStack; public bool[] PopSelectBools(int size) { if (selectBoolsStack == null) { selectBoolsStack = new Stack(); } if (selectBoolsStack.Count == 0) { return new bool [size]; } bool[] selectBools = (bool[])selectBoolsStack.Pop(); if (selectBools.Length < size) { return new bool [size]; } return selectBools; } public void PushSelectBools(bool[] cache) { selectBoolsStack.Push(cache); } public ISelectable[] PopSelectObjects(int size) { if (selectObjectsStack == null) { selectObjectsStack = new Stack(); } if (selectObjectsStack.Count == 0) { return new ISelectable [size]; } ISelectable[] selectObjects = (ISelectable[])selectObjectsStack.Pop(); if (selectObjects.Length < size) { return new ISelectable [size]; } return selectObjects; } public void PushSelectObjects(ISelectable[] cache) { selectObjectsStack.Push(cache); } public SyncHandle[] PopSelectSyncHandles(int size) { if (selectSyncHandlesStack == null) { selectSyncHandlesStack = new Stack(); } if (selectSyncHandlesStack.Count == 0) { return new SyncHandle [size]; } SyncHandle[] selectSyncHandles = (SyncHandle[])selectSyncHandlesStack.Pop(); if (selectSyncHandles.Length < size) { return new SyncHandle [size]; } return selectSyncHandles; } public void PushSelectSyncHandles(SyncHandle[] cache) { selectSyncHandlesStack.Push(cache); } // Given a frame's range in memory (its esp/ebp), check whether // the frame contains the top transition record. If so, // prepare for making a transition from process mode back // to kernel mode. [AccessedByRuntime("referenced from halasm.asm")] [NoStackLinkCheck] // We don't want to throw an exception here; // Therefore, we cannot risk allocating stack segments, // and we should only call other NoStackLinkCheck functions (XXX). internal static unsafe UIntPtr CheckKernelProcessBoundary(UIntPtr esp, UIntPtr ebp, Exception exn) { ThreadContext *context = Processor.GetCurrentThreadContext(); CallStack.TransitionRecord *topMarker = context->kernelMarkers; CallStack.TransitionRecord *secondMarker = context->stackMarkers; UIntPtr topMarkerPtr = (UIntPtr) topMarker; // If the top marker is in our frame, we've reached a boundary: if (esp < topMarkerPtr && topMarkerPtr <= ebp) { context->uncaughtFlag = true; Thread.CurrentThread.lastUncaughtException = exn; Processor.GetCurrentThreadContext()->SetKernelMode(); return 1; } else { return 0; } } // Most recently thrown exception object that the thread // did not catch at all (i.e. that propagated to the bottom // of the stack without encountering an appropriate catch clause). public Exception LastUncaughtException { [NoHeapAllocation] get { return lastUncaughtException; } } // Tell JoinAll not to block waiting for this thread to exit. // Some special threads (e.g. the finalizer thread) need to run // only as long as there are other threads running, and should // not be considered by JoinAll. [NoHeapAllocation] internal void SetIgnoredByJoinAll() { ignoredByJoinAll = true; } } // This class is designed to support queues whose enqueue, // dequeue, and remove operations do not allocate memory. // This feature is useful when writing code that needs to // do such operations with interrupts off. [CLSCompliant(false)] public class ThreadQueue { private ThreadQueueItem head = null; private ThreadQueueItem tail = null; [NoHeapAllocation] public void Enqueue(ThreadQueueItem item) { VTable.Assert(item.Next == null); VTable.Assert(item.Prev == null); VTable.Assert(item.Queue == null); item.Queue = this; item.Prev = tail; if (tail != null) { VTable.Assert(tail.Next == null); tail.Next = item; } else { VTable.Assert(head == null); head = item; } tail = item; } [NoHeapAllocation] public ThreadQueueItem Dequeue() { ThreadQueueItem item = head; if (item != null) { Remove(item); } return item; } [NoHeapAllocation] public void Remove(ThreadQueueItem item) { VTable.Assert(item.Queue == this); if (item.Next != null) { item.Next.Prev = item.Prev; } else { VTable.Assert(item == tail); tail = item.Prev; } if (item.Prev != null) { item.Prev.Next = item.Next; } else { VTable.Assert(item == head); head = item.Next; } item.Next = null; item.Prev = null; item.Queue = null; } [NoHeapAllocation] public bool IsEmpty() { return (head == null); } } [CLSCompliant(false)] public class ThreadQueueItem { public readonly Thread Thread = null; public ThreadQueueItem Next = null; public ThreadQueueItem Prev = null; public ThreadQueue Queue = null; public ThreadQueueItem(Thread thread) { Thread = thread; } [NoHeapAllocation] public void Remove() { if (Queue != null) { Queue.Remove(this); } VTable.Assert(Next == null); VTable.Assert(Prev == null); VTable.Assert(Queue == null); } } }