BigMachines 0.57.0

dotnet add package BigMachines --version 0.57.0
                    
NuGet\Install-Package BigMachines -Version 0.57.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="BigMachines" Version="0.57.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="BigMachines" Version="0.57.0" />
                    
Directory.Packages.props
<PackageReference Include="BigMachines" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add BigMachines --version 0.57.0
                    
#r "nuget: BigMachines, 0.57.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package BigMachines@0.57.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=BigMachines&version=0.57.0
                    
Install as a Cake Addin
#tool nuget:?package=BigMachines&version=0.57.0
                    
Install as a Cake Tool

BigMachines

NuGet Build and Test

BigMachines is a source-generated state-machine library for .NET. It provides typed machine controls, asynchronous commands, scheduled execution, lifecycle management, Tinyhand serialization, and optional CrystalData persistence.

Contents

Requirements

  • .NET 10 or later
  • C# 14 or later
  • Visual Studio 2026 or another build environment that supports the required .NET SDK and source generators

Installation

Install the package with the .NET CLI:

dotnet add package BigMachines

The package includes the BigMachines source generator.

Quick start

Define an empty partial root class, add the machines it owns, and mark each machine as partial.

using System;
using System.Threading.Tasks;
using Arc.Threading;
using BigMachines;

namespace QuickStart;

[BigMachineObject]
[AddMachine<CounterMachine>]
public partial class AppMachines;

[MachineObject]
public partial class CounterMachine : Machine<int>
{
    public CounterMachine()
    {
        this.DefaultTimeout = TimeSpan.FromSeconds(1);
        this.Lifespan = TimeSpan.FromSeconds(5);
    }

    public int Count { get; private set; }

    [StateMethod(0)]
    protected StateResult Initial(StateParameter parameter)
    {
        Console.WriteLine($"Machine {this.Identifier}: Initial");
        this.ChangeState(State.Counting);
        return StateResult.Continue;
    }

    [StateMethod]
    protected StateResult Counting(StateParameter parameter)
    {
        Console.WriteLine($"Machine {this.Identifier}: {this.Count++}");
        return StateResult.Continue;
    }

    [CommandMethod]
    protected CommandResult Print(string message)
    {
        Console.WriteLine(message);
        return CommandResult.Success;
    }

    protected override void OnTerminate()
    {
        this.BigMachine.ExecutionGroup.RequestTermination();
    }
}

public static class Program
{
    public static async Task Main()
    {
        var root = new ExecutionRoot();
        var machines = new AppMachines(root);
        machines.Start();

        var counter = machines.CounterMachine.GetOrCreate(42);
        await counter.Command.Print("Hello from BigMachines");
        await counter.RunAsync();

        await root.WaitForTermination();
    }
}

The generator adds the root constructor, typed controls, machine interface, state enum, and command proxy.

Core concepts

  • A big-machine root derives from BigMachineBase through generated code and owns the machine controls declared with AddMachine<TMachine> or discovered with BigMachineObject(Inclusive = true).
  • A machine derives from Machine or Machine<TIdentifier> and contains state and command methods.
  • A generated machine interface is the public handle used to inspect, run, pause, unpause, or terminate a machine.
  • A machine control creates, finds, enumerates, and schedules machine instances.
  • An ExecutionRoot owns the execution lifetime. Construct the generated root with it, call Start(), and request termination through the root or the generated root's ExecutionGroup.

Machine controls

MachineObjectAttribute.Control selects how instances are managed.

Control Purpose
Default Uses Single for Machine and Unordered for Machine<TIdentifier>.
Single Manages at most one instance of a machine type.
Unordered Manages multiple identified machines without ordering guarantees.
Sequential Queues identified machines in creation order. NumberOfTasks sets the number of dedicated workers.

Common control operations include:

var machine = machines.CounterMachine.GetOrCreate(42);

if (machines.CounterMachine.TryGet(42, out var existing))
{
    await existing.RunAsync();
}

foreach (var identifier in machines.CounterMachine.GetIdentifiers())
{
    Console.WriteLine(identifier);
}

CreateAlways terminates an existing matching instance before creating its replacement. TryCreate is available on sequential and manual controls when creation must fail instead of returning an existing machine.

Machines marked with MachineObject(Private = true) are not added to a root automatically. They can be managed through ManualControl or added explicitly when appropriate.

Execution and lifecycle

A machine can run manually, on a timer, or through a sequential control.

  • DefaultTimeout sets the periodic interval. TimeSpan.Zero disables interval execution.
  • SetTimeUntilRun changes the remaining delay.
  • SetNextRunTime schedules an absolute UTC execution time.
  • Lifespan terminates a machine after the remaining duration reaches zero.
  • TerminationTime terminates a machine at an absolute time.

Use the generated interface for runtime control:

await machine.RunAsync();
machine.PauseMachine();
machine.UnpauseMachine();
machine.SetNextRunTimeFromNow(TimeSpan.FromMinutes(1));
machine.TerminateMachine();

The lifecycle callbacks are invoked in this order for a newly created machine:

OnCreate(createParam) -> OnStart() -> OnTerminate()

OnCreate is not called after deserialization. OnStart is called after both creation and deserialization. OnTerminate runs while the machine semaphore is held.

States and commands

Mark state handlers with StateMethodAttribute. If a machine defines state handlers, state ID 0 is required and is the initial state. When an ID is omitted, the generator derives it from the method name.

[StateMethod(0)]
protected StateResult Initial(StateParameter parameter)
{
    this.ChangeState(State.Ready, rerun: true);
    return StateResult.Continue;
}

A method named <StateName>CanExit can reject leaving a state, and <StateName>CanEnter can reject entering one. Both methods return bool.

Mark command handlers with CommandMethodAttribute. The generator exposes them as asynchronous methods on machine.Command and converts thrown exceptions into CommandResult.Failure.

[CommandMethod]
protected CommandResult<string> Echo(string value)
    => new(value);

var result = await machine.Command.Echo("message");
if (result.Result == CommandResult.Success)
{
    Console.WriteLine(result.Response);
}

Commands acquire the machine semaphore by default. Set CommandMethod(WithLock = false) only when the handler is safe to run concurrently. All = true generates an extension that sends the command to every instance managed by the root.

Serialization and persistence

BigMachines integrates with Tinyhand and ValueLink. Apply TinyhandObjectAttribute to each concrete machine whose state must be serialized. Do not apply it only to the abstract Machine base classes.

using Tinyhand;

[TinyhandObject]
[MachineObject]
public partial class PersistentMachine : Machine<int>
{
    [Key(10)]
    public int Count { get; set; }

    [StateMethod(0)]
    protected StateResult Initial(StateParameter parameter)
        => StateResult.Continue;
}

var data = TinyhandSerializer.Serialize(machines);
var restored = TinyhandSerializer.Deserialize<AppMachines>(data);

The base machine uses reserved Tinyhand keys for runtime state. Use key 10 or greater for machine data, as shown in the repository examples. A machine without TinyhandObjectAttribute remains runtime-only. AddMachine(Volatile = true) excludes that control from root persistence.

BigMachines emits the closed formatter registrations required by Tinyhand 0.144 and NativeAOT. Closed generic machines should be listed explicitly with AddMachine<GenericMachine<ConcreteType>> so the generator can register their concrete types.

Enable NativeAOT in the application project, not in the analyzer project:

<PropertyGroup>
  <PublishAot>true</PublishAot>
  <RuntimeIdentifier>win-x64</RuntimeIdentifier>
</PropertyGroup>

The repository's NativeAotTest project publishes with trimming warnings treated as errors and executes Tinyhand round trips for single, unordered, and sequential controls.

For file persistence, register the generated root with CrystalData:

dotnet add package CrystalData
var builder = new CrystalUnit.Builder()
    .ConfigureCrystal(context =>
    {
        context.SetJournal(
            new SimpleJournalConfiguration(
                new LocalDirectoryConfiguration("Data/Journal")));

        context.AddCrystal<AppMachines>(new()
        {
            FileConfiguration = new LocalFileConfiguration("Data/AppMachines.tinyhand"),
            SaveFormat = SaveFormat.Utf8,
            NumberOfFileHistories = 3,
        });
    });

Use CrystalData 0.47.0 or later with Tinyhand 0.144 to avoid references to the removed dynamic formatter-registration API.

Dependency injection

Set MachineObject(UseServiceProvider = true) when a machine requires constructor injection. Register the machine and its dependencies, then assign the built provider to TinyhandSerializer.ServiceProvider before machines are created or deserialized.

var services = new ServiceCollection()
    .AddSingleton<Clock>()
    .AddTransient<ServiceMachine>()
    .BuildServiceProvider();

TinyhandSerializer.ServiceProvider = services;
[MachineObject(UseServiceProvider = true)]
public partial class ServiceMachine : Machine<int>
{
    public ServiceMachine(Clock clock)
    {
        this.Clock = clock;
    }

    private Clock Clock { get; }
}

Pass per-instance data through GetOrCreate(identifier, createParam) and receive it in OnCreate. Constructor dependencies and creation parameters serve different purposes.

Exceptions

Exceptions thrown by generated state or command dispatch are wrapped in BigMachineException and queued on the root. The default handler writes them to the console. Install a custom handler through IBigMachine when the application needs logging or another policy:

((IBigMachine)machines).SetExceptionHandler(exception =>
{
    Console.Error.WriteLine(exception);
});

Command callers receive CommandResult.Failure when a command handler throws. A command sent to a terminated machine returns CommandResult.Terminated.

Generic, external, and private machines

Constructed generic machines and machines from referenced assemblies can be added explicitly:

[BigMachineObject]
[AddMachine<GenericMachine<string>>]
[AddMachine<ExternalLibrary.WorkerMachine>]
public partial class AppMachines;

BigMachineObject(Inclusive = true) includes eligible non-private machines discovered in the current assembly. Explicit AddMachine<TMachine> declarations remain the clearest choice for constructed generic and external types.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (2)

Showing the top 2 NuGet packages that depend on BigMachines:

Package Downloads
Netsphere

Netsphere is a network protocol for Lp.

Lp

Lp is an experimental program that create value.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.57.0 0 9/5/2026
0.56.1 59 9/1/2026
0.56.0 179 8/20/2026
0.55.2 179 5/14/2026
0.55.1 200 5/11/2026
0.55.0 215 5/9/2026
0.54.2 191 4/23/2026
0.54.1 139 4/23/2026
0.53.5 166 4/5/2026
0.53.4 162 3/31/2026
0.53.3 288 3/28/2026
0.53.2 184 3/16/2026
0.53.0 208 3/11/2026
0.52.2 425 1/30/2026
0.52.1 617 11/19/2025
0.52.0 303 11/15/2025
0.51.0 286 10/29/2025
0.50.2 287 10/29/2025
0.50.1 289 10/28/2025
0.50.0 279 10/28/2025
Loading failed