Lyo.Lock 1.0.1

dotnet add package Lyo.Lock --version 1.0.1
                    
NuGet\Install-Package Lyo.Lock -Version 1.0.1
                    
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="Lyo.Lock" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Lyo.Lock" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Lyo.Lock" />
                    
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 Lyo.Lock --version 1.0.1
                    
#r "nuget: Lyo.Lock, 1.0.1"
                    
#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 Lyo.Lock@1.0.1
                    
#: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=Lyo.Lock&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Lyo.Lock&version=1.0.1
                    
Install as a Cake Tool

Lyo.Lock

Key-based exclusive locks and keyed semaphores (bounded concurrency per key) with a small abstraction layer and in-memory implementations for a single process.

Features

  • ILockService — acquire/release by string key, or ExecuteWithLockAsync helpers that throw TimeoutException if the lock is not obtained.
  • LocalLockService — one holder per normalized key using SemaphoreSlim.
  • IKeyedSemaphoreService — up to maxConcurrency simultaneous permit holders per key.
  • LocalKeyedSemaphoreService — per-key SemaphoreSlim with ref-counted cleanup when idle.
  • Key normalization — by default keys are compared case-insensitively (ToLowerInvariant); optional skip when keys are already normalized.
  • DIAddLocalLock, AddLocalLockFromConfiguration, AddLocalKeyedSemaphore, AddLocalKeyedSemaphoreFromConfiguration.
  • Metrics — optional timers/counters via Lyo.Metrics when EnableMetrics is true and IMetrics is registered.

Examples

Quick start

using Lyo.Lock;
using Microsoft.Extensions.DependencyInjection;

services.AddLocalLock();
services.AddLocalKeyedSemaphore();

// Optional: bind appsettings → LockOptions / KeyedSemaphoreOptions
services.AddLocalLockFromConfiguration(configuration);
services.AddLocalKeyedSemaphoreFromConfiguration(configuration);

Quick start (2)

// Exclusive lock: null if timeout
var handle = await lockService.AcquireAsync("order:123", timeout: TimeSpan.FromSeconds(5));
if (handle is not null)
{
    await using (handle)
        await ProcessOrderAsync();
}

// Throws TimeoutException if not acquired
await lockService.ExecuteWithLockAsync("order:123", async ct => await ProcessOrderAsync(ct));

// Up to 3 concurrent operations for the same key (same process only)
await semaphoreService.ExecuteAsync("export:tenant-1", 3, async ct => await RunExportAsync(ct));

KeyedSemaphoreOptions (KeyedSemaphoreOptions section)

{
  "LockOptions": {
    "DefaultAcquireTimeout": "00:00:30",
    "DefaultLockDuration": "00:01:00",
    "KeyPrefix": "lyo:lock:",
    "EnableMetrics": false,
    "SkipKeyNormalization": false
  },
  "KeyedSemaphoreOptions": {
    "DefaultAcquireTimeout": "00:00:30",
    "SkipKeyNormalization": false,
    "EnableMetrics": false
  }
}

Benchmarks

  • Portfolio suite: lock

When to use what

Primitive Type Scope Typical use
ILockService / LocalLockService Mutex per key One process Guard mutations to one aggregate, avoid duplicate work, serialize handlers per entity ID
IKeyedSemaphoreService / LocalKeyedSemaphoreService Counting semaphore per key One process Cap concurrent exports/API calls/backfills per tenant or resource key without global rate limits

For multiple servers or processes, register a distributed ILockService (see Lyo.Lock.Redis). Keyed semaphores in this package remain local only.

Quick start

Inject ILockService and/or IKeyedSemaphoreService:

Rules for keyed semaphores

  • Use a stable maxConcurrency for a given key while any permit is held or waiters exist. If you pass a different maxConcurrency for an active key, LocalKeyedSemaphoreService throws (InvalidOperationException) instead of undefined behavior.
  • Cancellation tokens on AcquireAsync / ExecuteAsync are honored while waiting.

LockOptions (LockOptions section)

Property Default Description
DefaultAcquireTimeout 30s Max wait for AcquireAsync / ExecuteWithLockAsync.
DefaultLockDuration 60s Used by distributed locks (Redis TTL). Ignored by LocalLockService.
KeyPrefix lyo:lock: Prefix for Redis keys; harmless for local-only usage.
SkipKeyNormalization false When true, keys are not lowercased (caller must ensure consistent casing).
EnableMetrics false Record lock timings/counters when IMetrics is available.

KeyedSemaphoreOptions (KeyedSemaphoreOptions section)

Property Default Description
DefaultAcquireTimeout 30s Max wait for a permit.
SkipKeyNormalization false Same semantics as lock options.
EnableMetrics false Record semaphore timings/counters when IMetrics is available.

Example appsettings.json:

Metrics (Constants)

When metrics are enabled and IMetrics is registered, names match Lyo.Lock.Constants:

Locks (Constants.Metrics)

Name Role
lock.acquire.duration Wait time for acquisition
lock.acquire.success / lock.acquire.failure Counter
lock.release.duration Release timing
lock.execute.duration Wall time for ExecuteWithLockAsync

Semaphores (Constants.SemaphoreMetrics)

Name Role
semaphore.acquire.duration Wait time for a permit
semaphore.acquire.success / semaphore.acquire.failure Counter
semaphore.release.duration Release timing
semaphore.execute.duration Wall time for ExecuteAsync

Tag dimension: key — logical key string as passed by the caller (see XML docs on Constants).

API summary — ILockService

  • AcquireAsync — returns ILockHandle? (null on timeout).
  • ExecuteWithLockAsync / ExecuteWithLockAsync<T> — acquire, run delegate, release; throw TimeoutException if not acquired.

API summary — ILockHandle / IPermitHandle

  • ReleaseAsync — idempotent after first release.
  • Dispose / DisposeAsync — release (sync dispose may block briefly on internal ReleaseAsync).

API summary — IKeyedSemaphoreService

  • AcquireAsync — returns IPermitHandle? on timeout.
  • ExecuteAsync / ExecuteAsync<T> — throw TimeoutException if no permit.

Dependencies

Generated from ProjectReference / PackageReference (same model as docs/Lyo.ProjectGraph.html).

  • Lyo.Exceptions — (direct, lyo)
  • Lyo.Metrics — (direct, lyo)
  • Microsoft.Extensions.Configuration.Binder 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.DependencyInjection.Abstractions 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Logging.Abstractions 10.0.5 — (direct, microsoft)
  • Microsoft.Extensions.Options.ConfigurationExtensions 10.0.5 — (transitive, microsoft)
Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  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. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net461 was computed.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (3)

Showing the top 3 NuGet packages that depend on Lyo.Lock:

Package Downloads
Lyo.FileMetadataStore.Postgres

PostgreSQL implementation of the Lyo FileMetadataStore service using Entity Framework Core.

Lyo.Lock.Redis

Redis-based distributed lock implementation for Lyo.Lock. Uses StackExchange.Redis for multi-instance coordination.

Lyo.FileMetadataStore.Sqlite

SQLite implementation of the Lyo FileMetadataStore service using Entity Framework Core.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 0 8/18/2026
1.0.0 66 8/16/2026