Skip to content

The shm: drive

The shm: drive

Values under names, shared by every process of the user and outliving the session, reached the way $env: is reached:

$shm:greeting = 'hello'          # this process
$shm:greeting                    # any other process of the user: hello
$shm:greeting = $null            # gone, for everyone

PowerShell resolves $drive:name through a provider’s content operations, so the syntax needs no language change and works in PowerShell 7 and in Windows PowerShell 5.1 alike. The module registers the drive when it imports, as shm, with provider SubEthaShm. Behind it is a SharedNamedValues store: a lock-free map from each name to a block in a shared arena, with an epoch table that keeps a value readable while a reader who found it is still reading.

Files

The default drive’s files are shm.map, shm.arena and shm.epochs in the user’s SubEtha directory, the one the wait calibration caches in: subetha in the per-user temporary directory on Windows, $XDG_RUNTIME_DIR/subetha or subetha-<uid> in the temporary directory on Unix. They are created by the first import that finds them absent and attached to by every later one, and they stay until removed, so a value written by a process that has exited is still there. The store holds 4,096 names; its arena is 256 MiB, in blocks of 64 bytes to 32 MiB; a value of up to 16 MiB fits; 256 readers may hold a value at once across every process.

New-PSDrive -Name work -PSProvider SubEthaShm -Root C:\work opens the shm files in another directory, creating them when absent, and $work:name reaches them.

Values

AssignedStored asComes back as
a boolean, an integer of any width, a single, a doubleits bytes behind one tag bytethe same type
a stringUTF-8a string
a byte[]the bytesa byte[]
a DateTimeits ticks and its kinda DateTime of the same kind, to the tick
anything else, arrays and hashtables includedthe CLIXML the remoting serializer writes at depth 2what remoting returns: exact for the primitives inside, a property bag with a Deserialized. type name for other objects

Assigning $null removes the name, as $env: does. A name the drive does not hold reads as $null. Assigning several objects at once, $shm:list = 1, 2, 3, stores them as one array.

Names

A name is one path segment, of any length. Names are compared without regard to case and listed as they were first written: after $shm:Greeting = 'hi', $shm:GREETING reads hi and Get-ChildItem shm: shows Greeting. Two different names whose 128-bit hashes collide are refused with an error naming both, never merged.

Items

The drive is a container of leaves, so the item cmdlets work on it:

CmdletEffect
Get-ChildItem shm:every name with its value, as SubEtha.ShmValue objects with Name and Value
Get-Item shm:nameone such object
Set-Item shm:name -Value v, New-Item shm:name -Value vthe same as $shm:name = v
Remove-Item shm:nameremoves the name; an error when the drive does not hold it
Clear-Item shm:name, Clear-Content shm:nameremoves the name, whether or not the drive holds it
Test-Path shm:namewhether the drive holds the name

Concurrent writers and readers

A write takes a fresh block, fills it, swaps its handle into the map and publishes it; the block the swap replaced is retired at the next epoch. Two processes assigning one name at once each publish a whole value and the map holds the later swap’s; a reader sees one whole value or the other, never a mixture, and a reader that found the earlier one keeps reading it until its read is done. A writer that dies part way leaves nothing a user must repair: the next write that finds the arena full walks it with the map as its root set and takes back what no name reaches.

Errors

ErrorWhen
LimitsExceededthe drive holds its 4,096 names already; the value and its name need more than 16 MiB; no block of the size is free even after collecting; the name is longer than 65,535 bytes
ResourceExistsanother name with the same hash holds the entry
ObjectNotFoundRemove-Item of a name the drive does not hold
OpenErrorthe user’s SubEtha directory cannot be created or belongs to another user

What a read costs

A $shm:name read goes through PowerShell’s provider path and the module’s bridge on every access, so it costs what a provider access costs, not what the store’s lookup costs. Measured by crates/subetha-pwrs/bench/ShmRead.ps1 on Windows 11 / Ryzen 9 7900X with 3.8 to 4.4 of 24 logical processors busy with other work, the median of five runs of 20,000 operations each, in microseconds per operation:

OperationPowerShell 7.6.6Windows PowerShell 5.1
a plain variable, read0.50.5
$env:name, read6.24.4
$shm:name, an integer, read30.638.4
$shm:name, a string, read33.638.5
$shm:name, an object of two properties, read38.546.8
a plain variable, written0.10.2
$env:name, written16.326.2
$shm:name, an integer, written171.6200.7

A read costs 30 to 47 microseconds, between 5 and 9 times an $env: read, and a write 170 to 210 microseconds: a write takes a block, fills it, swaps and publishes it and retires the block it replaced, behind the provider’s content-writer path. Read a value once into a local variable inside a loop rather than through $shm: on every iteration.