Caché ObjectScript Reference
[Home] [Back] [Next]
InterSystems: The power behind what matters   

Enables a process to apply and release locks to control access to data resources.

LOCK:pc +lockname#locktype:timeout,...
L:pc +lockname#locktype:timeout,...

LOCK:pc +(lockname#locktype,...):timeout,...
L:pc +(lockname#locktype,...):timeout,...
pc Optional — A postconditional expression.
Optional — The lock operation indicator (a + character, – character, or no character) to apply or remove a lock. A + (plus sign) applies the specified lock(s) without unlocking any prior locks. This can be used to apply an incremental lock. A – (minus sign) unlocks (or decrements) a lock. If you omit the lock operation indicator (no character), Caché unlocks all prior locks and applies the specified lock(s).
lockname A lock name associated with the resource(s) to be locked or unlocked. Must be a valid identifier, following the same naming conventions as local variables or globals.
#locktype Optional — A letter code specifying the type of lock to lock or unlock, specified in quotation marks. Available values are “S” (shared lock), ”E” (escalating lock), “I” (immediate unlock), and “D” (deferred unlock). When specifying, the preceding # symbol is mandatory. For example, #"S". You can specify more than one letter code. For example, #"SEI". “S” and “E” are specified for both locking and unlocking operations; “I” and “D” are only specified for unlocking operations. If omitted, the lock type defaults to an exclusive lock (non-S) that does not escalate (non-E) and that always defers releasing an unlocked lock to the end of the current transaction (non-I / non-D).
:timeout Optional — The time to wait before the attempted lock operation times out. Can be specified with or without the optional #locktype. When specifying, the preceding : symbol is mandatory. For example, LOCK ^a(1):10 or LOCK ^a(1)#"E":10. Specify timeout as an integer number of seconds. A value of 0 means to make one attempt, then time out. Fractional seconds are truncated to the integer portion. If omitted, Caché waits indefinitely.
There are two basic forms of the LOCK command:
LOCK without Arguments
The argumentless LOCK releases (unlocks) all locks currently held by the process in all namespaces. This includes exclusive and shared locks, both local and global. It also includes all accumulated incremental locks. For example, if there are three incremental locks on a given lock name, Caché releases all three locks and removes the lock name entry from the lock table.
If you issue an argumentless LOCK during a transaction, Caché places all locks currently held by the process in a Delock state until the end of the transaction. When the transaction ends, Caché releases the locks and removes the corresponding lock name entries from the lock table.
The following example applies various locks during a transaction, then issues an argumentless LOCK to release all of these locks. The locks are placed in a Delock state until the end of the transaction. The HANG commands give you time to check the lock’s ModeCount in the Lock Table:
  LOCK +^a(1)      // ModeCount: Exclusive
  HANG 2
  LOCK +^a(1)#"E"  // ModeCount: Exclusive/1+1e
  HANG 2
  LOCK +^a(1)#"S"  // ModeCount: Exclusive/1+1e,Shared
  HANG 2
  LOCK             // ModeCount: Exclusive/1+1e->Delock,Shared->Delock
  HANG 10
  TCOMMIT          // ModeCount: locks removed from table
Argumentless LOCK releases all locks held by the process without applying any locks. Completion of a process also releases all locks held by that process.
LOCK with Arguments
LOCK with arguments specifies one or more lock names on which to perform locking and unlocking operations. What lock operation Caché performs depends on the lock operation indicator argument you use:
A lock operation may immediately apply the lock, or it may place the lock request on a wait queue pending the release of a conflicting lock by another process. A waiting lock request may time out (if you specify a timeout) or may wait indefinitely (until the end of the process).
LOCK with Multiple Lock Names
You can specify multiple locks with a single LOCK command in either of two ways:
An optional postconditional expression that can make the command conditional. Caché executes the LOCK command if the postconditional expression is true (evaluates to a nonzero numeric value). Caché does not execute the command if the postconditional expression is false (evaluates to zero). You can specify a postconditional expression on an argumentless LOCK command or a LOCK command with arguments. For further details, refer to Command Postconditional Expressions in Using Caché ObjectScript.
lock operation indicator
The lock operation indicator is used to apply (lock) or remove (unlock) a lock. It can be one of the following values:
No character Unlock all prior locks belonging to the current process and attempt to apply the specified lock. For example, LOCK ^a(1) performs the following atomic operation: it releases all locks previously held by the process (whether local or global, exclusive or shared, escalating or non-escalating) and it attempts to lock ^a(1). This can result in one of two outcomes: (1) all prior locks unlocked and ^a(1) locked; (2) all prior locks unlocked and ^a(1) placed in a lock waiting state pending the release of a conflicting lock held by another process.
Plus sign (+) Attempt to apply the specified lock without performing any unlocks. This allows you to add a lock to the locks held by the current process. One use of this option is to perform incremental locking of a lock name.
Minus sign (-) Unlocks the specified lock. If the lock name has a lock count of 1, an unlock remove the lock from the lock table. If the lock name has a lock count of more than 1, an unlock removes one of its incremental locks (decrements the lock count). By default, this unlocks an exclusive, non-escalating lock. To remove a shared lock and/or an escalating lock, you must specify the corresponding #locktype.
If your LOCK command contains multiple comma-separated lock arguments, each lock argument can have its own lock operation indicator. Caché parses this as multiple independent LOCK commands.
A lockname is the name of a lock for a data resource; it is not the data resource itself. That is, your program can specify a lock named ^a(1) and a variable named ^a(1) without conflict. The relationship between the lock and the data resource is a programming convention; by convention, processes must acquire the lock before modifying the corresponding data resource.
Lock names are case-sensitive. Lock names follow the same naming conventions as the corresponding local variables and global variables. A lock name can be subscripted or unsubscripted. Lock subscripts have the same naming conventions and maximum length and number of levels as variable subscripts. In Caché, the following are all valid and unique lock names: a, a(1), A(1), ^a, ^a(1,2), ^A(1,1,1). For further details, see the Variables chapter of Using Caché ObjectScript.
For performance reasons, it is recommended you specify lock names with subscripts whenever possible. For example, ^a(1) rather than ^a. Subscripted lock names are used in documentation examples.
Lock names can be local or global. A lock name such as A(1) is a local lock name. It applies only to that process, but does apply across namespaces. A lock name that begins with a caret (^) character is a global lock name; the mapping for this lock follows the same mapping as the corresponding global, and thus can apply across processes, controlling their access to the same resource. (See Global Structure in Using Caché Globals.)
Process-private global names can not be used as lock names. Attempting to use a process-private global name as a lock name performs no operation and completes without issuing an error.
A lock name can represent a local or global variable, subscripted or unsubscripted. It can be an implicit global reference, or an extended reference to a global on another computer. (See Global Structure in Using Caché Globals.)
The data resource corresponding to a lock name does not need to exist. For example, you may lock the lock name ^a(1,2,3) whether or not a global variable with the same name exists. Because the relationship between locks and data resources is an agreed-upon convention, a lock may be used to protect a data resource with an entirely different name.
A letter code specifying the type of lock to apply or remove. locktype is an optional argument; if you omit locktype, the lock type defaults to an exclusive non-escalating lock. If you omit locktype, you must omit the pound sign (#) prefix. If you specify locktype, the syntax for lock type is a mandatory pound sign (#), followed by quotation marks enclosing one or more lock type letter codes. Lock type letter codes can be specified in any order and are not case-sensitive. The following are the lock type letter codes:
The number of seconds to wait for a lock request to succeed before timing out. timeout is an optional argument. If omitted, the LOCK command waits indefinitely for a resource to be lockable; if the lock cannot be applied, the process will hang. The syntax for timeout is a mandatory colon (:), followed by an integer value or an expression that evaluates to an integer value. A value of zero permits one locking attempt before timing out. A negative number is equivalent to zero.
Commonly, a lock will wait if another process has a conflicting lock that prevents this lock request from acquiring (holding) the specified lock. The lock request waits until either a lock is released that resolves the conflict, or the lock request times out. Terminating the process also ends (deletes) pending lock requests. Lock conflict can result from many situations, not just one process requesting the same lock held by another process. A detailed explanation of lock conflict and lock request wait states is provided in the Lock Management chapter of Using Caché ObjectScript.
If you use timeout and the lock is successful, Caché sets the $TEST special variable to 1 (TRUE). If the lock cannot be applied within the timeout period, Caché sets $TEST to 0 (FALSE). Issuing a lock request without a timeout has no effect on the current value of $TEST. Note that $TEST can also be set by the user, or by a JOB, OPEN, or READ timeout.
The following example applies a lock on lock name ^abc(1,1), and unlocks all prior locks held by the process:
  LOCK ^abc(1,1)
This command requests an exclusive lock: no other process can simultaneously hold a lock on this resource. If another process already holds a lock on this resource (exclusive or shared), this example must wait for that lock to be released. It can wait indefinitely, hanging the process. To avoid this, specifying a timeout value is strongly recommended:
  LOCK ^abc(1,1):10
If a LOCK specifies multiple lockname arguments in a comma-separated list, each lockname resource may have its own timeout (syntax without parentheses), or all of the specified lockname resources may share a single timeout (syntax with parentheses).
Caché performs multiple operations in strict left-to-right order. Therefore, in LOCK syntax without parentheses, the $TEST value indicates the outcome of the last (rightmost) of multiple lockname lock requests.
In the following examples, the current process cannot lock ^a(1) because it is exclusively locked by another process. These examples use a timeout of 0, which means they make one attempt to apply the specified lock.
The first example locks ^x(1) and ^z(1). It sets $TEST=1 because ^z(1) succeeded before timing out:
  LOCK +^x(1):0,+^a(1):0,+^z(1):0
The second example locks ^x(1) and ^z(1). It sets $TEST=0 because ^a(1) timed out. ^z(1) did not specify a timeout and therefore had no effect on $TEST:
  LOCK +^x(1):0,+^a(1):0,+^z(1)
The third example applies no locks, because a list of locks in parentheses is an atomic (all-or-nothing) operation. It sets $TEST=0 because ^a(1) timed out:
  LOCK +(^x(1),^a(1),^z(1)):0
Using the Lock Table to View and Delete Locks System-wide
Caché maintains a system-wide lock table that records all locks that are in effect and the processes that have applied them. The system manager can display the existing locks in the Lock Table or remove selected locks using the Management Portal interface or the ^LOCKTAB utility, as described in the Lock Management chapter of Using Caché ObjectScript. You can also use the %SYS.LockQuery class to read lock table information. From the %SYS namespace you can use the SYS.Lock class to manage the lock table.
You can use the Management Portal to view held locks and pending lock requests system-wide. Go to the Management Portal, select System Operation, select Locks, then select View Locks. For further details on the View Locks table refer to the Lock Management chapter of Using Caché ObjectScript.
You can use the Management Portal to remove (delete) locks currently held on the system. Go to the Management Portal, select System Operation, select Locks, then select Manage Locks. For the desired process (Owner) click either “Remove” or “Remove All Locks for Process”.
Removing a lock releases all forms of that lock: all increment levels of the lock, all exclusive, exclusive escalating, and shared versions of the lock. Removing a lock immediately causes the next lock waiting in that lock queue to be applied.
You can also remove locks using the SYS.Lock.DeleteOneLock() and SYS.Lock.DeleteAllLocks() methods.
Removing a lock requires WRITE permission. Lock removal is logged in the audit database (if enabled); it is not logged in cconsole.log.
Incremental Locking and Unlocking
Incremental locking permits you to apply the same lock multiple times: to increment the lock. An incremented lock has a lock count of > 1. Your process can subsequently increment and decrement this lock count. The lock is released when the lock count decrements to 0. No other process can acquire the lock until the lock count decrements to 0. The lock table maintains separate lock counts for exclusive locks and shared locks, and for escalating and non-escalating locks of each type. The maximum incremental lock count is 32,766. Attempting to exceed this maximum lock count results in a <MAX LOCKS> error.
You can increment a lock as follows:
Unlocking an incremented lock when not in a transaction simply decrements the lock count. Unlocking an incremented lock while in a transaction has the following default behavior:
Note that separate lock counts are maintained for the same lock as an Exclusive lock, a Shared lock, a Exclusive escalating lock and a Shared escalating lock. In the following example, the first unlock decrements four separate lock counts for lock ^a(1) by 1. The second unlock must specify all four of the ^a(1) locks to remove them. The HANG commands give you time to check the lock’s ModeCount in the Lock Table.
   LOCK +(^a(1),^a(1)#"E",^a(1)#"S",^a(1)#"SE")
   LOCK +(^a(1),^a(1)#"E",^a(1)#"S",^a(1)#"SE")
   HANG 10
   LOCK -(^a(1),^a(1)#"E",^a(1)#"S",^a(1)#"SE")
   HANG 10
   LOCK -(^a(1),^a(1)#"E",^a(1)#"S",^a(1)#"SE")
If you attempt to unlock a lock name that has no current locks applied, no operation is performed and no error is returned.
Automatic Unlock
When a process terminates, Caché performs an implicit argumentless LOCK to clear all locks that were applied by the process. It removes both held locks and lock wait requests.
Locks on Global Variables
Locking is typically used with global variables to synchronize the activities of multiple processes that may access these variables simultaneously. Global variables differ from local variables in that they reside on disk and are available to all processes. The potential exists, then, for two processes to write to the same global at the same time. In fact, Caché processes one update before the other, so that one update overwrites and, in effect, discards the other.
Global lock names begin with a caret (^) character.
To illustrate locking with global variables, consider the case in which two data entry clerks are concurrently running the same student admissions application to add records for newly enrolled students. The records are stored in a global array named ^student. To ensure a unique record for each student, the application increments the global variable ^index for each student added. The application includes the LOCK command to ensure that each student record is added at a unique location in the array, and that one student record does not overwrite another.
The relevant code in the application is shown below. In this case, the LOCK controls not the global array ^student but the global variable ^index. ^index is a scratch global that is shared by the two processes. Before a process can write a record to the array, it must lock ^index and update its current value (SET ^index=^index+1). If the other process is already in this section of the code, ^index will be locked and the process will have to wait until the other process releases the lock (with the argumentless LOCK command).
 READ !,"Last name: ",!,lname QUIT:lname=""  SET lname=lname_"," 
 READ !,"First name: ",!,fname QUIT:fname=""  SET fname=fname_"," 
 READ !,"Middle initial: ",!,minit QUIT:minit=""  SET minit=minit_":" 
 READ !,"Student ID Number: ",!,sid QUIT:sid=""
 SET rec = lname_fname_minit_sid
 LOCK ^index
 SET ^index = ^index + 1
 SET ^student(^index)=rec
The following example recasts the previous example to use locking on the node to be added to the ^student array. Only the affected portion of the code is shown. In this case, the ^index variable is updated after the new student record is added. The next process to add a record will use the updated index value to write to the correct array node.
 LOCK ^student(^index)
 SET ^student(^index) = rec
 SET ^index = ^index + 1
 LOCK  /* release all locks */
Note that the lock location of an array node is where the top level global is mapped. Caché ignores subscripts when determining lock location. Therefore, ^student(name) is mapped to the namespace of ^student, regardless of where the data for ^student(name) is stored.
Locks in a Network
In a networked system, one or more servers may be responsible for resolving locks on global variables.
You can use the LOCK command with any number of servers, up to 255. A LOCK command can specify a maximum of 8 remote systems, and a maximum of 20 locks for each remote system.
You can use ^$LOCK to list remote locks, but it cannot list the lock state of a remote lock.
Remote locks held by a client job on a remote server system are released when you call the ^RESJOB utility to remove the client job.
Local Variable Locks
The behavior of LOCK on local (non-careted) variables is the same on Caché and Open M [DTM]. The Config.Miscellaneous class provides a ZaMode property that you can use to set the locking mode system-wide for compatibility with the older DSM–11 ZALLOCATE (ZA) and ZDEALLOCATE (ZD) locking commands.
In Open M [DTM] local locks are always taken out in the manager's dataset regardless of the first character. If an application ported to Caché uses local locks that do not begin with a % character, the application may experience deadlock conditions if it uses the same, local, non-% lock names in different datasets. The locks now collide because they resolve to the same item. If this is part of a hierarchical locking system, a deadly embrace may occur that can hang an application.
The current behavior is:
Referencing explicit and implied namespaces is further described in Global Structure in Using Caché Globals.
See Also

Send us comments on this page
Copyright © 1997-2019 InterSystems Corporation, Cambridge, MA