singrdk/base/Services/ServiceManager/JournalService.sg

520 lines
20 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
//
// 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 "<service name>
// + Proxy". e.g. CounterProxy
protected const string JSSuffix = "Proxy";
protected ResilientService target;
protected Thread proxyThread;
protected Object! recoveryLock;
protected TRef<ServiceControlContract.Imp:Ready> controlRef;
protected TRef<ManagedProxyContract.Exp:Ready> recoveryRef;
protected TRef<DirectoryServiceContract.Exp:Start> directoryRef;
protected TRef<ServiceProxyContract.Exp:Start> serviceProxyRef;
protected TRef<ThreadTerminationContract.Imp:Start> signalSenderRef;
protected TRef<ThreadTerminationContract.Exp:Start> 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();
}
/// <summary>
/// Starts Journal Service process and the target service process.
/// </summary>
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<ManagedServiceContract.Imp:Ready>(msImp);
mpExp.SendSuccess();
recoveryRef = new TRef<ManagedProxyContract.Exp:Ready>(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<ServiceControlContract.Imp:Ready>(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;
}
/// <summary>
/// Starts the service of a service process. This is done by sending
/// a <i>StartService</i> message.
/// </summary>
// 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<ThreadTerminationContract.Imp:Start>(imp);
signalReceiverRef = new TRef<ThreadTerminationContract.Exp:Start>(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<DirectoryServiceContract.Exp:Start>(ds);
serviceProxyRef = new TRef<ServiceProxyContract.Exp:Start>(sp);
Monitor.Pulse(this);
}
finally {
Monitor.Exit(this);
}
//DebugStub.Print("JS: Exit TransferDS\n");
}
} // class JournalService
}