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

282 lines
11 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: TcpConnectionExpThread.sg
// Note: Provider-side helper for the IP Channel Contract
//
using System.Threading;
using System.Net.IP;
using Microsoft.SingSharp;
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using NetStack.Contracts;
using NetStack.Runtime;
using System.Collections;
using System;
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();
while(true) {
switch receive {
//
// ======================= Start state =======================
//
case ep.Connect(uint dstIP, ushort dstPort) :
{
TcpError error;
bool success = session.Connect(new IPv4(dstIP), dstPort, out error);
if (success)
{ ep.SendOK(); }
else
{ ep.SendCouldNotConnect(error); }
}
break;
case ep.BindLocalEndPoint(uint localIP, ushort localPort) :
{
bool success = session.BindLocalEndPoint(new IPv4(localIP), localPort);
if (success)
{ ep.SendOK(); }
else
{ ep.SendInvalidEndPoint(); }
}
break;
//
// ======================= Bound state =======================
//
case ep.Listen(int backlog) :
{
bool success = session.Listen(backlog);
if (success)
{ ep.SendOK(); }
else
{ ep.SendCouldNotListen(); }
}
break;
//
// ======================= Listening state =======================
//
case ep.IsSessionAvailable() :
{ ep.SendSessionIsAvailable(session.GetNumWaitingListenSessions() > 0); }
break;
case ep.Accept(TcpConnectionContract.Exp:PreConnected! newEP) :
{
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() :
{
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) :
{
byte[] in ExHeap data = session.PollData(timeout);
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
// no ConnectionClised message.
if (session.ValidForRead) {
ep.SendNoData(); // -> ReceiveOnly
}
else {
ep.SendNoMoreData(); // -> Zombie
}
}
}
else {
ep.SendData(data);
}
}
break;
case ep.Write(byte[]! in ExHeap data) :
{
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() :
{ ep.SendDataIsAvailable(session.IsDataAvailable()); }
break;
case ep.DoneSending() :
{ session.DoneSending(); }
break;
case ep.DoneReceiving() :
{ session.DoneReceiving(); }
break;
case ep.Abort() :
{ session.Abort(); }
break;
//
// ======================= Messages from multiple states =======================
//
case ep.GetLocalAddress() :
{ ep.SendIPAddress((uint)session.LocalAddress); }
break;
case ep.GetLocalPort() :
{ ep.SendPort(session.LocalPort); }
break;
case ep.GetRemoteAddress() :
{ ep.SendIPAddress((uint)session.RemoteAddress); }
break;
case ep.GetRemotePort() :
{ ep.SendPort(session.RemotePort); }
break;
case ep.Close() :
{ session.Close(); }
break;
case unsatisfiable :
session.Dispose();
delete ep;
// Exit this thread
return;
break;
}
}
}
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
}
}
}