////////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: EndpointCore.cs
//
// HACK: Because we currently compile this file as part of the Kernel with C#, we can't
// make EndpointCore a rep struct, which is necessary for inheriting from it.
// The compiler and Bartok recognize EndpointCore as special and treat it as
// unsealed.
// This hack can be removed, once we compile it with Sing#.
// DON'T change the name until then!
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Security;
using Microsoft.Singularity.Memory;
namespace Microsoft.Singularity.Channels
{
using Microsoft.Singularity.V1.Threads;
using Microsoft.Singularity.V1.Security;
using Microsoft.Singularity.V1.Services;
using Microsoft.Singularity.V1.Types;
using Allocation = Microsoft.Singularity.Memory.SharedHeap.Allocation;
using System.Threading;
using SharedHeap = Microsoft.Singularity.Memory.SharedHeap;
[CLSCompliant(false)]
public enum EndpointCoreEvent : ushort
{
Connect = 4,
TransferToProcess = 5,
}
[CLSCompliant(false)]
[CCtorIsRunDuringStartup]
unsafe public struct EndpointCore
{
////////////////////////////////////////////////////////////////////
// Fields
////////////////////////////////////////////////////////////////////
//
// NOTE: The fields specified here must match those in:
// Kernel/Singularity/Channels/EndpointCore.cs
// Kernel/Singularity/V1/Services/ChannelServices.cs
// Libraries/Singuarity.V1/Services/ChannelServices.cs
///
/// Handle to the actual message delivery mechanism
///
private DeliveryHandle deliveryHandle;
///
/// Event handle in case this endpoint is part of a collection
///
private AutoResetEventHandle collectionEvent;
//
// These "cached" fields are directly accessable by user programs,
// but are not trusted by the kernel (as they could be modified by untrusted
// code). The kernel relies on the trusted shadow copies held in the
// deliveryImpl object, but updates these fields to reflect any changes to user
// apps.
//
///
/// Event on which sends are signaled to this endpoint.
/// The handle is owned by the kernel, since the endpoint can move.
/// The kernel deallocates the handle when the channel is deallocated.
/// NOTE: stays valid until the entire channel gets collected.
///
private AutoResetEventHandle cachedMessageEvent;
///
/// Closed flag
///
private bool cachedClosed;
///
/// Contains the process id of the process currently owning this end of the
/// channel.
///
private int cachedOwnerProcessId;
///
/// Contains the channelId (positive on the EXP endpoint, negative on the imp endpoint)
///
private int cachedChannelId;
///
/// Whether to marshall or not
///
private bool cachedMarshall;
///
/// Points to the peer endpoint
///
private Allocation* /*EndpointCore* opt(ExHeap)*/ cachedPeer;
///
/// If true then the peer state can be queried directly from cachedPeer
///
private bool peerStateValid;
////////////////////////////////////////////////////////////////////
// Static Fields
////////////////////////////////////////////////////////////////////
///
/// Number of open channels (using any delivery mechanism)
///
private static int openChannelCount; // TODO open channel count!!!
///
/// Channel id generator used to create unique channel id's accross delivery
/// mechanisms.
///
private static int channelIdGenerator;
////////////////////////////////////////////////////////////////////
// Types
////////////////////////////////////////////////////////////////////
public enum DelegationState {None, ByCapability, ByMediation, Mediated};
////////////////////////////////////////////////////////////////////
// Methods
////////////////////////////////////////////////////////////////////
///
/// Retrieve underlying delivery mechanism for a given endpoint allocation pointer
///
[NoHeapAllocation]
internal static DeliveryImpl AllocationEndpointDeliveryImpl(
Allocation* /*EndpointCore* opt(ExHeap)!*/ endpoint)
{
EndpointCore * ep = (EndpointCore *) Allocation.GetData(endpoint);
if (ep == null) {
return null;
} else {
return ep->EndpointDeliveryImpl;
}
}
///
/// Retrieve underlying delivery mechanism for a given endpoint allocation pointer
/// using GetDataUnchecked.
///
[NoHeapAllocation]
internal static DeliveryImpl AllocationEndpointDeliveryImplUnchecked(
Allocation* /*EndpointCore* opt(ExHeap)!*/ endpoint)
{
EndpointCore * ep = (EndpointCore *) Allocation.GetDataUnchecked(endpoint);
if (ep == null) {
return null;
} else {
return ep->EndpointDeliveryImpl;
}
}
///
/// Retrieve underlying delivery mechanism for this endpoint
///
internal DeliveryImpl EndpointDeliveryImpl {
[NoHeapAllocation]
get {
if (deliveryHandle != DeliveryHandle.Zero) {
return DeliveryHandle.GetImpl(deliveryHandle);
} else {
unsafe {
DebugStub.WriteLine("deliveryHandle value is {0,8:x}\n", __arglist((uint) deliveryHandle.id));
}
DebugStub.Break();
return null;
}
}
}
///
/// Performs the initialization of the core part of each endpoint and cross links
/// them to form a channel. Uses the standard shared address space delivery
/// mechanism.
///
public static void Connect(
Allocation* /*EndpointCore* opt(ExHeap)!*/ imp,
Allocation* /*EndpointCore* opt(ExHeap)!*/ exp,
Allocation* /*EndpointCore* opt(ExHeap)!*/ securityEp)
{
Connect(imp, exp, securityEp, SingleAddrSpaceDelivery.ImplName);
}
///
/// Performs the initialization of the core part of each endpoint and cross links
/// them to form a channel. Uses the given delivery mechanism.
///
public static void Connect(
Allocation* /*EndpointCore* opt(ExHeap)!*/ imp,
Allocation* /*EndpointCore* opt(ExHeap)!*/ exp,
Allocation* /*EndpointCore* opt(ExHeap)!*/ securityEp,
string deliveryImplType)
{
if (imp == null || exp == null) {
throw new ApplicationException("Connect called with null endpoints");
}
EndpointCore* impData = (EndpointCore*)Allocation.GetData(imp);
EndpointCore* expData = (EndpointCore*)Allocation.GetData(exp);
if (impData == null || expData == null) {
throw new ApplicationException("SharedHeap.GetData return null");
}
Tracing.Log(Tracing.Debug, "connect {0:x8} and {1:x8}",
(UIntPtr)imp, (UIntPtr)exp);
if (!(DeliveryHandle.Create(deliveryImplType, imp, out impData->deliveryHandle) &&
DeliveryHandle.Create(deliveryImplType, exp, out expData->deliveryHandle))) {
throw new EndpointCoreException(
"Error trying to create EndpointCore using \"" +
deliveryImplType +
"\" delivery implementation");
}
#if false
DebugStub.Print("imp handle {0,8:x} exp handle {1,8:x}\n",
__arglist((uint)impData->deliveryHandle.id, (uint)expData->deliveryHandle.id));
#endif
DeliveryImpl impDi = impData->EndpointDeliveryImpl;
DeliveryImpl expDi = expData->EndpointDeliveryImpl;
VTable.Assert(impDi != null && expDi != null);
impDi.Connect(expDi, securityEp);
// keep track of how many channels are open
Interlocked.Increment(ref openChannelCount);
#if CHANNEL_COUNT
PerfCounters.IncrementChannelsCreated();
#endif
Monitoring.Log(Monitoring.Provider.EndpointCore,
(ushort)EndpointCoreEvent.Connect, 0,
(uint)expData->ChannelId, 0, 0, 0, 0);
}
///
/// Set this end to closed
///
public void Close()
{
DeliveryImpl di = EndpointDeliveryImpl;
if (di != null) {
di.Close();
}
}
[NoHeapAllocation]
public bool Closed()
{
DeliveryImpl di = EndpointDeliveryImpl;
if (di != null) {
return di.Closed;
} else {
// endpoint has not yet been connected
return true;
}
}
///
/// Closes this end of the channel and frees associated resources, EXCEPT the block
/// of memory for this endpoint. It must be released by the caller. Sing# does this
/// for the programmer.
///
/// This runs in the kernel to avoid a race condition with Process.Stop.
///
public bool Dispose()
{
DeliveryImpl di = EndpointDeliveryImpl;
if (di != null) {
return EndpointDeliveryImpl.Dispose();
} else {
return true; // endpoint was not yet connected
}
}
///
/// Explicitly frees this end of the channel.
///
/// Since both threads on the channel could try to do this simultaneously,
/// we use the ref counting on the underlying endpoints to let the last
/// free operation (the one pulling the ref count to 0) to free the associated
/// event.
///
public unsafe static void Free(Allocation* /*EndpointCore* opt(ExHeap)!*/ endpoint)
{
// Use unchecked GetData here, since this may be called from the
// cleanup threading running in the kernel.
EndpointCore * ep = ((EndpointCore*)Allocation.GetDataUnchecked(endpoint));
if (ep->deliveryHandle != DeliveryHandle.Zero) {
if (DeliveryHandle.Free(ep->deliveryHandle)) {
Interlocked.Decrement(ref openChannelCount);
}
} else {
// was never connected, just free this endpoint
AutoResetEventHandle areHandle = ep->cachedMessageEvent;
SharedHeap.KernelSharedHeap.Free(endpoint,
SharedHeap.CurrentProcessSharedHeap.EndpointPeerOwnerId);
if (areHandle.id != UIntPtr.Zero) {
Process.kernelProcess.ReleaseHandle(areHandle.id);
}
}
}
///
/// The event to wait for messages on this endpoint. Used by Select.
///
public SyncHandle GetWaitHandle() {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.MessageEvent;
}
///
/// Get the AutoResetEventHandle
///
public AutoResetEventHandle GetAreHandle() {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.AreHandle;
}
///
/// Notify the peer of this endpoint that a message is ready.
/// Notifies the set owner if this endpoint is part of a set.
///
public void NotifyPeer() {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
di.NotifyPeer();
}
///
/// Used internally by the kernel to transfer an endpoint to a new owner
///
/// Can be used to transfer ANY kind of shared heap data, not just endpoints.
///
public static Allocation* MoveEndpoint(SharedHeap fromHeap,
SharedHeap toHeap,
Process newOwner,
Allocation *ep)
{
return DeliveryImpl.MoveData(fromHeap, toHeap, newOwner, ep);
}
public static void AcceptDelegation(Allocation* /*EndpointCore* opt(ExHeap)!*/ imp,
Allocation* /*EndpointCore* opt(ExHeap)!*/ exp,
Allocation* /*EndpointCore* opt(ExHeap)!*/ ep)
{
DeliveryImpl impDi = AllocationEndpointDeliveryImpl(imp);
DeliveryImpl expDi = AllocationEndpointDeliveryImpl(exp);
DeliveryImpl epDi = AllocationEndpointDeliveryImpl(ep);
VTable.Assert((impDi != null && expDi != null && epDi != null));
VTable.Assert((impDi.GetType() == expDi.GetType()) &&
(impDi.GetType() == epDi.GetType()));
impDi.AcceptDelegation(expDi, epDi);
}
public void EnableDelegation(bool allowMediation)
{
EndpointDeliveryImpl.EnableDelegation(allowMediation);
}
public DelegationState OwnerDelegationState
{
[NoHeapAllocation]
get { return EndpointDeliveryImpl.OwnerDelegationState; }
}
public PrincipalHandle OwnerPrincipalHandle
{
[NoHeapAllocation]
get { return EndpointDeliveryImpl.OwnerPrincipalHandle; }
}
public PrincipalHandle PeerPrincipalHandle
{
[NoHeapAllocation]
get { return EndpointDeliveryImpl.PeerPrincipalHandle; }
}
public int PeerReceiveCount
{
[NoHeapAllocation]
get {
DeliveryImpl di = EndpointDeliveryImpl;
if (di != null) {
return di.PeerReceiveCount;
} else {
// not yet connected, therefore no peer yet
return 0;
}
}
}
//versions of begin update and marshallpointer that handle objects with pointers
//greater than one level deep
internal void BeginUpdate(byte* basep, byte* source, int* tagAddress, int msgSize)
{
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
// DebugStub.Print("BeginUpdate delivery handle {0,8:x}\n", __arglist((uint)deliveryHandle.id));
di.BeginUpdate(basep, source, tagAddress, msgSize);
}
public void MarshallPointer(byte* basep, byte** target, SystemType type, byte* parent, int offset)
{
if (target == null) return;
DeliveryImpl di = EndpointDeliveryImpl;
// DebugStub.Print("Marshall Pointer delivery handle {0,8:x}\n", __arglist((uint)deliveryHandle.id));
VTable.Assert(di != null);
di.MarshallPointer(basep, target, type, parent, offset);
}
public static PrincipalHandle TransferPrincipal(PrincipalHandle oldH,
int processId,
ref DelegationState delegationState)
{
Process p = Process.GetProcessByID(processId);
if (p == null)
throw new ApplicationException("Delegate endpoint process is null");
DelegationState newstate;
Principal pr;
switch (delegationState) {
case DelegationState.ByMediation:
pr = PrincipalImpl.NewDelegation(
PrincipalImpl.MakePrincipal(oldH.val), p.Principal);
newstate = DelegationState.Mediated;
break;
case DelegationState.ByCapability:
pr = PrincipalImpl.NewDelegation(
PrincipalImpl.MakePrincipal(oldH.val), p.Principal);
newstate = DelegationState.None;
break;
case DelegationState.Mediated:
pr = p.Principal;
newstate = DelegationState.None;
break;
case DelegationState.None:
default:
pr = p.Principal;
newstate = DelegationState.None;
break;
}
delegationState = newstate;
return new PrincipalHandle(pr.Val);
}
#if SINGULARITY_KERNEL && CHANNEL_COUNT
internal static void IncreaseBytesSentCount(long bytes)
{
PerfCounters.AddBytesSent(bytes);
}
#endif // SINGULARITY_KERNEL
///
/// Transfer the given Allocation block to the target endpoint
///
public static void TransferBlockOwnership(Allocation* ptr, ref EndpointCore target)
{
Allocation.SetOwnerProcessId(ptr, target.cachedOwnerProcessId);
// TODO MAKE THIS APROPRIATE TO BOTH SINGLE AND PAGED IMPLS
DeliveryImpl di = target.EndpointDeliveryImpl;
VTable.Assert(di != null);
//Monitoring.Log(Monitoring.Provider.ChannelService,
// (ushort)ChannelServiceEvent.TransferBlockOwnership, 0,
// (uint)di.ChannelId,
// (uint)di.ProcessId,
// 0, 0, 0);
#if CHANNEL_COUNT
IncreaseBytesSentCount((long) Allocation.GetSize(ptr));
#endif
Allocation.SetOwnerProcessId(ptr, di.ProcessId);
}
///
/// Transfer any contents that needs to be adjusted from the transferee to the target
/// endpoint.
///
// TODO: change "ref EndpointCore" to "EndpointCore"
public static void TransferContentOwnership(
ref EndpointCore transferee,
ref EndpointCore target)
{
// TODO MAKE THIS APROPRIATE TO BOTH SINGLE AND PAGED IMPLS
DeliveryImpl transfereeDi = transferee.EndpointDeliveryImpl;
// XXX BUG? BUG? BUG?
// targetDi = transferee.EndpointDeliveryImpl
// should be:
// targetDi = target.EndpointDeliveryImpl
DeliveryImpl targetDi = transferee.EndpointDeliveryImpl;
VTable.Assert((transfereeDi != null) && (targetDi != null));
//Monitoring.Log(Monitoring.Provider.ChannelService,
// (ushort)ChannelServiceEvent.TransferContentOwnership, 0,
// (uint)transfereeDi.ProcessId,
// (uint)targetDi.ProcessId,
// (uint)transfereeDi.ChannelId,
// (uint)targetDi.ChannelId,
// (uint)targetDi.Peer.ChannelId);
int toProcessId = targetDi.ProcessId;
transfereeDi.ProcessId = toProcessId;
DelegationState newstate = transfereeDi.OwnerDelegationState;
transfereeDi.OwnerPrincipalHandle =
TransferPrincipal(transfereeDi.OwnerPrincipalHandle, toProcessId, ref newstate);
transfereeDi.OwnerDelegationState = newstate;
Allocation* transfereePeerAllocation = transfereeDi.Peer();
// also transfer the peer allocation
Allocation.SetOwnerProcessId(transfereePeerAllocation, toProcessId);
}
public static int OpenChannelCount {
[NoHeapAllocation]
get { return openChannelCount; }
}
public static void IncOpenChannelCount () {
Interlocked.Increment(ref openChannelCount);
}
public static void DecOpenChannelCount () {
Interlocked.Decrement(ref openChannelCount);
}
//
// These getter defer to the delivery implementation, as the kernel only
// trusts the shadow versions held in Kernel memory space.
//
public bool PeerClosed()
{
DeliveryImpl di = EndpointDeliveryImpl;
if (di != null) {
return di.PeerClosed();
} else {
// endpoint has not yet been connected
return true;
}
}
///
/// Return a handle for the peer
///
public Allocation* Peer(out bool marshall)
{
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.Peer(out marshall);
}
public int ChannelId
{
[NoHeapAllocation]
get {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.ChannelId;
}
}
public int ProcessId
{
[NoHeapAllocation]
get {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.ProcessId;
}
}
public int ReceiveCount
{
get {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.ReceiveCount;
}
}
public int PeerProcessId
{
[NoHeapAllocation]
get {
DeliveryImpl di = EndpointDeliveryImpl;
VTable.Assert(di != null);
return di.PeerProcessId;
}
}
public DeliveryHandle dImpHandle
{
get {
return deliveryHandle;
}
set {
deliveryHandle = value;
}
}
public AutoResetEventHandle CollectionEvent
{
get {
return collectionEvent;
}
set {
collectionEvent = value;
}
}
internal static int GetNextChannelId()
{
return Interlocked.Increment(ref channelIdGenerator);
}
//
// These methods only set the user accessable cached copies, real changes should
// be made to the delivery implementation copies.
//
[NoHeapAllocation]
public void SetCachedClose(bool closed) { cachedClosed = closed; }
[NoHeapAllocation]
public void SetCachedProcessId(int pid) { cachedOwnerProcessId = pid; }
[NoHeapAllocation]
public void SetCachedChannelId(int cid) { cachedChannelId = cid; }
[NoHeapAllocation]
public void SetCachedMarshall(bool marshall) { cachedMarshall = marshall; }
[NoHeapAllocation]
public void SetCachedMessageEvent(AutoResetEventHandle mh)
{
cachedMessageEvent = mh;
}
[NoHeapAllocation]
public void SetCachedPeer(Allocation * /* EndpointCore */ peer)
{
cachedPeer = peer;
}
[NoHeapAllocation]
public void SetPeerStateValid(bool valid)
{
peerStateValid = valid;
}
}
public class EndpointCoreException: Exception
{
public EndpointCoreException (string message) : base(message)
{
}
}
}