GithubHelp home page GithubHelp logo

seraksab / littleforker Goto Github PK

View Code? Open in Web Editor NEW

This project forked from damianh/littleforker

0.0 0.0 0.0 1.01 MB

A .NET utility library to spawn, supervise and (optionally) cleanly shut down child processes.

License: Apache License 2.0

Shell 0.50% C# 98.88% Batchfile 0.61%

littleforker's Introduction

Little Forker

CI NuGet feedz.io

A utility to aid in the launching and supervision of processes. The original use case is installing a single service who then spawns other processes as part of a multi-process application.

Features

  1. ProcessExitedHelper: a helper around Process.Exited with some additional logging and event raising if the process has already exited or not found.

  2. ProcessSupervisor: allows a parent process to launch a child process and lifecycle is represented as a state machine. Supervisors can participate in co-operative shutdown if supported by the child process.

  3. CooperativeShutdown: allows a process to listen for a shutdown signal over a NamedPipe for a parent process to instruct a process to shutdown.

Installation

dotnet add package LittleForker

CI packages are on personal feed: https://www.myget.org/F/dh/api/v3/index.json

Using

1. ProcessExitedHelper

This helper is typically used by "child" processes to monitor a "parent" process so that it exits itself when the parent exits. It's also safe guard in co-operative shut down if the parent failed to signal correctly (i.e. it crashed).

It wraps Process.Exited with some additional behaviour:

  • Raises the event if the process is not found.
  • Raises the event if the process has already exited which would otherwise result in an InvalidOperationException
  • Logging.

This is something simple to implement in your own code so you may consider copying it if you don't want a dependency on LittleForker.

Typically you will tell a process to monitor another process by passing in the other process's Id as a command line argument. Something like:

.\MyApp --ParentProcessID=12345

Here we extract the CLI arg using Microsoft.Extensions.Configuration, watch for a parent to exit and exit ourselves when that happens.

var configRoot = new ConfigurationBuilder()
   .AddCommandLine(args)
   .Build();

var parentPid = _configRoot.GetValue<int>("ParentProcessId");
using(new ProcessExitedHelper(parentPid, exitedHelper => Environment.Exit(0)))
{
   // Rest of application
}

Environment.Exit(0) is quite an abrupt way to shut town; you may want to handle things more gracefully such as flush data, cancel requests in flight etc. For an example, see NotTerminatingProcess Run() that uses a CancellationTokenSource.

2. ProcessSupervisor

Process supervisor launches a process and tracks it's lifecycle that is represented by a state machine. Typically use case is a "parent" processes launching one or more "child" processes.

There are two types of processes that are supported:

  1. Self-Terminating where the process will exit of it's own accord.
  2. Non-Terminating is a process that never shut down unless it is signalled to do so (if it participates in co-operative shutdown) or is killed.

A process's state is represented by ProcessSupervisor.State enum:

  • NotStarted,
  • Running,
  • StartFailed,
  • Stopping,
  • ExitedSuccessfully,
  • ExitedWithError,
  • ExitedUnexpectedly,
  • ExitedKilled

... with the transitions between them described with this state machine depending whether self-terminating or non-terminating:

statemachine

Typically, you will want to launch a process and wait until it is in a specific state before continuing (or handle errors).

// create the supervisor
var supervisor = new ProcessSupervisor(
   processRunType: ProcessRunType.NonTerminating, // Expected to be a process that doesn't stop
   workingDirectory: Environment.CurrentDirectory,
   processPath: "dotnet",
   arguments: "./LongRunningProcess/LongRunningProcess.dll");

// attach to events
supervisor.StateChanged += state => { /* handle state changes */ };
supervisor.OutputDataReceived += s => { /* console output */ }

// start the supervisor which will launch the process
await supervisor.Start();

// ... some time later
// attempts a co-operative shutdown with a timeout of 3
// seconds otherwise kills the process

await supervisor.Stop(TimeSpan.FromSeconds(3));

With an async extension, it is possible to await a supervisor state:

var exitedSuccessfully = supervisor.WhenStateIs(ProcessSupervisor.State.ExitedSuccessfully);
await supervisor.Start();
await Task.WhenAny(exitedSuccessfully).

You can also leverage tasks to combine waiting for various expected states:

var startFailed = supervisor.WhenStateIs(ProcessSupervisor.State.StartFailed);
var exitedSuccessfully = supervisor.WhenStateIs(ProcessSupervisor.State.ExitedSuccessfully);
var exitedWithError = supervisor.WhenStateIs(ProcessSupervisor.State.ExitedWithError);

supervisor.Start();

var result = await Task.WhenAny(startFailed, exitedSuccessfully, exitedWithError);
if(result == startFailed)
{
   Log.Error(supervisor.OnStartException, $"Process start failed {supervisor.OnStartException.Message}")
}
// etc.

CooperativeShutdown

Cooperative shutdown allows a "parent" process to instruct a "child" process to shut down. Different to SIGTERM and Process.Kill() in that it allows a child to acknowledge receipt of the request and shut down cleanly (and fast!). Combined with Supervisor.Stop() a parent can send the signal and then wait for ExitedSuccessfully.

The inter-process communication is done via named pipes where the pipe name is of the format LittleForker-{processId}

For a "child" process to be able receive co-operative shut down requests it uses CooperativeShutdown.Listen() to listen on a named pipe. Handling signals should be fast operations and are typically implemented by signalling to another mechanism to start cleanly shutting down:

var shutdown = new CancellationTokenSource();
using(await CooperativeShutdown.Listen(() => shutdown.Cancel())
{
   // rest of application checks shutdown token for co-operative
   // cancellation. See MSDN for details.
}

For a "parent" process to be able to signal:

await CooperativeShutdown.SignalExit(childProcessId);

This is used in ProcessSupervisor so if your parent process is using that, then you typically won't be using this explicitly.

Building

With docker which is same as CI:

  • Run build.cmd/build.sh to compile, run tests and build package.

Local build which requires .NET Core SDKs 2.1, 3.1 and .NET 5.0:

  • Run build-local.cmd/build-local.sh to compile, run tests and build package.

Credits & Feedback

@randompunter for feedback.

Hat tip to @markrendle for the project name.

littleforker's People

Contributors

damianh avatar dependabot-preview[bot] avatar pgermishuys avatar osoykan avatar erwinvandervalk avatar adamralph avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.