Compze.Threading 0.3.0-alpha

This is a prerelease version of Compze.Threading.
There is a newer prerelease version of this package available.
See the version list below for details.
dotnet add package Compze.Threading --version 0.3.0-alpha
                    
NuGet\Install-Package Compze.Threading -Version 0.3.0-alpha
                    
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="Compze.Threading" Version="0.3.0-alpha" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Compze.Threading" Version="0.3.0-alpha" />
                    
Directory.Packages.props
<PackageReference Include="Compze.Threading" />
                    
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 Compze.Threading --version 0.3.0-alpha
                    
#r "nuget: Compze.Threading, 0.3.0-alpha"
                    
#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 Compze.Threading@0.3.0-alpha
                    
#: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=Compze.Threading&version=0.3.0-alpha&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Compze.Threading&version=0.3.0-alpha&prerelease
                    
Install as a Cake Tool

Compze.Threading

Pit of success threading primitives. Impossible to forget to lock. Automatically resolves and diagnoses deadlocks, including both the involved stack traces in the exception.

The Problem

With the tools built into the BCL:

  • Nothing stops you from accessing shared state without locking.
  • Forget once and you have an intermittent bug is often incredibly hard to diagnose.
  • Deadlocks hang forever bringing production software to a grinding permantent halt.

Usage requires constant vigilance. Getting it disastrously wrong is all too easy.

Our Solutions

Automatic Deadlock Resolution with Dual Stack Traces

Every lock acquisition in all the abstractions shown below use a timeout internally. When deadlocks occur the timeout elapses and we capture both stack traces in the exception thrown: the thread that was blocked and the thread that was blocking it. No more guessing which thread held the lock.

💡 If the "winning" thread does not promptly release the lock after the deadlock is resolved its stacktrace will not occur in the exception, but both stack traces will be logged when it does release so the full diagnostic information you need will be there in the logs.

IThreadShared<T> Make Forgetting to Lock Impossible.

class MyThreadSafeClass
{
    readonly IThreadShared<PrivateImplementation> _inner = IThreadShared.New(new PrivateImplementation());

    void Add(string key, int value) => _inner.Locked(it => it.Add(key, value));
    int Get(string key) => _inner.Locked(it => it.Get(key));

    class PrivateImplementation{/**/}
}

💡 Note: There is no way to access the PrivateImplementation instance without acquiring the lock. This resolves whole categories of bugs in one fell swoop.

IAwaitableThreadShared<T> — Condition Waits with Read/Update Semantics

To be able to efficiently wait for shared state to reach a certain condition you need IAwaitableThreadShared instead:

class WorkTracker
{
    readonly IAwaitableThreadShared<PrivateImplementation> _inner = IAwaitableThreadShared.New(new PrivateImplementation());

    void Add(WorkItem item) => _inner.Update(it => it.Add(item));

    void Dispose()
    {
        _inner.UpdateWhen(it => it.IsEmpty, it => it.Dispose());
    }

    class PrivateImplementation{/**/}
}

💡 Note: Always use Update if you change state or are at all unsure. Waiting for conditions will never react to updates made inside Read

API Overview

Simple Locking — IThreadShared<T>

class MyService
{
    readonly IThreadShared<MyState> _state = IThreadShared.New(new MyState());

    public int GetValue() => _state.Locked(state => state.Value);
    public void SetValue(int value) => _state.Locked(state => state.Value = value);
}

One operation: Locked. The shared state is the lambda parameter. No way to misuse it.

Read/Update/Wait — IAwaitableThreadShared<T>

class WorkTracker
{
    readonly IAwaitableThreadShared<MyState> _state = IAwaitableThreadShared.WithDefaultTimeouts(new MyState());
    // or: IAwaitableThreadShared.New(new MyState(), LockTimeout.Seconds(5), WaitTimeout.Seconds(30));

    public int GetValue() => _state.Read(state => state.Value);                                  // read lock
    public void SetValue(int value) => _state.Update(state => state.Value = value);              // update lock + pulse waiters
    public int AwaitReady() => _state.ReadWhen(state => state.IsReady, state => state.Value);    // wait for condition, then read
    public void ProcessWhenReady() => _state.UpdateWhen(state => state.HasWork, state => state.Process()); // wait, then update
    public void AwaitDone() => _state.Await(state => state.IsDone);                              // block until condition is true
}

Lower-Level — IMonitor and IAwaitableMonitor

Wrap all logic in existing public methods inside calls to _monitor to quickly migrate existing classes:
class MyThreadSafe
{
    IMonitor _monitor = IMonitor.New();
    
    public void DoStuff() => _monitor.Locked(() => 
    {
        //implementation here
    });
}

class MyAwaitableThreadSafe
{
    IAwaitableMonitor _monitor = IAwaitableMonitor.New();

    public AType ReadAType() => _monitor.Read(() => _aField);
    public void UpdateSomething(AType value) => _monitor.Update(() => _aField = value);
    public AType AwaitValue() => _monitor.ReadWhen(() => _aField != null, () => _aField);

}
Advanced/rare/risky usages:
class RiskyExample
{
    readonly IAwaitableMonitor _monitor = IAwaitableMonitor.WithDefaultTimeout();

    public void ReadSomething()
    {
        ...
        using(_monitor.TakeReadLock()) { /* read */ }
        ...
    }

    public void UpdateSomething()
    {
        ...
        using(_monitor.TakeUpdateLock()) { /* update, notifies waiters */ }
        ...
    }

    public void WaitThenRead()
    {
        ...
        using(_monitor.TakeReadLockWhen(() => ready)) { /* waits for condition, then reads */ }
        ...
    }
}

💡 WARNING: Think carefully before doing this. Much of the problems with BCL primitives come right back. Profile before you assume that more granular locking is needed for performance. It is far more rare than you may think.

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 (21)

Showing the top 5 NuGet packages that depend on Compze.Threading:

Package Downloads
Compze.DependencyInjection

Package Description

Compze.Internals.SystemCE

For Compze internal use only. Do not take a direct dependency on this package.

Compze.Abstractions

Core abstractions for the Compze framework: entity IDs, message type contracts, time sources, serialization interfaces, and type mapping infrastructure.

Compze.Tessaging

Messaging infrastructure for the Compze framework including command, query, and event handling.

Compze.Sql.Common

For Compze internal use only. Do not take a direct dependency on this package

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
0.9.0-alpha 308 7/24/2026
0.8.0-alpha 247 7/15/2026
0.7.0-alpha 239 7/5/2026
0.6.0-alpha 105 7/4/2026
0.5.0-alpha 708 6/4/2026
0.3.0-alpha 91 3/4/2026