Lock one invariant, not an entire call
LOCK(name) waits for exclusive ownership of one named mutex, while TRYLOCK(name) returns immediately when that mutex is unavailable. Both return 1 when obtained and 0 otherwise. The result proves only lock acquisition; it does not prove that a shared-variable update, database operation, file write, external request or call branch succeeded.
Name the invariant before adding a mutex: for example, allocating one scarce identifier or updating a read-modify-write counter. Keep prompts, dialing, network requests and unrelated calculations outside the critical section so one slow call does not serialize other channels unnecessarily.
Choose blocking or explicit contention handling
Use LOCK() only when bounded waiting is acceptable to the caller and every exit path is understood. Asterisk documents a special three-second attempt when the same channel already holds another lock, reducing one deadlock risk without proving that arbitrary lock ordering is safe.
Use TRYLOCK() when the dialplan must choose immediately among retry, queue, alternate destination, unchanged read or explicit failure. A zero result is contention or error evidence, not permission to enter the protected section anyway. Record the exact branch taken and the caller treatment that follows.
Keep lock name, channel and data scope aligned
The lock is named, while ownership belongs to the executing channel. Derive names only from bounded, normalized identifiers; an accidental per-call suffix defeats mutual exclusion, while one global name can turn unrelated tenants or resources into a single bottleneck. Do not place untrusted caller text directly into an administrative naming scheme.
SHARED() values occupy a namespace separate from ordinary channel variables and can change without warning when other channels write them. Global variables apply across calls. A named lock does not automatically bind either store: every reader and writer participating in the invariant must use the same lock contract.
Protect the complete read-modify-write sequence
Acquire the mutex, verify the success result, read the shared value into an ordinary channel variable, validate it, compute the new value, commit the update, verify the write and release the same name. Locking only the write still allows two channels to read the same old value and produce a lost update.
Keep database transactions, AstDB operations, files and remote services attached to their own commit evidence. A mutex inside one Asterisk process does not establish cross-node serialization, durable storage, remote idempotency or cluster consensus.
Release deliberately on every normal branch
UNLOCK(name) returns 1 when the channel held a lock and 0 otherwise. Treat an unexpected zero as evidence of mismatched naming, ownership or control flow. Asterisk says held locks are automatically freed when the channel is destroyed, but that cleanup boundary is not a substitute for releasing after the protected operation.
Keep the release immediately visible in the same subroutine or use a single cleanup branch that every acquired path reaches. Never attempt an unlock from a sibling channel merely because it shares a linked ID, and do not rely on hangup cleanup to shorten a long-lived call.
Prove contention, state and caller outcome separately
For an authorized test, launch a small controlled overlap against a non-production key. Record channel IDs, lock name, acquisition result, wait interval, state before and after, write result, unlock result and final dialplan destination. Include an uncontended control and a deliberately failed protected operation.
Keep live_dangerously=no unless a reviewed integration requires otherwise; the functions document that this setting confines direct execution to dialplan rather than external protocols. This guide is documentation-derived. No PBX, lock, variable, external resource or call was tested.
Protect multi-step AstDB invariants separately
An AstDB key can persist locally, but a read-modify-write sequence still needs one shared mutex contract. Use the AstDB guide for storage semantics and this guide for local serialization.
Do not mistake one database connection for transaction safety
The func_odbc guide keeps connection reuse, database transactions, local dialplan locks and caller treatment as separate contracts.
Keep a dialplan lock distinct from a database transaction
The ODBC transaction guide treats local serialization, database isolation, schema constraints and idempotency as different controls.
A shared condition still needs one protected invariant
The WaitForCondition guide does not treat polling, a linked ID or a similarly named value as cross-channel synchronization.
Do not turn a changing key list into hidden shared state
The HASH and ARRAY guide snapshots HASHKEYS before mutation and reserves persistent or cross-call ownership for an explicit shared-state design.
Do not turn Setvar into an unlocked shared-state update
The AMI variable diagnostic checks namespace and concurrent writers before accepting a shared or global value.
Coordinate AMI and dialplan writers
The AMI AstDB guide treats local persistence as separate from concurrency and application-level ownership.
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 internal database overview ↗
- Asterisk DB dialplan function ↗
- Asterisk DB_EXISTS dialplan function ↗
- Asterisk DB_DELETE dialplan function ↗
- Asterisk DB_KEYS dialplan function ↗
- Asterisk Configuring res_odbc ↗
- Asterisk func_odbc.conf sample ↗
- Asterisk res_odbc.conf sample ↗
- Asterisk 23 ODBC_FETCH function ↗
- Asterisk 23 SQL_ESC function ↗
- Asterisk 23 SQL_ESC_BACKSLASHES function ↗
- Asterisk security advisory AST-2022-003 ↗
- Asterisk database transactions ↗
- Asterisk latest ODBC transaction function ↗
- Asterisk latest ODBC_Commit application ↗
- Asterisk latest ODBC_Rollback application ↗
- Asterisk 23 WaitForCondition application ↗
- Asterisk 22 WaitForCondition application ↗
- Asterisk dialplan expressions ↗
- Asterisk conditional applications ↗
- Asterisk 23 VARIABLE_EXISTS function ↗
- Asterisk 23 GLOBAL_EXISTS function ↗
- Asterisk HASH dialplan function ↗
- Asterisk HASHKEYS dialplan function ↗
- Asterisk ARRAY dialplan function ↗
- Asterisk ClearHash dialplan application ↗
- Asterisk SHIFT dialplan function ↗
- Asterisk channel-variable setting and substitution ↗
- Asterisk channel-variable inheritance ↗
- Asterisk func_strings implementation ↗
- Asterisk 23 AMI Getvar action ↗
- Asterisk 23 AMI Setvar action ↗
- Asterisk 23 AMI VarSet event ↗
- Asterisk 23 SHARED dialplan function ↗
- Asterisk external-protocol dialplan-function safeguards ↗
- Asterisk AMI event filtering ↗
- Asterisk 23 AMI DBGet action ↗
- Asterisk 23 AMI DBGetTree action ↗
- Asterisk 23 AMI DBPut action ↗
- Asterisk 23 AMI DBDel action ↗
- Asterisk 23 AMI DBDelTree action ↗
- Asterisk SQLite3 AstDB back end ↗
- Asterisk AMI v2 specification ↗
Examples require adaptation to your topology. No live PBX or hardware testing is claimed.