🚨 Oracle not porting Rdb to x86 and EOL on Itanium 12/2027 🚨
Technical Capabilities
Having trouble finding what you need?
Get in touch with us, so we can answer your specific questions directly.
Get In Touch

VX/LCKMGR: OpenVMS Lock Manager

Overview

VX/LCKMGR is Sector7's reimplementation of the OpenVMS lock manager for Linux: the SYS$ENQ, SYS$DEQ, and SYS$GETLKI system-service family together with the arbitration daemon behind them. Migrated OpenVMS applications keep their locking logic unchanged. They call the same SYS$ services, and a local lock-manager daemon arbitrates every request and delivers grants and blocking notifications through ASTs, exactly as the VMS lock manager does.

It ships as a single-node lock manager. One server process owns the resource and lock databases, and client processes reach it over a System V IPC message queue. All six OpenVMS lock modes, the full grant and conversion queues, 16-byte and 64-byte lock value blocks, resource domains, and an audit log with replay are supported. Cluster-wide lock mastering is out of scope for the current build.

<!-- Webflow embed (min). Source: lckmgr-page-technical/lckmgr-tech-body-embed-dark.html → python3 website/embeds/minify_webflow_embed.py … --> <div class="s7-tech-embed" data-vxlckmgrd-body-embed="1" data-technical-mega-embed="1" lang="en"><h2 id="architecture">Architecture and components</h2><p> VX/LCKMGR is a client/server pair, not a library the application links its locking logic into. The application calls the <code>SYS$</code> wrappers on the client side; those marshal a request packet onto the daemon's well-known IPC queue; the daemon owns the resource and lock databases, arbitrates the request, and calls back asynchronously through ASTs delivered as reply messages. The table maps each OpenVMS lock-manager concept to its VX/LCKMGR counterpart. </p><div class="table-wrapper"><table><thead><tr><th>OpenVMS concept</th><th>VX/LCKMGR component</th><th>Role</th><th>Status</th></tr></thead><tbody><tr><td>Lock manager (LOCKMGR)</td><td><code>lckmgr</code> daemon</td><td>The arbitration daemon: owns the resource and lock databases and fires grant and blocking ASTs.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$ENQ</code> / <code>SYS$DEQ</code> / <code>SYS$GETLKI</code></td><td>Client library wrappers</td><td>Marshal request packets and run a per-process AST-dispatch thread that invokes the caller's completion and blocking ASTs.</td><td><span class="ac-live">live</span></td></tr><tr><td>SDA <code>SHOW LOCK</code>, operator control</td><td><code>lckctl</code></td><td>Start, stop, statistics, status, and queue maintenance for the daemon.</td><td><span class="ac-live">live</span></td></tr><tr><td>(no VMS analog)</td><td><code>AuditHistory</code></td><td>Generates a post-run report over a recorded audit log.</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><div class="callout"><strong>One local daemon, no cluster transport.</strong> The resource master is always the local host and there is no cluster interconnect wired into the shipping build. Clients and the daemon communicate over a System V IPC message queue on the same node. </div><h6>In-memory data model</h6><p> The daemon keeps one <strong>Lock Block (LKB)</strong> per outstanding request and one <strong>Resource Block (RSB)</strong> per named resource. Each RSB holds the three queues that drive arbitration: the grant queue, the conversion queue, and the wait queue. A <strong>Lock Holder</strong> record tracks each client session, and a sparse lock table indexed by a 24-bit slot plus an 8-bit generation counter turns a lock id back into its LKB. Named resources live in a <code>ResourceDomain</code> hierarchy so unrelated applications can share the daemon without colliding. </p><h6>Request lifecycle</h6><p>Every lock operation follows the same round trip between the client wrappers and the daemon:</p><style>.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-diagram {margin: 1.5rem 0;padding: 16px;border: 1px solid var(--s7t-border);border-radius: 10px;background: rgba(255, 255, 255, 0.01);}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-peers,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-clients {display: grid;gap: 10px;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-peers {grid-template-columns: repeat(2, minmax(0, 1fr));}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-clients {grid-template-columns: repeat(3, minmax(0, 1fr));}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-card,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-hub {min-width: 0;padding: 14px 16px;border-radius: 8px;background: var(--s7t-surface);}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-daemon {border: 1px solid var(--s7t-note-bd);background: var(--s7t-note-bg);}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-card strong,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-card span,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-hub strong,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-hub span {display: block;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-card strong,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-hub strong {margin-bottom: 4px;color: var(--s7t-text);}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-card span,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-hub span {color: var(--s7t-muted);font-size: 0.9em;line-height: 1.45;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-label,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-direction {font-family: var(--s7t-mono);color: var(--s7t-accent);font-size: 0.76em;font-weight: 700;letter-spacing: 0.08em;text-transform: uppercase;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-transfers {display: grid;gap: 8px;margin-top: 10px;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-transfer {display: grid;grid-template-columns: minmax(100px, 0.35fr) minmax(0, 1fr);gap: 12px;align-items: center;padding: 11px 14px;border-radius: 6px;background: var(--s7t-surface2);}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-transfer span:last-child {color: var(--s7t-muted);font-size: 0.9em;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-converge {display: grid;gap: 10px;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-hub {text-align: center;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-queue {border-top: 3px solid var(--s7t-accent);background: var(--s7t-surface2);}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-converge-note {text-align: center;font-family: var(--s7t-mono);color: var(--s7t-accent);font-size: 0.8em;font-weight: 700;}@media (max-width: 700px) {.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-peers,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-clients,.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-transfer {grid-template-columns: 1fr;}.s7-tech-embed[data-vxlckmgrd-body-embed] .ipc-transfer {gap: 4px;}}</style><div class="ipc-diagram" role="group" aria-label="VX/LCKMGR request and AST reply round trip"><div class="ipc-peers"><div class="ipc-card"><span class="ipc-label">Application process</span><strong><code>SYS$ENQ</code>, <code>SYS$DEQ</code>, and <code>SYS$GETLKI</code> wrappers</strong><span>The client marshals requests and its AST-dispatch thread receives asynchronous replies.</span></div><div class="ipc-card ipc-daemon"><span class="ipc-label">lckmgr daemon</span><strong>Resource and lock databases</strong><span>The daemon arbitrates the grant, conversion, and wait queues.</span></div></div><div class="ipc-transfers"><div class="ipc-transfer"><span class="ipc-direction">Client to daemon</span><span>Request packet travels over the System V IPC message queue.</span></div><div class="ipc-transfer"><span class="ipc-direction">Daemon to client</span><span>Grant or blocking AST returns as a reply message to the AST-dispatch thread.</span></div></div></div><p> A synchronous grant can be collapsed into the acknowledgement packet, so a lock that is immediately grantable returns without a separate AST round trip. A queued lock is granted later, when the resource frees up, by a reply message that the client's AST-dispatch thread turns into the caller's completion AST. </p><h2 id="lock-modes">Lock modes and compatibility</h2><p> VX/LCKMGR implements all six OpenVMS lock modes with the same symbol values and the same grant rules as the VMS lock manager. Modes range from the null placeholder used during conversions up to the exclusive lock that excludes every other holder. </p><div class="table-wrapper"><table><thead><tr><th>Symbol</th><th>Value</th><th>Abbr.</th><th>Meaning</th></tr></thead><tbody><tr><td><code>LCK$K_NLMODE</code></td><td>0</td><td>NL</td><td>Null: grants no access. Used as an interest marker or a placeholder for a lock conversion.</td></tr><tr><td><code>LCK$K_CRMODE</code></td><td>1</td><td>CR</td><td>Concurrent read: shared read, other readers and writers allowed.</td></tr><tr><td><code>LCK$K_CWMODE</code></td><td>2</td><td>CW</td><td>Concurrent write: shared, unprotected write.</td></tr><tr><td><code>LCK$K_PRMODE</code></td><td>3</td><td>PR</td><td>Protected read: the traditional share lock, no writers.</td></tr><tr><td><code>LCK$K_PWMODE</code></td><td>4</td><td>PW</td><td>Protected write: the traditional update lock, shares with CR.</td></tr><tr><td><code>LCK$K_EXMODE</code></td><td>5</td><td>EX</td><td>Exclusive: excludes every mode except NL.</td></tr></tbody></table></div><h6>Compatibility matrix</h6><p> A new request is granted immediately only if its mode is compatible with every lock already granted on the resource (and no conversion or wait is queued ahead of it). Rows are the mode of a currently granted lock; columns are the mode of the new request. </p><div class="table-wrapper"><table><thead><tr><th>Granted \ Requested</th><th>NL</th><th>CR</th><th>CW</th><th>PR</th><th>PW</th><th>EX</th></tr></thead><tbody><tr><td><strong>NL</strong></td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td></tr><tr><td><strong>CR</strong></td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>Yes</td><td>No</td></tr><tr><td><strong>CW</strong></td><td>Yes</td><td>Yes</td><td>Yes</td><td>No</td><td>No</td><td>No</td></tr><tr><td><strong>PR</strong></td><td>Yes</td><td>Yes</td><td>No</td><td>Yes</td><td>No</td><td>No</td></tr><tr><td><strong>PW</strong></td><td>Yes</td><td>Yes</td><td>No</td><td>No</td><td>No</td><td>No</td></tr><tr><td><strong>EX</strong></td><td>Yes</td><td>No</td><td>No</td><td>No</td><td>No</td><td>No</td></tr></tbody></table></div><div class="callout"><strong>Reading the grid.</strong> NL is compatible with everything, which is why it is safe as a conversion placeholder. EX is compatible only with NL. CR is the most permissive real access mode: it coexists with every mode except EX. </div><h6>Grant and conversion rules</h6><p> A request on a new resource is granted at once. On an existing resource it is granted only when the conversion and wait queues are empty and the mode is compatible with all granted locks; an NL request with the expedite option can jump the wait queue. Otherwise the request either returns <code>SS$_NOTQUEUED</code> when the no-queue option is set, or is appended to the wait queue. Conversions to a less restrictive mode are granted synchronously; conversions to a more restrictive mode are granted synchronously when compatible, and otherwise join the conversion queue while the lock stays granted at its current mode. When a lock is released or converted down, the conversion queue is re-evaluated up to the first incompatible entry, and the wait queue is evaluated once the conversion queue empties. </p><h2 id="request-flags">Request flags</h2><p> Lock requests carry the same option flags as OpenVMS. The header defines them in the mask form (<code>LCK$M_*</code>); older application code often spells the same options in the bit-position form (<code>LCK$V_*</code>). Both refer to the identical OpenVMS options. Several bit positions are deliberately overloaded by call context, exactly as on VMS: the value is the same, but its meaning differs between an enqueue (<code>SYS$ENQ</code>) and a dequeue (<code>SYS$DEQ</code>). </p><div class="table-wrapper"><table><thead><tr><th>Flag (mask form)</th><th>Hex</th><th>Meaning</th></tr></thead><tbody><tr><td><code>LCK$M_VALBLK</code></td><td><code>0x00000001</code></td><td>Read or write the lock value block at grant time.</td></tr><tr><td><code>LCK$M_DEQALL</code></td><td><code>0x00000001</code></td><td>On dequeue: release all locks held by the caller.</td></tr><tr><td><code>LCK$M_CONVERT</code></td><td><code>0x00000002</code></td><td>The request converts the mode of an existing lock id.</td></tr><tr><td><code>LCK$M_CANCEL</code></td><td><code>0x00000002</code></td><td>On dequeue: cancel a pending conversion.</td></tr><tr><td><code>LCK$M_NOQUEUE</code></td><td><code>0x00000004</code></td><td>Do not queue if not immediately grantable; return <code>SS$_NOTQUEUED</code>.</td></tr><tr><td><code>LCK$M_SYNCSTS</code></td><td><code>0x00000008</code></td><td>Return <code>SS$_SYNCH</code> on a synchronous grant and skip the completion AST.</td></tr><tr><td><code>LCK$M_SYSTEM</code></td><td><code>0x00000010</code></td><td>System-wide lock (resource group 0).</td></tr><tr><td><code>LCK$M_NOQUOTA</code></td><td><code>0x00000020</code></td><td>Bypass quota checks.</td></tr><tr><td><code>LCK$M_EXPEDITE</code></td><td><code>0x00000800</code></td><td>An NL-mode request jumps the wait queue.</td></tr><tr><td><code>LCK$M_XVALBLK</code></td><td><code>0x00010000</code></td><td>Request the 64-byte extended lock value block.</td></tr><tr><td><code>LCK$M_AUDITED</code></td><td><code>0x00080000</code></td><td><strong>Sector7 extension</strong>: mark the request for audit tracing.</td></tr></tbody></table></div><div class="callout"><strong>Overloaded bits.</strong> <code>LCK$M_CONVERT</code>, <code>LCK$M_CANCEL</code>, and <code>LCK$M_DEQALL</code> share bit values because VMS assigns them to the same bit in mutually exclusive service contexts. VX/LCKMGR preserves that encoding so existing code that sets these options keeps working unchanged. </div><h2 id="client-api">Client API: the SYS$ routines</h2><p> Applications call the same system services as on OpenVMS. Each routine has an asynchronous form and a synchronous form that blocks until the request completes. The synchronous forms are the ones that end in W. </p><div class="table-wrapper"><table><thead><tr><th>Routine</th><th>Purpose</th><th>Status</th></tr></thead><tbody><tr><td><code>SYS$ENQ</code></td><td>Asynchronous enqueue or convert; returns a request acknowledgement, with the grant delivered by AST.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$ENQW</code></td><td>Synchronous enqueue: blocks until the lock is granted or <code>SS$_SYNCH</code> is returned.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$DEQ</code></td><td>Asynchronous dequeue by lock id, with optional value block and flags.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$DEQW</code></td><td>Synchronous dequeue.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$GETLKI</code></td><td>Query lock information by lock id or wildcard, through an item list.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$GETLKIW</code></td><td>Synchronous GETLKI.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$SET_RESOURCE_DOMAIN</code></td><td>Join or leave a resource domain.</td><td><span class="ac-live">live</span></td></tr><tr><td><code>SYS$RUNDOWN</code></td><td>Release the locks held by an image at exit.</td><td><span class="ac-live">live</span></td></tr></tbody></table></div><h6>Example: enqueue, convert up, release</h6><p> This is the common lock lifecycle for a shared resource. The example takes a system-wide lock on a banking account resource at NL mode, converts it up to EX to make an exclusive change, then releases it and writes the lock value block back. It checks both the routine return code and the lock status block after each step. </p> <pre><span class="cmt">/* new NL-mode lock on a system-wide resource (async request, then wait) */</span> rc = <span class="kw">sys$enq</span>(efn, <span class="kw">LCK$K_NLMODE</span>, &amp;lksb, <span class="kw">LCK$M_SYSTEM</span>, &amp;resnam, 0, ast_granted, ctx, ast_blocked, <span class="kw">PSL$C_USER</span>, 0); rc = <span class="kw">sys$waitfr</span>(efn); <span class="kw">if</span> (!(rc &amp; 1)) { request_error++; } <span class="kw">if</span> (!(lksb.status &amp; 1)) { grant_error++; } <span class="cmt">/* not granted */</span> <span class="cmt">/* convert the same lock up to EX; LCK$M_CONVERT names the held lock id */</span> rc = <span class="kw">sys$enqw</span>(efn, <span class="kw">LCK$K_EXMODE</span>, &amp;lksb, <span class="kw">LCK$M_SYSTEM</span> | <span class="kw">LCK$M_VALBLK</span> | <span class="kw">LCK$M_CONVERT</span>, 0, 0, 0, 0, 0, <span class="kw">PSL$C_USER</span>, 0); <span class="cmt">/* release; the value block is written back from lksb.valblk */</span> rc = <span class="kw">sys$deq</span>(lksb.lkid, &amp;lksb.valblk, <span class="kw">PSL$C_USER</span>, 0);</pre> <p> The completion AST and the blocking AST are ordinary functions passed by address. The client-side AST-dispatch thread invokes them when the daemon publishes the matching reply on the process's AST channel, so the asynchronous behaviour of the VMS lock manager is preserved without the application changing how it waits. </p><h2 id="getlki">Lock information and status codes</h2><p><code>SYS$GETLKI</code> returns detailed information about a lock through an item list: the caller supplies item codes and receives the matching values. It is important to keep three things separate, because migrated code sometimes conflates them: the lock-state constants, the GETLKI item codes, and the completion status codes a request returns. </p><h6>Lock-state constants</h6><p>These describe where a lock currently sits, and are returned for the state item code.</p><div class="table-wrapper"><table><thead><tr><th>Constant</th><th>Value</th><th>Meaning</th></tr></thead><tbody><tr><td><code>LKI$C_WAITING</code></td><td>-1</td><td>Lock is on the wait queue.</td></tr><tr><td><code>LKI$C_CONVERT</code></td><td>0</td><td>Lock is on the conversion queue.</td></tr><tr><td><code>LKI$C_GRANTED</code></td><td>1</td><td>Lock is granted.</td></tr></tbody></table></div><h6>GETLKI item codes</h6><p> The item codes select which field to return. Per-lock identity and state codes fall in the 256 to 264 range; namespace, resource, value block, and counter fields fall in the 512 to 524 range. </p><div class="table-wrapper"><table><thead><tr><th>Item code</th><th>Value</th><th>Item code</th><th>Value</th></tr></thead><tbody><tr><td><code>LKI$_PID</code></td><td>256</td><td><code>LKI$_LKID</code></td><td>263</td></tr><tr><td><code>LKI$_STATE</code></td><td>257</td><td><code>LKI$_CSID</code></td><td>264</td></tr><tr><td><code>LKI$_PARENT</code></td><td>258</td><td><code>LKI$_NAMSPACE</code></td><td>512</td></tr><tr><td><code>LKI$_LCKREFCNT</code></td><td>259</td><td><code>LKI$_RESNAM</code></td><td>513</td></tr><tr><td><code>LKI$_LOCKID</code></td><td>260</td><td><code>LKI$_VALBLK</code></td><td>515</td></tr><tr><td><code>LKI$_REMLKID</code></td><td>261</td><td><code>LKI$_LCKCOUNT</code></td><td>517</td></tr><tr><td><code>LKI$_MSTLKID</code></td><td>262</td><td><code>LKI$_BLOCKEDBY</code></td><td>518</td></tr><tr><td><code>LKI$_BLOCKING</code></td><td>519</td><td><code>LKI$_WAITCOUNT</code></td><td>522</td></tr><tr><td><code>LKI$_LOCKS</code></td><td>520</td><td><code>LKI$_GRANTCOUNT</code></td><td>523</td></tr><tr><td><code>LKI$_CVTCOUNT</code></td><td>521</td><td><code>LKI$_MSTCSID</code></td><td>524</td></tr></tbody></table></div><div class="callout"><strong>Single-node degeneracy.</strong> Because there is no cluster mastering, the cluster-relative fields collapse: the master lock id equals the local lock id (<code>LKI$_MSTLKID</code> equals <code>LKI$_LKID</code>) and the cluster system ids are always zero (<code>LKI$_CSID</code> equals <code>LKI$_MSTCSID</code> equals 0). The node is always the local host. </div><h6>Return and completion status codes</h6><p> These are the status values a lock request returns, in the routine return code or in the lock status block. They are distinct from the GETLKI item codes above. </p><div class="table-wrapper"><table><thead><tr><th>Status</th><th>Meaning</th></tr></thead><tbody><tr><td><code>SS$_NORMAL</code></td><td>Request completed normally.</td></tr><tr><td><code>SS$_SYNCH</code></td><td>Synchronous grant; no completion AST will be delivered.</td></tr><tr><td><code>SS$_NOTQUEUED</code></td><td>No-queue request could not be granted immediately.</td></tr><tr><td><code>SS$_CANCEL</code></td><td>A pending conversion was cancelled.</td></tr><tr><td><code>SS$_ABORT</code></td><td>The request was aborted.</td></tr><tr><td><code>SS$_VALNOTVALID</code></td><td>The lock value block is marked invalid.</td></tr><tr><td><code>SS$_IVLOCKID</code></td><td>Invalid lock id supplied.</td></tr><tr><td><code>SS$_NOLOCKID</code></td><td>No lock id available.</td></tr><tr><td><code>SS$_PARNOTGRANT</code></td><td>Parent lock is not granted.</td></tr></tbody></table></div><figure style="margin:1.5rem 0;"><img src="https://cdn.prod.website-files.com/6654a06b5910e86da45495b0/678e68036e503584b0427fdc_LOCK_MANAGER.png" alt="VX/LCKMGR lock-status and return-code reference chart for SYS$ENQ, SYS$DEQ and SYS$GETLKI on Linux" loading="lazy" style="max-width:100%;height:auto;border-radius:8px;"></figure><h2 id="transport">Single-node transport</h2><p> The shipping client and server communicate over a <strong>System V IPC message queue</strong> on the same host. The daemon derives a well-known queue key from its configured system value, attaches to the queue, and both sides exchange packets with the standard <code>msgget</code>, <code>msgsnd</code>, and <code>msgrcv</code> calls. Per-process statistics are read the same single-host way, out of a shared status file in the daemon's temporary directory. </p><div class="ipc-diagram ipc-converge" role="group" aria-label="Many client processes converging on one VX/LCKMGR daemon"><div class="ipc-clients"><div class="ipc-card"><span class="ipc-label">Client A</span><strong>Application process</strong><span>Publishes lock requests and receives AST replies.</span></div><div class="ipc-card"><span class="ipc-label">Client B</span><strong>Application process</strong><span>Uses the same host-local transport.</span></div><div class="ipc-card"><span class="ipc-label">Client C</span><strong>Application process</strong><span>Converges on the shared queue and daemon.</span></div></div><div class="ipc-converge-note">All clients converge through one host-local IPC queue</div><div class="ipc-hub ipc-queue"><span class="ipc-label">System V message queue</span><strong>Key = SYSTEM value + 3</strong><span>Request packets flow to the daemon. Grant and blocking AST replies flow back to the originating process.</span></div><div class="ipc-converge-note">One queue converges on one arbitration service</div><div class="ipc-hub ipc-daemon"><span class="ipc-label">lckmgr daemon</span><strong>Resource database, lock database, and AST reply dispatch</strong><span>The single local daemon coordinates all attached client processes.</span></div></div><div class="callout"><strong>Why single-node.</strong> The resource master is always the local host and cluster system ids are zero, because the transport is a local message queue rather than a cluster interconnect. This is a deliberate scope boundary: multi-node and cluster-wide lock mastering are out of scope for the current build. Applications that lock only within a single node run unchanged; applications that depend on cluster-wide lock coordination should be reviewed against this boundary during migration planning. </div><h2 id="operator">Operator control, statistics, and audit</h2><p> The <code>lckctl</code> utility starts and stops the daemon and reports on its state. It is the VX/LCKMGR equivalent of using SDA and operator commands to inspect the VMS lock manager. </p><div class="table-wrapper"><table><thead><tr><th>Command</th><th>Action</th></tr></thead><tbody><tr><td><code>lckctl start</code></td><td>Launch the <code>lckmgr</code> daemon.</td></tr><tr><td><code>lckctl stop</code></td><td>Send a shutdown request to the daemon.</td></tr><tr><td><code>lckctl stats</code> / <code>status</code></td><td>Dump the daemon statistics block.</td></tr><tr><td><code>lckctl reset-stats</code></td><td>Zero and rewrite the statistics block.</td></tr><tr><td><code>lckctl vacuum</code></td><td>Drain orphaned packets from the IPC queue: republish those whose target process is still alive, discard the rest.</td></tr></tbody></table></div><h6>Statistics</h6><p> The statistics block reports service timestamps and a running set of counters: packets received, dispatched, and replied; blocking ASTs and grant ASTs fired; enqueue, dequeue, and query counts; how many requests took the grant, conversion, and wait paths; and outcome counters for no-queue rejections and deadlock-search cases. It also reports object counts (lock blocks, resource blocks, resource names, and active sessions) and per-arena memory use, with a memory-to-request ratio for capacity planning. </p><h6>Audit log and replay</h6><p> The daemon can record every lock request to an audit log and later replay it. Each record captures the operation, direction, timestamp, and the full request message, in a 32-bit-portable format so a recording made by one build can be read by another. Replay can be driven back through the daemon to reproduce a workload for tuning, or reported offline with the <code>AuditHistory</code> tool, which tallies enqueue, dequeue, and query volumes and prints an operations-per-second summary. Because replay reconstructs the request stream directly, bottlenecks can be isolated without re-running the original application. </p><h2 id="status">Status and divergences from the OpenVMS lock manager</h2><p> VX/LCKMGR reproduces the lock-manager surface that migrated applications actually call. The matrix below is explicit about what is live, what is stubbed, and what is deliberately out of scope, so migration planning has no surprises. </p><div class="table-wrapper"><table><thead><tr><th>Capability</th><th>Status</th></tr></thead><tbody><tr><td><code>SYS$ENQ(W)</code> / <code>SYS$DEQ(W)</code> / <code>SYS$GETLKI(W)</code></td><td><span class="ac-live">live</span></td></tr><tr><td>Six lock modes and the full compatibility matrix</td><td><span class="ac-live">live</span></td></tr><tr><td>Grant, conversion, and wait queues; 16-byte and 64-byte value blocks</td><td><span class="ac-live">live</span></td></tr><tr><td>Resource domains (<code>SYS$SET_RESOURCE_DOMAIN</code>)</td><td><span class="ac-live">live</span></td></tr><tr><td>Audit log record, replay, and reporting</td><td><span class="ac-live">live</span></td></tr><tr><td><code>lckctl</code> start, stop, stats, status, vacuum</td><td><span class="ac-live">live</span></td></tr><tr><td>Runtime debug-mask add and remove in <code>lckctl</code></td><td><span class="ac-v1">stubbed</span></td></tr><tr><td>Deadlock detection</td><td><span class="ac-no">not implemented</span></td></tr><tr><td>ENQLM and sublock-depth quota enforcement</td><td><span class="ac-no">out of scope</span></td></tr><tr><td>Cluster and multi-node lock mastering</td><td><span class="ac-no">out of scope</span></td></tr></tbody></table></div><div class="callout warn"><strong>No deadlock detection.</strong> The daemon does not currently run a deadlock search. The counters and status plumbing exist, but applications that rely on the VMS lock manager raising a deadlock condition should be reviewed during migration. Where deadlock avoidance matters, design lock ordering into the application rather than depending on detection. </div><h6>Deliberate divergences</h6><div class="table-wrapper"><table><thead><tr><th>Divergence</th><th>Reason</th></tr></thead><tbody><tr><td>Single-node only; cluster system ids degenerate to 0</td><td>The transport is a local System V message queue, not a cluster interconnect.</td></tr><tr><td>No deadlock detection</td><td>The deadlock search is not implemented in the current build.</td></tr><tr><td>ENQLM and sublock-depth limits never raised</td><td>These quota limits are not imposed.</td></tr><tr><td>Client-side pre-validation of arguments</td><td>The daemon never touches client memory, so access-violation and bad-parameter checks are the client library's responsibility.</td></tr><tr><td><code>LCK$M_AUDITED</code> option added</td><td>A Sector7 extension for audit tracing, not present in the original lock manager.</td></tr><tr><td>Maximum of 80,000 concurrent lock ids</td><td>The lock table is sized for up to 80,000 active locks per daemon.</td></tr></tbody></table></div><h2 id="quickref">Quick reference</h2><h6>Lock modes</h6><div class="kw-grid"><div>NL = 0</div><div>CR = 1</div><div>CW = 2</div><div>PR = 3</div><div>PW = 4</div><div>EX = 5</div></div><p>EX excludes all but NL; NL is compatible with every mode.</p><h6>Client routines</h6><div class="kw-grid"><div>SYS$ENQ</div><div>SYS$ENQW</div><div>SYS$DEQ</div><div>SYS$DEQW</div><div>SYS$GETLKI</div><div>SYS$GETLKIW</div><div>SYS$SET_RESOURCE_DOMAIN</div><div>SYS$RUNDOWN</div></div><h6>Common request flags</h6><div class="kw-grid"><div>LCK$M_VALBLK</div><div>LCK$M_CONVERT</div><div>LCK$M_NOQUEUE</div><div>LCK$M_SYNCSTS</div><div>LCK$M_SYSTEM</div><div>LCK$M_EXPEDITE</div><div>LCK$M_XVALBLK</div><div>LCK$M_AUDITED</div></div><h6>Lock states</h6><div class="kw-grid"><div>LKI$C_WAITING = -1</div><div>LKI$C_CONVERT = 0</div><div>LKI$C_GRANTED = 1</div></div><h6>Operator commands</h6><div class="kw-grid"><div>lckctl start</div><div>lckctl stop</div><div>lckctl stats</div><div>lckctl status</div><div>lckctl reset-stats</div><div>lckctl vacuum</div></div><h6>Scope</h6><p> Single node, one <code>lckmgr</code> daemon per node, clients reached over a System V message queue. Up to 80,000 concurrent lock ids. No cluster mastering and no deadlock detection in the current build. </p></div>

Frequently Asked Questions

Curious about how Sector7 can facilitate your application migration? Explore our FAQs for expert insights.

What is VX/LCKMGR and how does it help with OpenVMS migrations?

VX/LCKMGR is a fully compatible OpenVMS lock management system designed for Linux environments. It ensures identical functionality to OpenVMS lock management while leveraging Sector7's high-speed AST backplane for enhanced performance.

This solution supports core OpenVMS lock manager functions like SYS$ENQ(W), SYS$DEQ(W), and SYS$GETLKI(), enabling seamless integration with existing OpenVMS applications during migration to Linux. Contact us to discuss your OpenVMS lock manager migration requirements.

What lock modes does VX/LCKMGR support?

VX/LCKMGR supports all major OpenVMS lock modes including Protected Read (LCK$K_PRMODE), Protected Write (LCK$K_PWMODE), Concurrent Read (LCK$K_CRMODE), Concurrent Write (LCK$K_CWMODE), Exclusive (LCK$K_EXMODE), and NULL (LCK$K_NLMODE).

How fast can VX/LCKMGR process lock operations?

In stress testing, VX/LCKMGR demonstrated exceptional performance by handling 80,000 lock conversions per second on a 1997-era quad-core Opteron server.

What $ENQ and $DEQ codes are supported?

VX/LCKMGR supports essential $ENQ/$DEQ codes including LCK$V_VALBLK (Value Block), LCK$V_CONVERT (Convert Lock), LCK$V_NOQUEUE (No Queue), LCK$V_SYNCSTS (Sync Status), LCK$V_SYSTEM (System Lock), and LCK$V_RECOVER (Recovery Mode).

How does VX/LCKMGR handle lock diagnostics and monitoring?

VX/LCKMGR provides comprehensive diagnostic capabilities including real-time lock request logging and replay functionality. The system can log all lock requests and replay them for performance tuning and diagnostic purposes.

Additionally, it offers real-time display of all lock requests, making it easier for system administrators to track lock statuses and manage resources effectively. Companies like Telstra have benefited from similar real-time monitoring capabilities during their OpenVMS migrations.

What $GETLKI information can be retrieved?

VX/LCKMGR supports $GETLKI codes for retrieving lock information including LKI$C_LENGTH (Lock Length), LKI$_BLOCKEDBY (Blocked By Lock), LKI$_BLOCKING (Blocking Lock), LKI$_LCKCOUNT (Lock Count), and LKI$_LOCKID (Lock ID).

Are there any limitations with VX/LCKMGR implementation?

VX/LCKMGR fully supports OpenVMS lock management functions but is limited to single-node environments. It does not operate across multiple nodes, which is important to consider when planning implementation in distributed systems.

What are the key benefits of migrating to VX/LCKMGR?

VX/LCKMGR delivers exceptional performance with high-speed lock processing capabilities and comprehensive diagnostic tools. Organizations gain full OpenVMS lock manager compatibility while moving to modern Linux environments.

The system provides advanced logging, real-time monitoring, and replay capabilities that help optimize performance and troubleshoot issues. This enables seamless migration from OpenVMS while maintaining all critical locking functionality.

Financial institutions like BNY Mellon have successfully leveraged similar OpenVMS migration strategies. To explore how VX/LCKMGR can support your specific migration requirements, contact our team for a detailed consultation.

What is VX/LCKMGR and how does it help with an OpenVMS migration?

VX/LCKMGR is Sector7's reimplementation of the OpenVMS lock manager for Linux. It provides the SYS$ENQ, SYS$DEQ and SYS$GETLKI system services and the arbitration daemon behind them, so migrated OpenVMS applications keep their existing locking logic and coordinate access to shared resources on modern Linux without a rewrite. Contact our team to review how it fits your estate.

Does VX/LCKMGR require rewriting our application's locking code?

No. Applications call the same SYS$ services with the same arguments and option flags as on OpenVMS. The lock modes, flag encodings and completion behaviour are preserved, so existing code that enqueues, converts and dequeues locks links and runs unchanged.

Can VX/LCKMGR run across multiple nodes or an OpenVMS cluster?

No. VX/LCKMGR is a single-node lock manager. One daemon on a node owns the resource and lock databases, and client processes reach it over a local message queue. It supports every OpenVMS lock mode on that node but does not perform cluster-wide lock mastering, so applications that depend on cluster coordination should be reviewed during migration planning.

Which lock modes does VX/LCKMGR support?

All six OpenVMS lock modes: null (NL), concurrent read (CR), concurrent write (CW), protected read (PR), protected write (PW) and exclusive (EX). The full compatibility matrix and the grant, conversion and wait queue behaviour match the OpenVMS lock manager.

What lock information can we query at runtime?

SYS$GETLKI returns detailed lock information through an item list, including the process, lock id, state, resource name, value block, and blocking and waiting relationships. This supports the same troubleshooting, monitoring and diagnostic workflows teams rely on with the OpenVMS lock manager.

How does VX/LCKMGR help diagnose locking performance issues?

The daemon can record every lock request to an audit log and replay it later for tuning and diagnostics, and it reports live statistics on request volumes and queue activity through the lckctl utility. Because replay reconstructs the request stream directly, bottlenecks can be investigated without re-running the original application.

Does VX/LCKMGR perform deadlock detection?

The current build does not run a deadlock search. Applications that rely on the OpenVMS lock manager signalling a deadlock should be reviewed during migration, and where deadlock avoidance matters we recommend designing consistent lock ordering into the application. Our team can advise on the right approach for your workload.

What platforms and scale does VX/LCKMGR support?

VX/LCKMGR runs on Linux and supports up to 80,000 concurrent lock ids per daemon, with 16-byte and 64-byte lock value blocks and resource domains so unrelated applications can share one daemon safely. It is delivered as part of Sector7's OpenVMS to Linux migration toolset.

Transform Your Legacy Software Today!

Get In Touch
Unlock the potential of your legacy software with our expert migration services.