2008-03-05 09:52:00 -05:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// File: ThreadQueue.cs
|
|
|
|
//
|
|
|
|
// Note:
|
|
|
|
//
|
|
|
|
|
|
|
|
// #define DEBUG_SCHEDULER
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Runtime.CompilerServices;
|
|
|
|
using System.Threading;
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
|
|
|
|
namespace Microsoft.Singularity.Scheduling
|
|
|
|
{
|
|
|
|
// 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)]
|
|
|
|
[AccessedByRuntime("referenced from c++")]
|
|
|
|
public class ThreadQueue
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
private ThreadEntry head;
|
|
|
|
private ThreadEntry tail;
|
|
|
|
private WaitHandleBase handle;
|
2008-03-05 09:52:00 -05:00
|
|
|
|
|
|
|
public ThreadQueue()
|
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
this.head = null;
|
|
|
|
this.tail = null;
|
|
|
|
this.handle = null;
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
public ThreadQueue(WaitHandleBase handle)
|
2008-03-05 09:52:00 -05:00
|
|
|
{
|
2008-11-17 18:29:00 -05:00
|
|
|
this.head = null;
|
|
|
|
this.tail = null;
|
2008-03-05 09:52:00 -05:00
|
|
|
this.handle = handle;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
public WaitHandleBase Handle {
|
2008-03-05 09:52:00 -05:00
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return handle; }
|
|
|
|
}
|
|
|
|
|
|
|
|
public ThreadEntry Head
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return head; }
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public bool IsEnqueued(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
return (entry.queue == this);
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public void EnqueueTail(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
VTable.Assert(entry.next == null);
|
|
|
|
VTable.Assert(entry.prev == null);
|
|
|
|
VTable.Assert(entry.queue == null);
|
|
|
|
|
|
|
|
entry.queue = this;
|
|
|
|
entry.prev = tail;
|
|
|
|
|
|
|
|
if (tail != null) {
|
|
|
|
VTable.Assert(tail.next == null);
|
|
|
|
tail.next = entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(head == null);
|
|
|
|
head = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
tail = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public void EnqueueHead(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
VTable.Assert(entry.next == null);
|
|
|
|
VTable.Assert(entry.prev == null);
|
|
|
|
VTable.Assert(entry.queue == null);
|
|
|
|
|
|
|
|
entry.queue = this;
|
|
|
|
entry.next = head;
|
|
|
|
|
|
|
|
if (head != null) {
|
|
|
|
VTable.Assert(head.prev == null);
|
|
|
|
head.prev = entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(tail == null);
|
|
|
|
tail = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
head = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public void InsertBefore(ThreadEntry position, ThreadEntry entry)
|
|
|
|
{
|
|
|
|
if (position == null) {
|
|
|
|
EnqueueTail(entry);
|
|
|
|
}
|
|
|
|
else if (position == head) {
|
|
|
|
EnqueueHead(entry);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(head != null);
|
|
|
|
VTable.Assert(entry.queue == null);
|
|
|
|
|
|
|
|
entry.queue = this;
|
|
|
|
entry.prev = position.prev;
|
|
|
|
entry.next = position;
|
|
|
|
position.prev = entry;
|
|
|
|
entry.prev.next = entry;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public Thread DequeueHeadThread()
|
|
|
|
{
|
|
|
|
ThreadEntry entry = DequeueHead();
|
|
|
|
if (entry != null) {
|
|
|
|
return entry.Thread;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public Thread DequeueTailThread()
|
|
|
|
{
|
|
|
|
ThreadEntry entry = DequeueTail();
|
|
|
|
if (entry != null) {
|
|
|
|
return entry.Thread;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public ThreadEntry DequeueHead()
|
|
|
|
{
|
|
|
|
ThreadEntry entry = head;
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
Remove(entry);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public ThreadEntry DequeueTail()
|
|
|
|
{
|
|
|
|
ThreadEntry entry = tail;
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
Remove(entry);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
[NoHeapAllocation]
|
|
|
|
public void DequeueAll(ThreadQueue newParentQueue)
|
|
|
|
{
|
|
|
|
|
|
|
|
ThreadEntry threadEntry = head;
|
|
|
|
|
|
|
|
// Navigate through all elements and set the queue to the new queue
|
|
|
|
while ( threadEntry != null) {
|
|
|
|
threadEntry.queue = newParentQueue;
|
|
|
|
threadEntry = threadEntry.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Move queue elements to parent queue
|
|
|
|
if (newParentQueue.tail !=null) {
|
|
|
|
// If new parent queue is not empty enqueue new elements at the end of the
|
|
|
|
// new parent queue
|
|
|
|
newParentQueue.tail.next = this.head;
|
|
|
|
newParentQueue.tail = this.tail;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// New queue is empty update head and tail
|
|
|
|
newParentQueue.head = this.head;
|
|
|
|
newParentQueue.tail = this.tail;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset initial queue
|
|
|
|
this.head = null;
|
|
|
|
this.tail = null;
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-03-05 09:52:00 -05:00
|
|
|
[NoHeapAllocation]
|
|
|
|
public void Remove(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
VTable.Assert(entry.queue == this);
|
|
|
|
|
|
|
|
if (entry.next != null) {
|
|
|
|
entry.next.prev = entry.prev;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(entry == tail);
|
|
|
|
tail = entry.prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry.prev != null) {
|
|
|
|
entry.prev.next = entry.next;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(entry == head);
|
|
|
|
head = entry.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.next = null;
|
|
|
|
entry.prev = null;
|
|
|
|
entry.queue = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public bool IsEmpty()
|
|
|
|
{
|
|
|
|
#if false
|
|
|
|
Scheduler.AssertDispatchLockHeld();
|
|
|
|
#endif
|
|
|
|
return (head == null);
|
|
|
|
}
|
|
|
|
}
|
2008-11-17 18:29:00 -05:00
|
|
|
|
|
|
|
// This struct 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)]
|
|
|
|
[AccessedByRuntime("referenced from c++")]
|
|
|
|
public struct ThreadQueueStruct
|
|
|
|
{
|
|
|
|
private ThreadEntry head;
|
|
|
|
private ThreadEntry tail;
|
|
|
|
|
|
|
|
public ThreadEntry Head
|
|
|
|
{
|
|
|
|
[NoHeapAllocation]
|
|
|
|
get { return head; }
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public void EnqueueTail(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
VTable.Assert(entry.next == null);
|
|
|
|
VTable.Assert(entry.prev == null);
|
|
|
|
VTable.Assert(entry.queue == null);
|
|
|
|
|
|
|
|
entry.prev = this.tail;
|
|
|
|
|
|
|
|
if (tail != null) {
|
|
|
|
VTable.Assert(tail.next == null);
|
|
|
|
tail.next = entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(head == null);
|
|
|
|
head = entry;
|
|
|
|
}
|
|
|
|
tail = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public void EnqueueHead(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
VTable.Assert(entry.next == null);
|
|
|
|
VTable.Assert(entry.prev == null);
|
|
|
|
VTable.Assert(entry.queue == null);
|
|
|
|
|
|
|
|
entry.next = head;
|
|
|
|
|
|
|
|
if (head != null) {
|
|
|
|
VTable.Assert(head.prev == null);
|
|
|
|
head.prev = entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(tail == null);
|
|
|
|
tail = entry;
|
|
|
|
}
|
|
|
|
head = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public ThreadEntry DequeueHead()
|
|
|
|
{
|
|
|
|
ThreadEntry entry = head;
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
Remove(entry);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public ThreadEntry DequeueTail()
|
|
|
|
{
|
|
|
|
ThreadEntry entry = tail;
|
|
|
|
|
|
|
|
if (entry != null) {
|
|
|
|
Remove(entry);
|
|
|
|
return entry;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public void Remove(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
if (entry.next != null) {
|
|
|
|
entry.next.prev = entry.prev;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(entry == tail);
|
|
|
|
tail = entry.prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (entry.prev != null) {
|
|
|
|
entry.prev.next = entry.next;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
VTable.Assert(entry == head);
|
|
|
|
head = entry.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
entry.next = null;
|
|
|
|
entry.prev = null;
|
|
|
|
entry.queue = null;
|
|
|
|
}
|
|
|
|
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public bool IsEmpty()
|
|
|
|
{
|
|
|
|
return (head == null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// <summary>
|
|
|
|
/// Class to enumerate thread entries in a queue
|
|
|
|
/// </summary>
|
|
|
|
///
|
|
|
|
[CLSCompliant(false)]
|
|
|
|
public struct ThreadEntryEnum
|
|
|
|
{
|
|
|
|
///
|
|
|
|
/// <summary>
|
|
|
|
/// Constructor
|
|
|
|
/// </summary>
|
|
|
|
///
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public ThreadEntryEnum(ThreadEntry entry)
|
|
|
|
{
|
|
|
|
this.current = entry;
|
|
|
|
}
|
|
|
|
|
|
|
|
///
|
|
|
|
/// <summary>
|
|
|
|
/// GetNext element in enumarator
|
|
|
|
/// </summary>
|
|
|
|
///
|
|
|
|
[NoHeapAllocation]
|
|
|
|
public ThreadEntry GetNext()
|
|
|
|
{
|
|
|
|
ThreadEntry entryToReturn = this.current;
|
|
|
|
|
|
|
|
if (this.current != null) {
|
|
|
|
current = current.next;
|
|
|
|
}
|
|
|
|
|
|
|
|
return entryToReturn;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary> Current element in enumerator</summary>
|
|
|
|
private ThreadEntry current;
|
|
|
|
}
|
2008-03-05 09:52:00 -05:00
|
|
|
}
|