singrdk/base/Applications/cassini/Singularity/Dispatcher.sg

339 lines
13 KiB
Plaintext
Raw Permalink Normal View History

2008-03-05 09:52:00 -05:00
//////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// Note:
// This file holds the Singularity-specific web app dispatcher that knows
// how to invoke web apps under Singularity.
//
using System;
using Microsoft.SingSharp;
using Microsoft.SingSharp.Runtime;
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.V1.Services;
using Microsoft.Singularity.WebApps;
using Microsoft.Singularity.WebApps.Contracts;
using System.Diagnostics;
2008-11-17 18:29:00 -05:00
using System.Threading;
2008-03-05 09:52:00 -05:00
using System.Web;
namespace Microsoft.VisualStudio.WebHost
{
internal class Dispatcher
{
2008-11-17 18:29:00 -05:00
// Parameters for load balancing.
private static TRef<WebAppContract.Imp:ProcessingState> m_AppConnBalance;
private static long m_loadTimeout; // If longer than this then, offload more requests.
private static int m_loadBase = 0; // # of requests to offload before handling one locally.
private static int m_loadCount = 0; // # of requests currently offloaded.
private static int m_maxLoadBase = 5;
// When m_loadBase > 0 then we count it page request with m_loadCount.
// If m_loadCount != m_loadBase, we use the balancing WebApp.
// When m_loadCount >= m_loadBase, we use the child WebApp and reset
// m_loadCount to 0.
// We increment m_loadBase (to a max of m_maxLoadBase) whenever
// the time servicing a request (on the child WebApp) exceeds
// m_loadTimeout.
// Standard parameters
2008-03-05 09:52:00 -05:00
private static TRef<WebAppContract.Imp:ProcessingState> m_AppConn;
2008-11-17 18:29:00 -05:00
private static bool m_quitUrl;
private static bool m_verbose;
2008-03-05 09:52:00 -05:00
private static string[] m_childArgs;
private static ulong m_TotalRemoteTime = 0;
private static ulong m_TotalRemoteHits = 0;
private const string QUIT_URI = "quit";
public static void DispatchRequest(Request! request)
{
string uriPath = request.GetUriPath();
if (m_quitUrl && uriPath.Length >= QUIT_URI.Length &&
uriPath.Substring(uriPath.Length - QUIT_URI.Length) == QUIT_URI)
{
request.CloseConnection();
ProcessService.Stop(0);
}
// Make the appropriate request object
IHttpRequest localRequest = new LocalHttpRequest(request);
// Call out-of-proc (usual case)
HttpRequestContract.Imp! reqConnImp;
HttpRequestContract.Exp! reqConnExp;
HttpRequestContract.NewChannel(out reqConnImp, out reqConnExp);
2008-11-17 18:29:00 -05:00
// [!!!] We should decide here which connection to use.
// [!!!] So we probably need some idea of load.
bool useBalance = false;
if (m_AppConnBalance != null) {
#if true
int count = Interlocked.Increment(ref m_loadCount);
//Console.WriteLine("- count={0}, base={1}.", count, m_loadBase);
if (m_loadBase > 0) {
if (count >= m_loadBase) {
//Console.WriteLine("+ count={0}, base={1}.", count, m_loadBase);
m_loadCount = 0;
useBalance = false;
}
else {
useBalance = true;
}
}
#endif
#if false
if (useBalance) {
Console.WriteLine("--- Using balance connection.");
}
#endif
}
ulong started = Processor.CycleCount;
DateTime beg = new DateTime();
2008-03-05 09:52:00 -05:00
2008-11-17 18:29:00 -05:00
if (useBalance) {
WebAppContract.Imp balConn = m_AppConnBalance.Acquire();
try {
// Start processing this request
balConn.SendProcess(reqConnImp);
switch receive {
case balConn.OK():
break;
case balConn.ChannelClosed():
Console.WriteLine("Balance child died, no restart.");
break;
}
}
finally {
m_AppConnBalance.Release(balConn);
2008-03-05 09:52:00 -05:00
}
}
2008-11-17 18:29:00 -05:00
else {
WebAppContract.Imp appConn = m_AppConn.Acquire();
try {
// Start processing this request
appConn.SendProcess(reqConnImp);
switch receive {
case appConn.OK():
break;
case appConn.ChannelClosed():
Console.WriteLine("Child died, attempting to restart.");
delete appConn;
appConn = StartChild();
break;
}
}
finally {
m_AppConn.Release(appConn);
}
2008-03-05 09:52:00 -05:00
}
// Start servicing the request contract
RequestExporter.ServiceRequestChannel(localRequest, reqConnExp);
// Clean up
delete reqConnExp;
2008-11-17 18:29:00 -05:00
DateTime end = DateTime.UtcNow;
TimeSpan span = end - beg;
Console.WriteLine("- {0,8} ms : vs {1}", span.Milliseconds, m_loadTimeout);
if (!useBalance) {
if (span.Milliseconds > m_loadTimeout && m_loadBase < m_maxLoadBase) {
Interlocked.Increment(ref m_loadBase);
Console.WriteLine("- Incremented m_loadBase to {0}", m_loadBase);
}
}
2008-03-05 09:52:00 -05:00
ulong elapsed = Processor.CycleCount - started;
2008-11-17 18:29:00 -05:00
if (m_verbose) {
Console.WriteLine("served \"" + request.GetUriPath() + "\" in " + elapsed + " cycles");
}
2008-03-05 09:52:00 -05:00
++m_TotalRemoteHits;
2008-11-17 18:29:00 -05:00
if (m_TotalRemoteHits > 1) {
2008-03-05 09:52:00 -05:00
m_TotalRemoteTime += elapsed;
//Console.WriteLine("Average remote time: " + m_TotalRemoteTime / (m_TotalRemoteHits - 1));
}
2008-11-17 18:29:00 -05:00
else {
2008-03-05 09:52:00 -05:00
//Console.WriteLine("First remote hit (cold cache)");
}
request.CloseConnection();
}
public static WebAppContract.Imp:ProcessingState StartChild()
{
// Start up the webapp so it's ready to respond to requests
WebAppContract.Imp! appConnImp;
WebAppContract.Exp! appConnExp;
WebAppContract.NewChannel(out appConnImp, out appConnExp);
2008-11-17 18:29:00 -05:00
// REVIEW. should really inspect the manifest and determine what
// endpoints are needed by the child process. For example, specweb99
2008-03-05 09:52:00 -05:00
// now needs both the WebAppContract and a directory service contract
Process child = new Process(m_childArgs, (Endpoint * in ExHeap)appConnExp);
child.Start();
appConnImp.RecvWebAppReady();
return appConnImp;
}
public static bool Initialize(string! appName,
string[] appArgs,
bool verbose,
bool quitUrl)
{
m_quitUrl = quitUrl;
m_verbose = verbose;
// Start up the webapp so it's ready to respond to requests
string[] args;
if (appArgs != null) {
args = new string[1 + appArgs.Length];
args[0] = appName;
for (int i = 0; i < appArgs.Length; ++i) {
args[i + 1] = appArgs[i];
}
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
args = new string[1];
args[0] = appName;
}
m_childArgs = args;
WebAppContract.Imp! appConnImp = StartChild();
if (appConnImp == null) {
2008-11-17 18:29:00 -05:00
throw new Exception(String.Format("Could not create process {0}",args[0]));
2008-03-05 09:52:00 -05:00
}
m_AppConn = new TRef<WebAppContract.Imp:ProcessingState>(appConnImp);
return true;
}
2008-11-17 18:29:00 -05:00
public static bool BalanceConnection([Claims] WebAppContract.Imp:ProcessingState appImp,
long loadTimeout)
{
m_loadTimeout = loadTimeout;
Console.WriteLine(" - {0} ms is loadTimeout", m_loadTimeout);
m_AppConnBalance = new TRef<WebAppContract.Imp:ProcessingState>(appImp);
return true;
}
2008-03-05 09:52:00 -05:00
internal class RequestExporter
{
public static void ServiceRequestChannel(IHttpRequest! request,
HttpRequestContract.Exp:Start
opt(HttpRequestContract.Completed)! conn)
{
bool done = false;
2008-11-17 18:29:00 -05:00
while (!done) {
switch receive {
2008-03-05 09:52:00 -05:00
case conn.GetUriPath() :
conn.SendUriPath(Bitter.FromString2(request.GetUriPath()));
break;
case conn.GetQueryString() :
conn.SendQueryString(Bitter.FromString(request.GetQueryString()));
break;
case conn.GetVerb() :
conn.SendVerb(Bitter.FromString2(request.GetVerb()));
break;
case conn.GetHeader(char[]! in ExHeap headerChars) :
string headerName = Bitter.ToString(headerChars);
delete headerChars;
string headerVal = request.GetHeader(headerName);
if (headerVal != null) {
conn.SendHeaderValue(Bitter.FromString(headerVal));
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
conn.SendHeaderValue(null);
}
break;
case conn.GetBody() :
byte[] bodyData = request.GetBodyData();
if (bodyData != null) {
conn.SendBodyData(Bitter.FromByteArray(bodyData));
2008-11-17 18:29:00 -05:00
}
else {
2008-03-05 09:52:00 -05:00
conn.SendBodyData(null);
}
break;
case conn.GetRemoteAddress() :
conn.SendRemoteAddress(Bitter.FromString(request.GetRemoteAddress()));
break;
case conn.SendStatus(int code, char[]! in ExHeap descChars) :
{
string description = Bitter.ToString(descChars);
delete descChars;
request.SendStatus(code, description);
conn.SendOK();
}
break;
case conn.SendHeader(char[]! in ExHeap nameChars, char[]! in ExHeap valChars) :
{
string name = Bitter.ToString(nameChars);
delete nameChars;
string value = Bitter.ToString(valChars);
delete valChars;
request.SendHeader(name, value);
conn.SendOK();
}
break;
case conn.SendBodyData(byte[]! in ExHeap dataBytes) :
{
request.SendBodyData(dataBytes);
conn.SendOK();
}
break;
case conn.Done() :
request.Done();
done = true;
break;
case conn.ChannelClosed() :
request.Done();
done = true;
break;
case unsatisfiable :
// This is even more unexpected
DebugStub.Break();
done = true;
break;
}
}
}
}
}
}