VoIP.info Troubleshooting

Asterisk calls wait on a lock or shared state is wrong

Find the first mismatch among function availability, lock name, acquisition result, channel ownership, critical-section duration, shared-value read and write, release and downstream caller treatment.

Reviewed 2026-10-15Asterisk Latest API branch 23 LOCK, TRYLOCK and UNLOCK documentation, Asterisk 22 SHARED documentation and current global-variable guidance reviewed October 14, 2026; function availability, loaded func_lock and func_global modules, exact lock names, channel ownership, protected data, wait duration, dialplan branches and external side effects remain deployment specific; no PBX, module, dialplan, channel, lock, variable, database row, file, external service or call was inspected or testedAdvancedDocumentation based · not lab tested

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.

Examples require adaptation to your topology. No live PBX or hardware testing is claimed.