/////////////////////////////////////////////////////////////////////////////// // // 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 providerRef; private TRef myProviderRef; private TRef internalImpRef; private TRef 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(ep); ThreadTerminationContract.Imp! imp; ThreadTerminationContract.Exp! exp; ThreadTerminationContract.NewChannel(out imp, out exp); internalImpRef = new TRef(imp); internalExpRef = new TRef(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(ep); } else { providerRef.Release(ep); } thread.Start(); } internal void Stop(out ServiceProviderContract.Imp:Start! ep) { TRef 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 {} } } }