Freeze one contention interval and one invariant
Record timestamp and timezone, Asterisk version, dialplan revision, channel name, unique and linked IDs, context, extension and priority, exact lock name, function result, entry and exit times, protected data key, value before and after, release result and final caller treatment. Redact values that contain identity, credentials or customer data.
Classify the first divergence as unavailable function, wrong dialplan branch, inconsistent lock name, lock not obtained, unexpectedly long critical section, nested-lock ordering, unprotected reader or writer, failed protected operation, wrong unlock owner, destroyed channel or downstream route failure.
Prove the function and executed branch
Confirm the running build exposes LOCK(), TRYLOCK() and UNLOCK() and that the affected channel executed the expected priority. Online branch documentation does not prove the installed package, loaded modules or generated FreePBX dialplan.
Retain the return value before entering the critical section. LOCK() or TRYLOCK() returning zero means the mutex was not obtained; later state from an unguarded branch cannot be treated as protected. A successful result still says nothing about the data update or destination.
Compare every participant’s exact lock name
Normalize case, delimiters, prefixes, tenant or resource identifiers and whitespace before comparing names. One caller using a per-channel suffix will never contend with another; one caller omitting a tenant component can block unrelated work.
Map acquisition and release to the same channel. Local-channel halves, transfer-created legs, queue members and originated helpers are separate owners even when they share a linked ID. An unlock attempt from the wrong leg can return zero while the original channel continues holding the mutex.
Measure the critical section and nested ordering
Measure time between successful acquisition and release, then locate prompts, Dial, network calls, database waits, AGI or external commands inside that window. Move unrelated or slow work out only after preserving the atomic invariant.
If a channel holds more than one named lock, list the acquisition order for every code path. Asterisk’s documented three-second attempt when a channel already holds another lock does not make inconsistent order correct; it can surface as delay or a zero result rather than a permanent wait.
Audit the shared read-modify-write contract
List every reader and writer of the SHARED, GLOBAL, AstDB, database, file or external key. Confirm that each participant acquires the same mutex before the entire read-validate-compute-write sequence, and copies a SHARED value into an ordinary channel variable before calculations.
A correct mutex inside one process does not coordinate another Asterisk node or an external writer. If state spans hosts or services, use the storage system’s transaction, uniqueness or idempotency controls and keep the dialplan lock as only one local layer.
Restore explicit release and caller treatment
Route every successful acquisition through a visible UNLOCK() path after the smallest protected operation. Preserve the automatic channel-destruction cleanup as a last boundary, not the normal design. For TRYLOCK() contention, define retry, alternate route or clear failure treatment instead of silently using stale state.
Correct the demonstrated owner, then repeat an uncontended attempt, a controlled overlap and a protected-operation failure. Verify state and caller outcome independently. No PBX, module, dialplan, channel, lock, variable, database row, file, external service or call was inspected or tested for this diagnostic.
Polling does not serialize a shared-state update
The WaitForCondition diagnostic keeps wait status separate from mutex ownership and a protected read-modify-write sequence.
Sources & applicability
Primary references for the technical details above. Operational examples and planning checklists are VoIP.info editorial guidance.
- Asterisk LOCK dialplan function ↗
- Asterisk TRYLOCK dialplan function ↗
- Asterisk UNLOCK dialplan function ↗
- Asterisk 22 SHARED dialplan function ↗
- Asterisk global variables basics ↗
- Asterisk 23 WaitForCondition application ↗
- Asterisk 22 WaitForCondition application ↗
- Asterisk dialplan expressions ↗
- Asterisk conditional applications ↗
- Asterisk 23 VARIABLE_EXISTS function ↗
- Asterisk 23 GLOBAL_EXISTS function ↗
Examples require adaptation to your topology. No live PBX or hardware testing is claimed.