STMSharp is a .NET library for lock-free synchronization using Software Transactional Memory (STM), enabling atomic transactions and efficient multi-threading
$ dotnet add package STMSharpSTMSharp brings Software Transactional Memory to .NET: write your concurrent logic as atomic transactions over shared variables, with optimistic snapshots and a lock-free CAS commit that prevents lost updates under contention.
Exponential, ExponentialWithJitter (default), Linear, Constant.Transaction<T> via StmDiagnostics.Software Transactional Memory (STM) is a concurrency control mechanism that simplifies writing concurrent programs by providing an abstraction similar to database transactions. STM allows developers to work with shared memory without the need for explicit locks, reducing the complexity of concurrent programming.
In STMSharp, STM is implemented using transactions that read from and write to STM variables. Transactions can be automatically retried using a backoff strategy to handle conflicts, making it easier to work with shared data in concurrent environments.
STMVariable<T> stores a value and a monotonic version (long)._reads (cache, including read-your-own-writes),_writes (buffered updates),_snapshotVersions (immutable version per first observation).Commit protocol (lock-free):
even → odd (based on the immutable snapshot) via TryAcquireForWrite.odd → even (commit complete).This ensures serializability and prevents lost updates without runtime locks.
STMVariable<T>
Encapsulates a shared value and its version. Supports:
ReadWithVersion() / Version,Write(T) that are protocol-compatible (they reserve even → odd and release odd → even), see caveats below.Transaction<T>
Internal transactional context used by STMEngine. Tracks:
_reads),_writes),_snapshotVersions),
and implements the optimistic commit protocol.STMEngine
Public façade exposing Atomic<T>(...) overloads, with:
StmOptions.StmOptions
Immutable configuration for transactional execution:
MaxAttemptsBaseDelay, MaxDelayBackoffTypeTransactionMode (ReadWrite, ReadOnly)StmDiagnostics
Public diagnostics helper:
GetConflictCount<T>()GetRetryCount<T>()Reset<T>()Counters are per closed generic type (Transaction<int> vs Transaction<string>).
STMSharp uses an even/odd version scheme and Compare-And-Exchange (CAS) to coordinate writers:
// success only if current == snapshotVersion (even)
// sets version to snapshotVersion + 1 (odd), meaning "reserved"
Interlocked.CompareExchange(ref version, snapshotVersion + 1, snapshotVersion);
currentVersion == snapshotVersion and (currentVersion & 1) == 0.Interlocked.Increment(ref version) to turn odd → even (commit complete).ReleaseAfterAbort() also increments once to revert odd → even.(value, version) pair used both for validation and reservation.Basic example
// Initialize a shared STM variable
var sharedVar = new STMVariable<int>(0);
// Perform an atomic transaction to increment the value
await STMEngine.Atomic<int>(tx =>
{
var value = tx.Read(sharedVar);
tx.Write(sharedVar, value + 1);
});
// Perform another atomic transaction
await STMEngine.Atomic<int>(tx =>
{
var value = tx.Read(sharedVar);
tx.Write(sharedVar, value + 1);
});
Using StmOptions and read-only mode
var sharedVar = new STMVariable<int>(0);
// Read-only transaction (throws if Write is called)
var readOnlyOptions = StmOptions.ReadOnly;
await STMEngine.Atomic<int>(async tx =>
{
var value = tx.Read(sharedVar);
Console.WriteLine($"Current value: {value}");
// tx.Write(sharedVar, 123); // would throw InvalidOperationException
}, readOnlyOptions);
// Custom retry/backoff policy
var customOptions = new StmOptions(
MaxAttempts: 5,
BaseDelay: TimeSpan.FromMilliseconds(50),
MaxDelay: TimeSpan.FromMilliseconds(1000),
Strategy: BackoffType.ExponentialWithJitter,
Mode: TransactionMode.ReadWrite
);
await STMEngine.Atomic<int>(async tx =>
{
var value = tx.Read(sharedVar);
tx.Write(sharedVar, value + 1);
}, customOptions);
Diagnostics
// Reset counters for int-transactions
StmDiagnostics.Reset<int>();
// Run some atomic operations...
var conflicts = StmDiagnostics.GetConflictCount<int>();
var retries = StmDiagnostics.GetRetryCount<int>();
Console.WriteLine($"Conflicts: {conflicts}, Retries: {retries}");
Detailed performance measurements were conducted using BenchmarkDotNet to compare variable access and atomic operations under various backoff strategies.
This project includes a benchmarking application designed to test and simulate the behavior of the STMSharp library under varying conditions. The benchmark is built to analyze the efficiency and robustness of the STM mechanism. The benchmark parameters are configurable through a JSON file named appsettings.json. This allows centralized and flexible management of the values used for testing.
The goal of the benchmark is to measure the performance of the STMSharp library based on:
At the end of execution, the benchmark provides several statistics:
Thank you for considering to help out with the source code! If you'd like to contribute, please fork, fix, commit and send a pull request for the maintainers to review and merge into the main code base.
STMSharp source code is available under MIT License, see license in the source.
Please contact at francesco.delre[at]protonmail.com for any details.