520 lines
20 KiB
Plaintext
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
|
||
|
}
|