singrdk/base/Kernel/SingSharp.Runtime/EMap.sg

266 lines
8.9 KiB
Plaintext

////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: EMap.ssc
//
// Note:
//
//
//
using System;
using Microsoft.SingSharp;
using Microsoft.Contracts;
namespace Microsoft.Singularity.Channels {
using Microsoft.Singularity.V1.Threads;
public class EMap<T, State, Data> : ITracked, ISelectable
where T: unmanaged struct, IEventCollectionElement
{
private AutoResetEventHandle evHandle;
public SyncHandle GetWaitHandle() { return evHandle; }
public void ResetWaitSignal() {
AutoResetEventHandle.Reset(this.evHandle);
}
private Node! listHead;
public EMap() {
this.listHead = new Node(this);
AutoResetEventHandle handleOnStack;
if (!AutoResetEventHandle.Create(false, out handleOnStack)) {
throw new System.Threading.HandleCreateException();
}
this.evHandle = handleOnStack;
}
private Node? lookAside;
private Node? nextScanStart;
private Node! GetFreshNode(T* in ExHeap opt(State) ep, Data data, EMap<T,State,Data>! parent) {
Node n = this.lookAside;
if (n == null) {
n = new Node(ep, data, parent);
}
else {
this.lookAside = null;
n.Init(ep, data, parent);
}
return n;
}
// we only want type visibility outside NewESet, but not member accessibility
private class Node {
unsafe internal T* opt(ExHeap) opt(State) endpoint;
internal Data data;
unsafe private EMap<T, State, Data>! parent;
internal Node! next;
internal Node! prev;
internal Node(T* opt(ExHeap) opt(State) ep, Data data, [Delayed] EMap<T, State, Data>! parent) {
this.next = this;
this.prev = this;
this.parent = parent;
this.Init(ep, data, parent);
}
[Delayed]
internal void Init(T* opt(ExHeap) opt(State) ep, Data data, [Delayed] EMap<T, State, Data>! parent) {
this.parent = parent;
this.endpoint = ep;
this.data = data;
}
/// <summary>
/// For constructing the dummy head node.
/// </summary>
internal Node([Delayed] EMap<T, State, Data>! parent) {
this.next = this;
this.prev = this;
this.parent = parent;
}
internal T* opt(ExHeap) opt(State) Unlink(EMap<T,State,Data>! parent, out Data data) {
assert (parent == this.parent);
this.prev.next = this.next;
this.next.prev = this.prev;
this.next = this;
this.prev = this;
data = this.data;
T* in ExHeap opt(State) ep = this.endpoint;
this.endpoint = null;
ep->UnlinkFromCollection(parent.evHandle);
parent.lookAside = this;
return ep;
}
internal void LinkAsNext(T* opt(ExHeap) opt(State)! ep, Data data, EMap<T,State,Data>! parent) {
Node next = parent.GetFreshNode(ep, data, parent);
next.next = this.next;
this.next = next;
next.prev = this;
next.next.prev = next;
ep->LinkIntoCollection(parent.evHandle);
}
}
#region ITracked members
void ITracked.Release()
{
Node current = this.listHead.next;
while (current != this.listHead) {
// temporary hack until we fix the compiler to allow this upcast in receiver context
unsafe {
current.endpoint->Release();
}
current = current.next;
}
}
void ITracked.Acquire()
{
Node current = this.listHead.next;
while (current != this.listHead) {
// temporary hack until we fix the compiler to allow this upcast in receiver context
unsafe {
current.endpoint->Acquire();
}
current = current.next;
}
}
public void Dispose()
{
Node lh = this.listHead;
Node current = lh.next;
while (current != lh) {
Node next = current.next;
Data data;
T* opt(ExHeap) opt(State)! ep = (!)current.Unlink(this, out data);
unsafe { // needed because specializer does not add call usually done by delete
ep->Dispose();
}
delete ep;
current = next;
}
lh.next = lh;
lh.prev = lh;
if (this.evHandle.id != UIntPtr.Zero) {
AutoResetEventHandle.Dispose(this.evHandle);
this.evHandle = new AutoResetEventHandle();
}
}
void ITracked.Expose() {}
void ITracked.UnExpose() {}
#endregion
public void Add([Claims] T* opt(ExHeap) opt(State)! ep, Data data) {
this.listHead.LinkAsNext(ep, data, this);
}
///
/// We have to be careful here. The node in nextScanStart might no longer
/// be in the set.
///
/// The policy is to start scanning from the successor of the last scanned
/// point.
private Node! NextScanStart {
get {
Node candidate = this.nextScanStart;
if (candidate == null || candidate.endpoint == null) {
// node might no longer be in the set
return this.listHead;
}
return candidate;
}
}
/// possible set to false when match not possible. Never set to true!
public bool HeadMatches(int tag, ref bool possible, ref object setMatch)
{
// check if this tag is about the collection elements or about the collection itself:
if ((tag & (~0xff)) != 0) {
// about elements. Shift right 8 bits
tag = tag>>8;
// scan starting at different place each time
Node scanStart = this.NextScanStart;
bool setPossible = false;
Node current = scanStart;
do {
if (current != this.listHead) {
bool curPossible = true;
if (current.endpoint->HeadMatches(tag, ref curPossible, ref setMatch)) {
setMatch = current;
this.nextScanStart = current.next;
return true;
}
setPossible |= curPossible;
}
current = current.next;
} while (current != scanStart);
if (!setPossible) { possible = false; }
return false;
}
else {
// about the map itself
switch(tag) {
case EMapReceiveTag.Empty:
if (this.listHead == null || this.listHead.next == this.listHead) {
return true;
}
possible = false; // can't match on this select receive
break;
case EMapReceiveTag.Head:
if (this.listHead != null && this.listHead.next != this.listHead) {
return true;
}
possible = false; // can't match on this select receive
break;
}
return false;
}
}
[Selectable((int)ESetReceiveTag.Empty)]
public void RecvEmpty() {
}
[Selectable((int)ESetReceiveTag.Head)]
public void RecvHead(out T*! in ExHeap opt(State) ep, out Data data) {
Node current = this.listHead.next;
assert current != this.listHead;
ep = (!)current.Unlink(this, out data);
}
public T* opt(ExHeap) opt(State)! Extract(object setMatch, out Data data) {
assert setMatch != null;
Node node = (Node)setMatch;
T* opt(ExHeap) opt(State) result = node.Unlink(this, out data);
return (!)result;
}
}
internal enum EMapReceiveTag {
Any = 1,
Empty = 2,
Head = 3,
}
}