singrdk/base/Libraries/Resiliency/ServiceProviderProxy.sg

333 lines
11 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Libraries\Resiliency\ServiceProviderProxy.sg
//
// Note:
//
using System;
using System.Collections;
using System.Threading;
using Microsoft.SingSharp;
using Microsoft.Singularity;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Services;
namespace Microsoft.Singularity.Resiliency
{
internal sealed class ServiceProviderProxy : IRecoveryAware, ICheckpoint
{
private readonly string! path;
private IRecoveryAware! dsProxy;
private JournaletFactory! factory;
private JournaletGroup! group;
private IList! blackList;
private Thread thread;
private bool running;
private TRef<ServiceProviderContract.Imp:Start> providerRef;
private TRef<ServiceProviderContract.Exp:Start> myProviderRef;
private TRef<ThreadTerminationContract.Imp:Start> internalImpRef;
private TRef<ThreadTerminationContract.Exp:Start> internalExpRef;
internal ServiceProviderProxy(IRecoveryAware! dsProxy,
string! path,
JournaletFactory! factory,
[Claims]ServiceProviderContract.Exp:Start! ep)
{
this.dsProxy = dsProxy;
this.path = path;
this.factory = factory;
this.group = new JournaletGroup();
this.blackList = new ArrayList();
this.myProviderRef = new TRef<ServiceProviderContract.Exp:Start>(ep);
ThreadTerminationContract.Imp! imp;
ThreadTerminationContract.Exp! exp;
ThreadTerminationContract.NewChannel(out imp, out exp);
internalImpRef = new TRef<ThreadTerminationContract.Imp:Start>(imp);
internalExpRef = new TRef<ThreadTerminationContract.Exp:Start>(exp);
}
private void Run()
{
ServiceProviderContract.Exp:Start! ep;
ThreadTerminationContract.Exp:Start! signal;
if (myProviderRef == null) {
DebugStub.Print("PPx: not initialized!\n");
return;
}
lock (this) {
running = true;
}
try {
Recover();
}
catch (Exception) {
DebugStub.Print("PPx: exception during recovery\n");
group.Stop();
return;
}
ep = myProviderRef.Acquire();
signal = internalExpRef.Acquire();
for (;;) {
DebugStub.Print("PPx: set\n");
switch receive {
case ep.Connect(serviceEp):
{
if (factory.Accept(serviceEp)) {
try {
if (CreateJournalet(serviceEp)) {
ep.SendAckConnect();
}
else {
ep.SendNackConnect(null);
}
}
catch (Exception) {
ep.SendNackConnect(null);
dsProxy.NotifyRecovery();
goto exit;
}
}
else {
ep.SendNackConnect(serviceEp);
}
break;
}
case ep.ChannelClosed():
{
group.Stop();
goto exit;
break;
}
// FIXME: This is confusing because the Stop message is
// used to trigger the recovery action; it's used to
// terminate threads in other parts of the proxy.
case signal.Stop():
{
signal.SendAckStop();
dsProxy.NotifyRecovery();
goto exit;
break;
}
// An explicit channel close terminates this thread.
case signal.ChannelClosed():
{
group.Stop();
goto exit;
break;
}
}
}
exit:
myProviderRef.Release(ep);
internalExpRef.Release(signal);
lock (this) {
running = false;
}
DebugStub.Print("PPx: Quit\n");
}
///
/// Creates and activates a new Journalet with the specified endpoint.
///
private bool CreateJournalet([Claims]ServiceContract.Exp:Start! ep)
{
bool success = false;
bool throwException = false;
Journalet2! journalet;
ServiceContract.Exp! newEp;
ServiceProviderContract.Imp:Start! provider;
journalet = factory.CreateJournalet(ep, out newEp);
provider = providerRef.Acquire();
provider.SendConnect(newEp);
switch receive {
case provider.AckConnect():
success = true;
break;
case provider.NackConnect(rejected):
delete rejected;
break;
case provider.ChannelClosed():
// recovery
throwException = true;
break;
}
providerRef.Release(provider);
if (throwException) {
throw new ChannelClosedException();
}
if (success) {
if (group.Parent == null) {
group.Parent = this;
}
group.Add(journalet);
journalet.Start();
}
return success;
}
private void Recover()
{
bool throwException = false;
Journalet2 journalet;
ServiceContract.Exp! newEp;
ServiceProviderContract.Imp:Start! provider;
DebugStub.Print("PPx: Enter Recovery\n");
provider = providerRef.Acquire();
foreach (Object obj in group) {
if (obj == null) {
break;
}
journalet = obj as Journalet2;
if (journalet == null) {
continue;
}
// journalet must be inactive.
journalet.CreateEndpoint(out newEp);
provider.SendConnect(newEp);
switch receive {
case provider.AckConnect():
journalet.Start();
break;
case provider.NackConnect(rejected):
delete rejected;
blackList.Add(journalet);
break;
case provider.ChannelClosed():
throwException = true;
break;
}
if (throwException) {
providerRef.Release(provider);
throw new ChannelClosedException();
}
}
foreach (Object obj in blackList) {
if (obj == null) {
break;
}
journalet = obj as Journalet2;
if (journalet != null) {
group.Remove(journalet);
}
}
blackList.Clear();
//
// Restart journalets
//
group.Start();
providerRef.Release(provider);
DebugStub.Print("PPx: Exit Recovery\n");
}
public void NotifyRecovery()
{
ThreadTerminationContract.Imp:Start! signal;
DebugStub.Print("PPx: NotifyRecovery\n");
//
// Terminates itself, then the thread reports to Directory Proxy
//
signal = internalImpRef.Acquire();
signal.SendStop();
switch receive {
case signal.AckStop():
break;
case unsatisfiable:
//DebugStub.Break();
break;
}
internalImpRef.Release(signal);
}
public void UpdateCheckpoint()
{
//DebugStub.Print("PPx: Enter UpdateCheckpoint\n");
dsProxy.UpdateCheckpoint();
//DebugStub.Print("PPx: Exit UpdateCheckpoint\n");
}
internal void Start([Claims]ServiceProviderContract.Imp:Start! ep)
{
assert !running;
thread = new Thread(new ThreadStart(Run));
if (providerRef == null) {
providerRef = new TRef<ServiceProviderContract.Imp:Start>(ep);
}
else {
providerRef.Release(ep);
}
thread.Start();
}
internal void Stop(out ServiceProviderContract.Imp:Start! ep)
{
TRef<ServiceProviderContract.Imp:Start> reference;
DebugStub.Print("PPx: Stop\n");
if (running) {
// Stop the thread
NotifyRecovery();
thread.Join();
DebugStub.Print("PPx: Joined\n");
}
ep = providerRef.Acquire();
}
internal void Terminate(out ServiceProviderContract.Imp:Start! ep)
{
delete internalImpRef.Acquire();
ep = providerRef.Acquire();
}
public void Suspend()
{
group.Suspend();
}
public void Flush()
{
group.Flush();
}
public void Resume()
{
group.Resume();
}
internal string! Path
{
get { return path; }
private set {}
}
}
}