333 lines
11 KiB
Plaintext
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 {}
|
||
|
}
|
||
|
}
|
||
|
}
|