singrdk/base/Services/NetStack/Channels.Private/TCPExpConnection.sg

383 lines
16 KiB
Plaintext
Raw Normal View History

2008-11-17 18:29:00 -05:00
////////////////////////////////////////////////////////////////////////////////
2008-03-05 09:52:00 -05:00
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Note: Provider-side helper for the IP Channel Contract
//
2008-11-17 18:29:00 -05:00
using System;
2008-03-05 09:52:00 -05:00
using System.Net.IP;
2008-11-17 18:29:00 -05:00
using System.Threading;
2008-03-05 09:52:00 -05:00
using Microsoft.SingSharp;
using Microsoft.Singularity.Channels;
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
using NetStack.Contracts;
using NetStack.Runtime;
namespace NetStack.Channels.Private
{
public class TcpConnectionExpThread
{
private TRef<TcpConnectionContract.Exp:ReadyState>! chanEP;
private TcpSession! session;
public void Run()
{
try {
RunInternal();
}
finally {
// This breaks the
// NetStack's abstraction - we should be calling
// a new Core method to recycle the session
TcpSessionPool.Recycle(session);
}
}
private void RunInternal()
{
TcpConnectionContract.Exp! ep = chanEP.Acquire();
2008-11-17 18:29:00 -05:00
while (true) {
2008-03-05 09:52:00 -05:00
switch receive {
//
// ======================= Start state =======================
//
case ep.Connect(uint dstIP, ushort dstPort) :
{
2008-11-17 18:29:00 -05:00
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Connect);
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
TcpError tcpError;
bool success = session.Connect(new IPv4(dstIP), dstPort, out tcpError);
if (success) {
ep.SendOK();
}
else {
ep.SendCouldNotConnect(tcpError);
}
2008-03-05 09:52:00 -05:00
}
break;
case ep.BindLocalEndPoint(uint localIP, ushort localPort) :
{
2008-11-17 18:29:00 -05:00
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.BindLocalEndPoint);
2008-03-05 09:52:00 -05:00
bool success = session.BindLocalEndPoint(new IPv4(localIP), localPort);
2008-11-17 18:29:00 -05:00
if (success) {
ep.SendOK();
}
else {
ep.SendInvalidEndPoint();
}
2008-03-05 09:52:00 -05:00
}
break;
//
// ======================= Bound state =======================
//
case ep.Listen(int backlog) :
{
2008-11-17 18:29:00 -05:00
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Listen);
2008-03-05 09:52:00 -05:00
bool success = session.Listen(backlog);
2008-11-17 18:29:00 -05:00
if (success) {
ep.SendOK();
}
else {
ep.SendCouldNotListen();
}
2008-03-05 09:52:00 -05:00
}
break;
//
// ======================= Listening state =======================
//
case ep.IsSessionAvailable() :
2008-11-17 18:29:00 -05:00
{
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogQueryContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.IsSessionAvailable);
ep.SendSessionIsAvailable(session.GetNumWaitingListenSessions() > 0);
}
2008-03-05 09:52:00 -05:00
break;
case ep.Accept(TcpConnectionContract.Exp:PreConnected! newEP) :
{
2008-11-17 18:29:00 -05:00
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Accept);
2008-03-05 09:52:00 -05:00
TcpSession newSession = (TcpSession)session.Accept();
if (newSession != null) {
// Transition the newEP to the Connected state
newEP.SendReady();
// Wrap it up in a new thread
TcpConnectionExpThread newConnection = new TcpConnectionExpThread(newEP, newSession);
newConnection.Start();
ep.SendOK();
}
else {
// This is not expected; Accept() is supposed
// to block until there is a new, established
// connection.
delete newEP;
throw new Exception("Unexpected null return value from TcpSession.Accept");
}
}
break;
//
// ======================= Connected state =======================
//
case ep.Read() :
{
2008-11-17 18:29:00 -05:00
// Log DataTransfer Contract Call to RingBuffer and/or Debugger
session.LogDataTransferContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Read);
2008-03-05 09:52:00 -05:00
byte[] in ExHeap data = session.ReadData();
if (data == null) {
if (ep.InState(TcpConnectionContract.ReadResult.Value)) {
if (session.ValidForWrite) {
ep.SendNoMoreData(); // -> SendOnly
}
else {
ep.SendConnectionClosed(); // -> Zombie
}
}
else {
// Must be in the ROReadResult state. Here, there is
// no ConnectionClosed message.
ep.SendNoMoreData(); // -> Zombie
}
}
else {
// Note the unfortunate data copy.
ep.SendData(data);
}
}
break;
case ep.PollRead(int timeout) :
{
2008-11-17 18:29:00 -05:00
// Log DataTransfer Contract Call to RingBuffer and/or Debugger
session.LogDataTransferContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.PollRead);
byte[] in ExHeap data = session.PollData(TimeSpan.FromMilliseconds(timeout));
2008-03-05 09:52:00 -05:00
if (data == null) {
if (ep.InState(TcpConnectionContract.PollReadResult.Value)) {
if (session.ValidForRead) {
ep.SendNoData(); // Just no data for now. -> Connected
}
else if (session.ValidForWrite) {
ep.SendNoMoreData(); // Can't read anymore, but can write -> SendOnly
}
else {
ep.SendConnectionClosed(); // Can't read or write -> Zombie
}
}
else {
// Must be in the ROPollReadResult state. Here, there is
2008-11-17 18:29:00 -05:00
// no ConnectionClosed message.
2008-03-05 09:52:00 -05:00
if (session.ValidForRead) {
ep.SendNoData(); // -> ReceiveOnly
}
else {
ep.SendNoMoreData(); // -> Zombie
}
}
}
else {
ep.SendData(data);
}
}
break;
case ep.Write(byte[]! in ExHeap data) :
{
2008-11-17 18:29:00 -05:00
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogDataTransferContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Write);
2008-03-05 09:52:00 -05:00
if (!session.ValidForWrite) {
delete data;
ep.SendCantSend(); // -> Zombie
}
else {
int bytesToWrite = data.Length;
int written = session.WriteData(data);
if (written == -1) {
// A -1 return value indicates the connection has been closed
// underneath us while we were trying to write.
ep.SendCantSend();
}
else if (written != bytesToWrite) {
// This is unexpected; the current implementation always
// blocks and writes as much data as is provided.
throw new Exception("Unexpected partial write in TcpSession.WriteData");
}
else {
ep.SendOK();
}
}
}
break;
case ep.IsDataAvailable() :
2008-11-17 18:29:00 -05:00
{
// Log Query Contract Call to RingBuffer and/or Debugger
session.LogQueryContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.IsDataAvailable);
ep.SendDataIsAvailable(session.IsDataAvailable());
}
2008-03-05 09:52:00 -05:00
break;
case ep.DoneSending() :
2008-11-17 18:29:00 -05:00
{
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.DoneSending);
session.DoneSending();
}
2008-03-05 09:52:00 -05:00
break;
case ep.DoneReceiving() :
2008-11-17 18:29:00 -05:00
{
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.DoneReceiving);
session.DoneReceiving();
}
2008-03-05 09:52:00 -05:00
break;
case ep.Abort() :
2008-11-17 18:29:00 -05:00
{
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Abort);
session.Abort();
}
2008-03-05 09:52:00 -05:00
break;
//
// ======================= Messages from multiple states =======================
//
case ep.GetLocalAddress() :
2008-11-17 18:29:00 -05:00
{
// Log Info Contract Call to RingBuffer and/or Debugger
session.LogInfoContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.GetLocalAddress);
ep.SendIPAddress((uint)session.LocalAddress);
}
2008-03-05 09:52:00 -05:00
break;
case ep.GetLocalPort() :
2008-11-17 18:29:00 -05:00
{
// Log Info Contract Call to RingBuffer and/or Debugger
session.LogInfoContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.GetLocalPort);
ep.SendPort(session.LocalPort);
}
2008-03-05 09:52:00 -05:00
break;
case ep.GetRemoteAddress() :
2008-11-17 18:29:00 -05:00
{
// Log Info Contract Call to RingBuffer and/or Debugger
session.LogInfoContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.GetRemoteAddress);
ep.SendIPAddress((uint)session.RemoteAddress);
}
2008-03-05 09:52:00 -05:00
break;
case ep.GetRemotePort() :
2008-11-17 18:29:00 -05:00
{
// Log Info Contract Call to RingBuffer and/or Debugger
session.LogInfoContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.GetRemotePort);
ep.SendPort(session.RemotePort);
}
2008-03-05 09:52:00 -05:00
break;
case ep.Close() :
2008-11-17 18:29:00 -05:00
{
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.Close);
session.Close();
}
2008-03-05 09:52:00 -05:00
break;
2008-11-17 18:29:00 -05:00
case ep.ChannelClosed() :
{
// Log StateChange Contract Call to RingBuffer and/or Debugger
session.LogStateChangeContractCall(
TcpSessionEventsSource.TcpSessionContractEntrypoints.ChannelClosed);
// Cleanup the Session and EndPoint.
session.Close();
2008-03-05 09:52:00 -05:00
session.Dispose();
delete ep;
2008-11-17 18:29:00 -05:00
2008-03-05 09:52:00 -05:00
// Exit this thread
return;
2008-11-17 18:29:00 -05:00
}
break;
2008-03-05 09:52:00 -05:00
}
}
}
public TcpConnectionExpThread([Claims]TcpConnectionContract.Exp:ReadyState! ep)
{
chanEP = new TRef<TcpConnectionContract.Exp:ReadyState>(ep);
session = (!)((TcpSession)Core.Instance().CreateSession("TCP"));
}
private TcpConnectionExpThread([Claims]TcpConnectionContract.Exp:Connected! ep, TcpSession! newSession)
{
chanEP = new TRef<TcpConnectionContract.Exp:ReadyState>(ep);
session = (!)newSession;
}
public void Start()
{
#if THREAD_POOL
Core.Instance().ThreadPool.QueueUserWorkItem(new ThreadStart(Run));
#else
Thread newThread = new Thread(new ThreadStart(Run));
newThread.Start();
#endif
}
}
}