singrdk/base/Services/ServiceManager/Service.sg

491 lines
17 KiB
Plaintext

///////////////////////////////////////////////////////////////////////////////
//
// Microsoft Research Singularity
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// File: Services\ServiceManager\Service.sg
//
// Note: Service counterpart
//
using System;
using System.Collections;
using System.Collections.Specialized;
using System.Threading;
using Microsoft.SingSharp;
using Microsoft.Singularity;
using Microsoft.Singularity.Directory;
using Microsoft.Singularity.Channels;
using Microsoft.Singularity.ServiceManager;
using Microsoft.Singularity.V1.Processes;
namespace Microsoft.Singularity.Services.ServiceManager
{
/// <summary>
/// Service process counterpart.
/// </summary>
internal abstract class Service
{
protected readonly string! serviceName;
protected readonly string! binaryName;
protected readonly ServiceType serviceType;
protected int id;
protected Process process;
private Thread thread;
/// <summary>
/// Management channel to the service
/// </summary>
protected TRef<ManagedServiceContract.Imp:Ready> managementEndpoint;
/// <summary>
/// User-Service Communication Relay Point
/// </summary>
private TRef<ServiceControlContract.Exp:Ready> controlProxy;
protected TRef<DirectoryServiceContract.Imp:Ready> directory;
private TRef<ThreadTerminationContract.Imp:Start> senderRef;
private TRef<ThreadTerminationContract.Exp:Start> receiverRef;
private TRef<ServiceProxyContract.Exp:Start> dummyRef;
internal Service(string! serviceName,
string! binaryName,
ServiceType serviceType,
[Claims]DirectoryServiceContract.Imp:Ready! ds)
{
this.serviceName = serviceName;
this.binaryName = binaryName;
this.serviceType = serviceType;
ThreadTerminationContract.Imp! imp;
ThreadTerminationContract.Exp! exp;
ThreadTerminationContract.NewChannel(out imp, out exp);
senderRef = new TRef<ThreadTerminationContract.Imp:Start>(imp);
receiverRef = new TRef<ThreadTerminationContract.Exp:Start>(exp);
directory = new TRef<DirectoryServiceContract.Imp:Ready>(ds);
}
~Service()
{
delete directory.Acquire();
delete senderRef.Acquire();
delete receiverRef.Acquire();
}
/// <summary>
/// Starts this service counter part. Doesn't start any service process.
/// </summary>
internal void Start([Claims]ServiceControlContract.Exp:Start! ep)
{
ep.SendSuccess();
controlProxy = new TRef<ServiceControlContract.Exp:Ready>(ep);
thread = new Thread(new ThreadStart(Run));
thread.Start();
}
/// <summary>
/// Terminates the service counter part.
/// </summary>
internal void Stop(out ServiceControlContract.Exp:Ready! ep)
{
ThreadTerminationContract.Imp:Start! leader;
// Terminates Run()
leader = senderRef.Acquire();
try {
leader.SendStop();
leader.RecvAckStop();
thread.Join();
}
catch (Exception) {
DebugStub.Print("Service: already stopped.\n");
}
// Give EP back
ep = controlProxy.Acquire();
controlProxy = null;
// Reuse for the next connection
senderRef.Release(leader);
}
/// <summary>
/// Brings up the service that this object represents. Throws
/// ProcessCreateException.
/// </summary>
internal virtual ServiceError StartServiceProcess()
{
ServiceError report;
ManagedServiceContract.Imp! msClient;
ManagedServiceContract.Exp! msServer;
DirectoryServiceContract.Imp! dsClient;
DebugStub.Print("Service: Create process '{0}'\n",
__arglist(binaryName));
process = new Process(new String[1]{binaryName}, null, 3);
// Create a channel to the service
ManagedServiceContract.NewChannel(out msClient, out msServer);
if (!process.SetStartupEndpoint(0, (Endpoint * in ExHeap)msServer)) {
DebugStub.Print("Endpoint cannot be set. (ManagedService)\n");
delete msClient;
return ServiceError.TryAgain;
}
dsClient = directory.Acquire();
if (!process.SetStartupEndpoint(1, (Endpoint * in ExHeap)dsClient)) {
DebugStub.Print("Endpoint cannot be set. " +
"(DirectoryService)\n");
delete msClient;
return ServiceError.TryAgain;
}
//
// ServiceProxyContract Dummy
//NOTE: this is necessary even though the default service never
// be able to use.
ServiceProxyContract.Imp! imp;
ServiceProxyContract.Exp! exp;
ServiceProxyContract.NewChannel(out imp, out exp);
process.SetStartupEndpoint(2, (Endpoint * in ExHeap)imp);
dummyRef = new TRef<ServiceProxyContract.Exp:Start>(exp);
process.Start();
// Make sure the process has been started
switch receive {
case msClient.RecvSuccess():
managementEndpoint = new TRef<ManagedServiceContract.Imp:Ready>(msClient);
report = ServiceError.None;
DebugStub.Print("-- Process ID: {0}\n",
__arglist(process.Id));
break;
case msClient.ChannelClosed():
DebugStub.Print("Service process's channel is closed.\n");
delete msClient;
report = ServiceError.ChannelClosed;
break;
}
//DebugStub.Print("Service.sg: StartServiceProcess EXIT\n");
return report;
}
/// <summary>
/// Shuts down the service process.
/// </summary>
internal virtual ServiceError StopServiceProcess()
{
ServiceError report = ServiceError.Unknown;
ManagedServiceContract.Imp:Ready ep;
//DebugStub.Print("Service: ENTER StopServiceProcess\n");
StopPoll();
if (managementEndpoint == null) {
return ServiceError.NotFound;
}
ep = managementEndpoint.Acquire();
if (process.State != ProcessState.Stopped) {
try {
ep.SendStop();
switch receive {
case ep.AckStop():
report = ServiceError.None;
break;
case ep.ChannelClosed():
report = ServiceError.None;
break;
}
}
catch (Exception) {
DebugStub.Print("already stopped\n");
}
}
process.Stop();
delete ep;
managementEndpoint = null;
report = ServiceError.None;
//DebugStub.Print("Service: EXIT StopServiceProcess\n");
return report;
}
internal virtual ServiceError RestartServiceProcess()
{
ServiceError report;
report = StopServiceProcess();
if (report == ServiceError.None) {
report = StartServiceProcess();
}
return report;
}
internal virtual ServiceError StartService()
{
ServiceError report;
ManagedServiceContract.Imp:Ready! client;
//DebugStub.Print("Service: StartService ENTER\n");
client = managementEndpoint.Acquire();
client.SendStartService();
switch receive {
case client.AckStartService():
report = ServiceError.None;
break;
case client.NakStartService():
report = ServiceError.TryAgain;
break;
case client.ChannelClosed():
report = ServiceError.ChannelClosed;
break;
}
managementEndpoint.Release(client);
//DebugStub.Print("Service: StartService EXIT\n");
return report;
}
internal virtual ServiceError StopService()
{
ServiceError report;
ManagedServiceContract.Imp:Ready! client;
DebugStub.Print("Service: StopService ENTER\n");
client = managementEndpoint.Acquire();
client.SendStopService();
switch receive {
case client.AckStopService():
report = ServiceError.None;
break;
case client.Busy():
report = ServiceError.TryAgain;
break;
case client.ChannelClosed():
DebugStub.Print("Service: client channel closed\n");
report = ServiceError.ChannelClosed;
break;
}
managementEndpoint.Release(client);
DebugStub.Print("Service: StopService EXIT\n");
return report;
}
internal virtual ServiceError RestartService()
{
ServiceError report;
report = StopService();
if (report == ServiceError.None) {
report = StartService();
}
return report;
}
internal virtual void StartPoll(long interval) {}
internal virtual void StopPoll() {}
internal bool IsBound()
{
if (controlProxy != null) {
return true;
}
else {
return false;
}
}
internal bool IsReachable()
{
bool result = false;
ServiceError report;
ManagedServiceContract.Imp:Ready! ep;
ep = managementEndpoint.Acquire();
ep.SendKnock();
switch receive {
case ep.RecvAlive():
result = true;
break;
case ep.ChannelClosed():
result = false;
break;
}
managementEndpoint.Release(ep);
return result;
}
protected virtual void BeforeLoop() {}
protected virtual void AfterLoop() {}
protected void Run()
{
ServiceError report;
ServiceControlContract.Exp:Ready! ep;
ThreadTerminationContract.Exp:Start! signal;
BeforeLoop();
ep = controlProxy.Acquire();
signal = receiverRef.Acquire();
for (;;) {
switch receive {
case ep.StartService():
{
DebugStub.Print("Service: Start service\n");
try {
report = StartServiceProcess();
if (report != ServiceError.None) {
ep.SendNakStartService(report);
break;
}
report = StartService();
if (report != ServiceError.None) {
ep.SendNakStartService(report);
break;
}
id = process.Id;
ServiceManager.Register(this);
ep.SendAckStartService();
DebugStub.Print("Service: started\n");
}
catch (ProcessCreateException) {
DebugStub.Print("Service: " +
"Process creation failed\n");
ep.SendNakStartService(ServiceError.NotFound);
}
catch (ProcessStateException) {
ep.SendNakStartService(ServiceError.TryAgain);
}
break;
}
case ep.StopService():
{
report = StopService();
if (report != ServiceError.None) {
ep.SendNakStopService(report);
break;
}
report = StopServiceProcess();
if (report != ServiceError.None) {
ep.SendNakStopService(report);
break;
}
ServiceManager.Deregister(this);
ep.SendAckStopService();
break;
}
case ep.RestartService():
{
DebugStub.Print("Service: Restarting '{0}'...\n",
__arglist(serviceName));
report = RestartService();
if (report != ServiceError.None) {
ep.SendNakRestartService(report);
break;
}
DebugStub.Print("Service: '{0}' restarted\n",
__arglist(serviceName));
ep.SendAckRestartService();
break;
}
case ep.StartPoll(interval):
{
StartPoll(interval);
ep.SendAckStartPoll();
break;
}
case ep.StopPoll():
{
StopPoll();
ep.SendAckStopPoll();
break;
}
case ep.ChannelClosed():
{
controlProxy = null;
goto exit;
break;
}
case signal.Stop():
{
signal.SendAckStop();
goto exit;
break;
}
case signal.ChannelClosed():
{
goto exit;
break;
}
}
}
exit:
if (controlProxy != null) {
controlProxy.Release(ep);
}
else {
delete ep;
}
DebugStub.Print("service counterpart quit.\n");
receiverRef.Release(signal);
AfterLoop();
}
internal virtual string! Name
{
get { return serviceName; }
private set {}
}
internal virtual string! Binary
{
get { return binaryName; }
private set{}
}
internal virtual ServiceType Type
{
get { return serviceType; }
private set{}
}
internal virtual int Id
{
get { return id; }
private set{}
}
internal virtual TRef<ManagedServiceContract.Imp:Ready>! Endpoint
{
get { return managementEndpoint; }
private set{}
}
public override bool Equals(object o)
{
Service s = o as Service;
if (s == null) {
return false;
}
return (this.process.Id == s.process.Id &&
this.managementEndpoint == s.managementEndpoint);
}
}
}