/////////////////////////////////////////////////////////////////////////////// // // Microsoft Research Singularity // // Copyright (c) Microsoft Corporation. All rights reserved. // // File: Services\ServiceManager\JournalService.sg // // Note: // using System; using System.Threading; using Microsoft.SingSharp; using Microsoft.Singularity; using Microsoft.Singularity.Channels; using Microsoft.Singularity.Directory; using Microsoft.Singularity.ServiceManager; namespace Microsoft.Singularity.Services.ServiceManager { // JournalService is a service launcher. This object instantiates two // service processes: the target service and the logging service for it. // // JournalService sets up three endpoints for the logging service and two // channels for the target service. (* 'Contract' is omitted.) // For the logging service, // ManagedService Because it is a service. // DirectoryService.Imp To connect to a root directory service // DirectoryService.Exp To intercept the connection from the target // For the target, // ManagedService Because it is a service. // DirectoryService.Imp To intercept the connection to the root DS // internal class JournalService : Service { // Every resilient service process is associated with a dedicated // journal service process whose program name must be " // + Proxy". e.g. CounterProxy protected const string JSSuffix = "Proxy"; protected ResilientService target; protected Thread proxyThread; protected Object! recoveryLock; protected TRef controlRef; protected TRef recoveryRef; protected TRef directoryRef; protected TRef serviceProxyRef; protected TRef signalSenderRef; protected TRef signalReceiverRef; // name: The name of the target service (e.g. Counter) // ds: The channel to the root directory service. internal JournalService(string! serviceName, string! binaryName, [Claims]DirectoryServiceContract.Imp:Ready! ds) { base(serviceName, binaryName, ServiceType.Resilient, ds); recoveryLock = new Object(); } /// /// Starts Journal Service process and the target service process. /// internal override ServiceError StartServiceProcess() { bool msSuccess = false; bool dsSuccess = false; ServiceError report; ManagedServiceContract.Imp msImp; ManagedProxyContract.Exp mpExp; DirectoryServiceContract.Imp myDSImp; ServiceProxyContract.Imp proxyImp; ServiceControlContract.Imp! targetControl; ServiceControlContract.Exp! controlTarget; // // Setup Proxy Process // DebugStub.Print("JS: Create process '{0}'\n", __arglist(serviceName + JSSuffix)); process = new Process(new String[1]{serviceName + JSSuffix}, null, 5); SetupEndpoints(process, out msImp, out mpExp, out myDSImp, out proxyImp); if (msImp == null || myDSImp == null || mpExp == null || proxyImp == null) { delete msImp; delete mpExp; delete myDSImp; delete proxyImp; return ServiceError.TryAgain; } // // Start proxy // process.Start(); // // Make sure the proxy has been started // while (!(msSuccess && dsSuccess)) { switch receive { case msImp.Success(): DebugStub.Print("ms received\n"); msSuccess = true; break; case myDSImp.Success(): DebugStub.Print("sub received\n"); dsSuccess = true; break; case unsatisfiable: goto exit; break; } } exit: if (!msSuccess || !dsSuccess) { DebugStub.Print("JournalService: Service process " + "channel is closed.\n"); delete msImp; delete mpExp; delete myDSImp; delete proxyImp; return ServiceError.ChannelClosed; } DebugStub.Print("JS: Check\n"); managementEndpoint = new TRef(msImp); mpExp.SendSuccess(); recoveryRef = new TRef(mpExp); // // Create the target service process with the myDS DS // target = new ResilientService(serviceName, binaryName, myDSImp, proxyImp, this); report = target.StartServiceProcess(); if (report != ServiceError.None) { return report; } // // Start the counterpart // ServiceControlContract.NewChannel(out targetControl, out controlTarget); target.Start(controlTarget); switch receive { case targetControl.Success(): break; case unsatisfiable: DebugStub.Break(); break; } controlRef = new TRef(targetControl); DebugStub.Print("JournalService: StartServiceProcess EXIT\n"); return report; } private void SetupEndpoints(Process p, out ManagedServiceContract.Imp ms, out ManagedProxyContract.Exp mp, out DirectoryServiceContract.Imp d, out ServiceProxyContract.Imp sp) { ManagedServiceContract.Imp! msImp; ManagedServiceContract.Exp! msExp; ManagedProxyContract.Imp! mpImp; ManagedProxyContract.Exp! mpExp; DirectoryServiceContract.Imp! dsClient; DirectoryServiceContract.Imp! myDSImp; DirectoryServiceContract.Exp! myDSExp; ServiceProxyContract.Imp! proxyImp; ServiceProxyContract.Exp! proxyExp; ms = null; mp = null; d = null; sp = null; //TODO: Slot number management. Currently they are hard-coded. // // Service Manager endpoint to be given to the proxy // ManagedServiceContract.NewChannel(out msImp, out msExp); if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msExp)) { DebugStub.Print("JournalService: Endpoint cannot be set. " + "(ManagedService)\n"); delete msImp; return; } // // Root Directory Service endpoint // dsClient = DirectoryService.NewClientEndpoint(); if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)dsClient)) { DebugStub.Print("JournalService: Endpoint cannot be set. " + "(DirectoryService)\n"); delete msImp; return; } // // Substitute Directory Service endpoint // DirectoryServiceContract.NewChannel(out myDSImp, out myDSExp); if (!process.SetStartupEndpoint(2, (Endpoint * in ExHeap)myDSExp)) { DebugStub.Print("JournalService: Endpoint cannot be set. " + "(Substitute)\n"); delete msImp; delete myDSImp; return; } // // DS Transfer endpoint // ManagedProxyContract.NewChannel(out mpImp, out mpExp); if (!process.SetStartupEndpoint(3, (Endpoint * in ExHeap)mpImp)) { DebugStub.Print("JournalService: Endpoint cannot be set. " + "(ManagedProxy)\n"); delete msImp; delete myDSImp; delete mpExp; return; } // // Checkpointing and persistent memory space // ServiceProxyContract.NewChannel(out proxyImp, out proxyExp); if (!process.SetStartupEndpoint(4, (Endpoint * in ExHeap)proxyExp)) { DebugStub.Print("JournalService: Endpoint cannot be set. " + "(ServiceProxy)\n"); delete msImp; delete myDSImp; delete mpExp; delete proxyImp; return; } ms = msImp; mp = mpExp; d = myDSImp; sp = proxyImp; } /// /// Starts the service of a service process. This is done by sending /// a StartService message. /// // HI: Current implementation starts both the logging service and the // target service, because we assume that the logging service is // a part of the target service. This may conflicts the normal // (not resilient) service behavior. internal override ServiceError StartService() { ServiceError report; ManagedServiceContract.Imp:Ready management; ThreadTerminationContract.Imp! imp; ThreadTerminationContract.Exp! exp; ThreadTerminationContract.NewChannel(out imp, out exp); signalSenderRef = new TRef(imp); signalReceiverRef = new TRef(exp); proxyThread = new Thread(new ThreadStart(HandleProxy)); proxyThread.Start(); DebugStub.Print("JS: Start proxy\n"); // // Start JournalProducer service // management = managementEndpoint.Acquire(); management.SendStartService(); switch receive { case management.AckStartService(): break; case management.RecvNakStartService(): DebugStub.Print("JS: Can't start service\n"); report = ServiceError.Unknown; goto exit; break; case management.RecvChannelClosed(): report = ServiceError.ChannelClosed; goto exit; break; } DebugStub.Print("JS: Start server\n"); // // Start the target service // report = target.StartService(); if (report != ServiceError.None) { management.SendStopService(); switch receive { case management.RecvAckStopService(): break; case management.RecvBusy(): report = ServiceError.TryAgain; break; case management.ChannelClosed(): report = ServiceError.ChannelClosed; break; } } exit: managementEndpoint.Release(management); //DebugStub.Print("JournalService: StartService EXIT\n"); return report; } internal override ServiceError StopService() { ServiceError report; ManagedServiceContract.Imp:Ready management; ThreadTerminationContract.Imp:Start! signalToProxyThread; DebugStub.Print("JS: Enter StopService\n"); // // Stop the target service first. // report = target.StopService(); if (report != ServiceError.None) { return report; } // // Stop the logging service // signalToProxyThread = signalSenderRef.Acquire(); signalToProxyThread.SendStop(); switch receive { case signalToProxyThread.AckStop(): break; case signalToProxyThread.ChannelClosed(): break; } signalSenderRef.Release(signalToProxyThread); management = managementEndpoint.Acquire(); management.SendStopService(); switch receive { case management.RecvAckStopService(): report = ServiceError.None; break; case management.RecvBusy(): report = ServiceError.TryAgain; break; case management.ChannelClosed(): report = ServiceError.ChannelClosed; break; } managementEndpoint.Release(management); DebugStub.Print("JS: Exit StopService\n"); return report; } internal override ServiceError StopServiceProcess() { ServiceError report; //DebugStub.Print("JS: Enter StopServiceProcess\n"); report = target.StopServiceProcess(); if (report != ServiceError.None) { return report; } //DebugStub.Print("JS: Exit StopServiceProcess\n"); return base.StopServiceProcess(); } internal override ServiceError RestartService() { return RestartServiceProcess(); } internal override ServiceError RestartServiceProcess() { ServiceError error = ServiceError.Unknown; ServiceControlContract.Imp:Ready! control; if (!Monitor.TryEnter(recoveryLock)) { return ServiceError.TryAgain; } control = controlRef.Acquire(); control.SendRestartService(); switch receive { case control.AckRestartService(): error = ServiceError.None; break; case control.NakRestartService(err): error = err; break; case control.ChannelClosed(): error = ServiceError.ChannelClosed; break; } controlRef.Release(control); DebugStub.Print("JS: Exit RestartServiceProcess\n"); Monitor.Exit(recoveryLock); return error; } internal void RestartHelper() { RestartServiceProcess(); } private void HandleProxy() { ManagedProxyContract.Exp:Ready! proxy; ThreadTerminationContract.Exp:Start! signal; proxy = recoveryRef.Acquire(); signal = signalReceiverRef.Acquire(); for (;;) { switch receive { case proxy.RequestRecovery(): { try { Monitor.Enter(this); if (directoryRef == null) { new Thread(new ThreadStart(RestartHelper)).Start(); Monitor.Wait(this); } if (directoryRef != null) { proxy.SendAckRecovery(directoryRef.Acquire(), serviceProxyRef.Acquire()); directoryRef = null; } else { DebugStub.Break(); } } finally { Monitor.Exit(this); } break; } case proxy.RequestDSRecovery(): { DebugStub.Print("JS: Enter RequestRecovery\n"); try { Monitor.Enter(this); if (directoryRef == null) { new Thread(new ThreadStart(RestartHelper)).Start(); Monitor.Wait(this); } if (directoryRef != null) { proxy.SendAckDSRecovery( directoryRef.Acquire()); directoryRef = null; } else { DebugStub.Break(); } } finally { Monitor.Exit(this); } DebugStub.Print("JS: Exit RequestRecovery\n"); break; } case signal.Stop(): { signal.SendAckStop(); // Even if the proxy process waits for an ACK on the // 'proxy' channel, it is terminated by Service Manager // through the ManagedServiceContract channel. goto exit; break; } case signal.ChannelClosed(): { goto exit; break; } } // SWR } // END OF LOOP exit: signalReceiverRef.Release(signal); recoveryRef.Release(proxy); } internal void TransferDirectoryService([Claims]DirectoryServiceContract.Exp:Start! ds, [Claims]ServiceProxyContract.Exp:Start! sp) { //DebugStub.Print("JS: Enter TransferDS\n"); try { Monitor.Enter(this); directoryRef = new TRef(ds); serviceProxyRef = new TRef(sp); Monitor.Pulse(this); } finally { Monitor.Exit(this); } //DebugStub.Print("JS: Exit TransferDS\n"); } } // class JournalService }