2008-03-05 09:52:00 -05:00
|
|
|
// ==++==
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// ==--==
|
|
|
|
|
|
|
|
// #define DEBUG_SWITCH
|
|
|
|
|
|
|
|
namespace System.Threading
|
|
|
|
{
|
|
|
|
using System;
|
2008-11-17 18:29:00 -05:00
|
|
|
using System.Collections;
|
2008-03-05 09:52:00 -05:00
|
|
|
using System.Diagnostics;
|
|
|
|
using System.GCs;
|
|
|
|
using System.Globalization;
|
|
|
|
using System.Runtime.CompilerServices;
|
2008-11-17 18:29:00 -05:00
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
using System.Threading;
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
using Microsoft.Bartok.Runtime;
|
|
|
|
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
using Microsoft.Singularity.Channels;
|
|
|
|
using Microsoft.Singularity.V1.Services;
|
|
|
|
using Microsoft.Singularity.V1.Threads;
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread"]/*' />
|
2008-11-17 18:29:00 -05:00
|
|
|
public sealed partial class Thread
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
// Singularity specific fields
|
|
|
|
// Need CPU context
|
|
|
|
// Need stack
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
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;
|
2008-11-17 18:29:00 -05:00
|
|
|
private object m_userScheduler;
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
internal static Thread initialThread;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// This manager is responsible for storing the global data that is
|
|
|
|
// shared amongst all the thread local stores.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
static private LocalDataStoreMgr m_LocalDataStoreMgr;
|
|
|
|
|
|
|
|
internal const int maxThreads = 1024; // Must be power of 2 >= 64
|
|
|
|
private static int threadIndexGenerator;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// 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.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.Thread"]/*' />
|
|
|
|
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
|
2008-11-17 18:29:00 -05:00
|
|
|
// Disable local preemption while holding the lock. This might also be a
|
|
|
|
// the property of the lock
|
|
|
|
|
|
|
|
bool disabled = Processor.DisableLocalPreemption();
|
2008-03-05 09:52:00 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
finally {
|
2008-03-05 09:52:00 -05:00
|
|
|
Thread.threadTableLock.Release(CurrentThread);
|
2008-11-17 18:29:00 -05:00
|
|
|
Processor.RestoreLocalPreemption(disabled);
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Finalizer is responsible for freeing handle that keeps corresponding
|
|
|
|
/// kernel object live.
|
|
|
|
/// </summary>
|
|
|
|
~Thread() {
|
|
|
|
if (this.threadHandle.id != 0) {
|
|
|
|
ThreadHandle.Dispose(this.threadHandle);
|
|
|
|
this.threadHandle = new ThreadHandle();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// 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.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.Start"]/*' />
|
|
|
|
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)) {
|
2008-11-17 18:29:00 -05:00
|
|
|
// Log Thread Start.
|
2008-03-05 09:52:00 -05:00
|
|
|
Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Entered",
|
|
|
|
(UIntPtr)unchecked((uint)threadIndex));
|
|
|
|
|
|
|
|
ThreadStart startFun = currentThread.threadStart;
|
|
|
|
|
|
|
|
try {
|
|
|
|
startFun();
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
catch (Exception uncaughtException) {
|
|
|
|
// Save Uncaught Exception in Thread instance.
|
|
|
|
currentThread.lastUncaughtException = uncaughtException;
|
|
|
|
|
|
|
|
// Dump (possibly nested) Exceptions to the Debugger.
|
|
|
|
// Note: The first line identifies the outer exception,
|
|
|
|
// later lines identify the inner exceptions nesting level.
|
|
|
|
string formatString;
|
|
|
|
uint nestingLevel = 0;
|
|
|
|
Exception currentException = uncaughtException;
|
|
|
|
formatString = "Thread{0,3} Outer" +
|
|
|
|
" Exception: Type '{2}', Message '{3}'.";
|
|
|
|
while (currentException != null) {
|
|
|
|
DebugStub.WriteLine(
|
|
|
|
formatString,
|
|
|
|
__arglist(threadIndex,
|
|
|
|
nestingLevel,
|
|
|
|
currentException.GetType().ToString(),
|
|
|
|
currentException.Message));
|
|
|
|
currentException = currentException.InnerException;
|
|
|
|
formatString = "Thread{0,3} Inner{1,2}" +
|
|
|
|
" Exception: Type '{2}', Message '{3}'.";
|
|
|
|
if (++nestingLevel > 16) break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Assert (always asserts as exception is never null here).
|
|
|
|
string message = uncaughtException.Message;
|
|
|
|
if (message == null) message = String.Empty;
|
|
|
|
|
|
|
|
VTable.Assert(uncaughtException == null,
|
|
|
|
"Thread " + threadIndex +
|
|
|
|
" failed with Exception Type '" +
|
|
|
|
uncaughtException.GetType().ToString() +
|
|
|
|
"', Message '" + message + "'.");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Log Thread Exit.
|
2008-03-05 09:52:00 -05:00
|
|
|
Tracing.Log(Tracing.Audit, "ThreadStub(atid={0}) Exiting",
|
|
|
|
(UIntPtr)unchecked((uint)threadIndex));
|
|
|
|
}
|
|
|
|
|
|
|
|
currentThread.joinEvent.Set();
|
|
|
|
RemoveThread(threadIndex);
|
|
|
|
GC.DeadThreadNotification(currentThread);
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
bool disabled = Processor.DisableLocalPreemption();
|
|
|
|
// REVIEW: this is a dangerous locking strategy as the transition code
|
2008-03-05 09:52:00 -05:00
|
|
|
// 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);
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
finally {
|
2008-03-05 09:52:00 -05:00
|
|
|
Thread.threadTableLock.Release(currentThread);
|
2008-11-17 18:29:00 -05:00
|
|
|
Processor.RestoreLocalPreemption(disabled);
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//====================================================================
|
|
|
|
// 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.
|
|
|
|
//===================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
// 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;
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
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])();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Returns true if the thread has been started and is not dead.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.IsAlive"]/*' />
|
|
|
|
public bool IsAlive {
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return (threadState != ThreadState.Unstarted &&
|
|
|
|
threadState != ThreadState.Stopped); }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Waits for the thread to die.
|
|
|
|
//
|
|
|
|
// Exceptions: ThreadStateException if the thread has not been started yet.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.Join"]/*' />
|
|
|
|
public void Join()
|
|
|
|
{
|
|
|
|
Join(SchedulerTime.MaxValue);
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// 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.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.Join2"]/*' />
|
|
|
|
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;
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
bool disabled = Processor.DisableLocalPreemption();
|
2008-03-05 09:52:00 -05:00
|
|
|
Thread.threadTableLock.Acquire(CurrentThread);
|
|
|
|
try {
|
|
|
|
thread = threadTable[i];
|
|
|
|
}
|
|
|
|
finally {
|
|
|
|
Thread.threadTableLock.Release(CurrentThread);
|
2008-11-17 18:29:00 -05:00
|
|
|
Processor.RestoreLocalPreemption(disabled);
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
if (thread != null &&
|
|
|
|
thread != CurrentThread &&
|
|
|
|
thread.threadState != ThreadState.Unstarted &&
|
|
|
|
!thread.ignoredByJoinAll) {
|
|
|
|
thread.Join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
closing = true;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// 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.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
public static void Sleep(int milliseconds)
|
|
|
|
{
|
|
|
|
Sleep(TimeSpan.FromMilliseconds(milliseconds));
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.Sleep"]/*' />
|
|
|
|
public static void Sleep(TimeSpan timeout)
|
|
|
|
{
|
|
|
|
ThreadHandle.Sleep(timeout);
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
// 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.
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.SpinWait"]/*' />
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static void SpinWait(int iterations)
|
|
|
|
{
|
|
|
|
for (int i = iterations; i > 0; i--) {
|
|
|
|
// Ensure that the optimizer doesn't remove this
|
|
|
|
NativeNoOp();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
///
|
|
|
|
/// <summary>
|
|
|
|
/// Notify thread that it acquired spinlock of specific rank
|
|
|
|
/// </summary>
|
|
|
|
///
|
|
|
|
/// <param name="type">Type of spinlock</param>
|
|
|
|
///
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal void NotifySpinLockAboutToAcquire(int type)
|
|
|
|
{
|
|
|
|
// Add rank verification and etc ..
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// <summary>
|
|
|
|
/// Notify thread that released a spinlock of specific rank
|
|
|
|
/// </summary>
|
|
|
|
///
|
|
|
|
/// <param name="type">Type of spinlock</param>
|
|
|
|
///
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal void NotifySpinLockReleased(int type)
|
|
|
|
{
|
|
|
|
// Add rank verification and etc ..
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// <summary>
|
|
|
|
/// Given a thread id, return actual thread
|
|
|
|
/// </summary>
|
|
|
|
///
|
|
|
|
/// <param name="threadId">Thread id</param>
|
|
|
|
///
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static Thread GetThreadFromThreadId(int threadId)
|
|
|
|
{
|
|
|
|
// Assert preconditions: threadId has to be in range and thread can't be null
|
|
|
|
VTable.Assert(threadId < threadTable.Length);
|
|
|
|
VTable.Assert(threadTable[threadId] != null);
|
|
|
|
|
|
|
|
return threadTable[threadId];
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
[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();
|
|
|
|
}
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.CurrentThread"]/*' />
|
|
|
|
public static Thread CurrentThread
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
2008-11-17 18:29:00 -05:00
|
|
|
[NoStackLinkCheckTrans]
|
2008-03-05 09:52:00 -05:00
|
|
|
get {
|
|
|
|
return Processor.GetCurrentThread();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal ThreadHandle Handle
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return threadHandle; }
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
[NoStackLinkCheckTrans]
|
2008-03-05 09:52:00 -05:00
|
|
|
[RequiredByBartok]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
private static Thread GetCurrentThreadNative()
|
|
|
|
{
|
|
|
|
return Processor.GetCurrentThread();
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
[NoStackLinkCheckTrans]
|
2008-03-05 09:52:00 -05:00
|
|
|
[RequiredByBartok]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static int GetCurrentThreadIndex()
|
|
|
|
{
|
|
|
|
return Processor.GetCurrentThread().threadIndex;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Return the thread state as a consistent set of bits. This is more
|
|
|
|
// general then IsAlive or IsBackground.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.ThreadState"]/*' />
|
|
|
|
public ThreadState ThreadState
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return threadState; }
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public int GetThreadId()
|
|
|
|
{
|
|
|
|
return threadIndex;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
public object UserScheduler
|
|
|
|
{
|
|
|
|
get {
|
|
|
|
return m_userScheduler;
|
|
|
|
}
|
|
|
|
set {
|
|
|
|
if (null != m_userScheduler) {
|
|
|
|
// TODO make this a security exception
|
|
|
|
throw new Exception("User scheduler may not be changed");
|
|
|
|
}
|
|
|
|
m_userScheduler = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public int Affinity
|
|
|
|
{
|
|
|
|
get { return ThreadHandle.GetAffinity(threadHandle); }
|
|
|
|
set { ThreadHandle.SetAffinity(threadHandle, value); }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//=========================================================================
|
|
|
|
// Allocates an un-named data slot. The slot is allocated on ALL the
|
|
|
|
// threads.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.AllocateDataSlot"]/*' />
|
|
|
|
public static LocalDataStoreSlot AllocateDataSlot()
|
|
|
|
{
|
|
|
|
return m_LocalDataStoreMgr.AllocateDataSlot();
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Allocates a named data slot. The slot is allocated on ALL the
|
|
|
|
// threads. Named data slots are "public" and can be manipulated by
|
|
|
|
// anyone.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.AllocateNamedDataSlot"]/*' />
|
|
|
|
public static LocalDataStoreSlot AllocateNamedDataSlot(String name)
|
|
|
|
{
|
|
|
|
return m_LocalDataStoreMgr.AllocateNamedDataSlot(name);
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// 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.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.GetNamedDataSlot"]/*' />
|
|
|
|
public static LocalDataStoreSlot GetNamedDataSlot(String name)
|
|
|
|
{
|
|
|
|
return m_LocalDataStoreMgr.GetNamedDataSlot(name);
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Frees a named data slot. The slot is allocated on ALL the
|
|
|
|
// threads. Named data slots are "public" and can be manipulated by
|
|
|
|
// anyone.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.FreeNamedDataSlot"]/*' />
|
|
|
|
public static void FreeNamedDataSlot(String name)
|
|
|
|
{
|
|
|
|
m_LocalDataStoreMgr.FreeNamedDataSlot(name);
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Retrieves the value from the specified slot on the current thread.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.GetData"]/*' />
|
|
|
|
public static Object GetData(LocalDataStoreSlot slot)
|
|
|
|
{
|
|
|
|
m_LocalDataStoreMgr.ValidateSlot(slot);
|
|
|
|
|
|
|
|
if (localDataStore != null) {
|
|
|
|
return localDataStore.GetData(slot);
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// Sets the data in the specified slot on the currently running thread.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.SetData"]/*' />
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
//=============================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
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
|
2008-11-17 18:29:00 -05:00
|
|
|
//=========================================================================
|
|
|
|
// 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.
|
|
|
|
//=========================================================================
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern byte VolatileRead(ref byte address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead1"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern short VolatileRead(ref short address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead2"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern int VolatileRead(ref int address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead3"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern long VolatileRead(ref long address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead4"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern sbyte VolatileRead(ref sbyte address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead5"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern ushort VolatileRead(ref ushort address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead6"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern uint VolatileRead(ref uint address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead7"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern IntPtr VolatileRead(ref IntPtr address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead8"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern UIntPtr VolatileRead(ref UIntPtr address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead9"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern ulong VolatileRead(ref ulong address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead10"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern float VolatileRead(ref float address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead11"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern double VolatileRead(ref double address);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileRead12"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern Object VolatileRead(ref Object address);
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static unsafe extern byte VolatileReadUnsafe(byte* address);
|
|
|
|
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static unsafe extern short VolatileReadUnsafe(short* address);
|
|
|
|
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static unsafe extern int VolatileReadUnsafe(int* address);
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref byte address, byte value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite1"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref short address, short value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite2"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref int address, int value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite3"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref long address, long value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite4"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref sbyte address, sbyte value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite5"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref ushort address, ushort value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite6"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref uint address, uint value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite7"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref IntPtr address, IntPtr value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite8"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref UIntPtr address, UIntPtr value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite9"]/*' />
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref ulong address, ulong value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite10"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref float address, float value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite11"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref double address, double value);
|
|
|
|
|
|
|
|
//| <include path='docs/doc[@for="Thread.VolatileWrite12"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void VolatileWrite(ref Object address, Object value);
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static unsafe extern void VolatileWriteUnsafe(int* address,
|
|
|
|
int value);
|
|
|
|
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static unsafe extern void VolatileWriteUnsafe(short* address,
|
|
|
|
short value);
|
|
|
|
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
internal static unsafe extern void VolatileWriteUnsafe(byte* address,
|
|
|
|
byte value);
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
//| <include path='docs/doc[@for="Thread.MemoryBarrier"]/*' />
|
|
|
|
[Intrinsic]
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public static extern void MemoryBarrier();
|
|
|
|
|
|
|
|
[MethodImplAttribute(MethodImplOptions.InternalCall)]
|
|
|
|
[GCAnnotation(GCOption.NOGC)]
|
2008-11-17 18:29:00 -05:00
|
|
|
[StackBound(32)]
|
2008-03-05 09:52:00 -05:00
|
|
|
[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
|
2008-11-17 18:29:00 -05:00
|
|
|
void VisitBootstrapData(GCs.ReferenceVisitor visitor)
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
|
|
|
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) {
|
2008-11-17 18:29:00 -05:00
|
|
|
for (int i=0; i<cache.Length; i++) {
|
|
|
|
cache[i] = null;
|
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
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) {
|
2008-11-17 18:29:00 -05:00
|
|
|
for (int i=0; i<cache.Length; i++) {
|
|
|
|
cache[i] = new SyncHandle();
|
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
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")]
|
2008-11-17 18:29:00 -05:00
|
|
|
[NoStackLinkCheckTrans] // We don't want to throw an exception here;
|
2008-03-05 09:52:00 -05:00
|
|
|
// 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;
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
|
|
|
|
#if DEBUG || true
|
|
|
|
string debugName;
|
|
|
|
public string DebugName
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return debugName; }
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
set { debugName = value; }
|
|
|
|
}
|
|
|
|
#endif
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
// 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;
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
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;
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
VTable.Assert(item == tail);
|
|
|
|
tail = item.Prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item.Prev != null) {
|
|
|
|
item.Prev.Next = item.Next;
|
2008-11-17 18:29:00 -05:00
|
|
|
}
|
|
|
|
else {
|
2008-03-05 09:52:00 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|