PACKAGE BODY printer_access IS /* global variables for lock name and handle */ printer_lockname VARCHAR2(128) := 'printer_lock'; printer_lockhandle VARCHAR2(128); FUNCTION get_printer_lockhandle RETURN VARCHAR2 IS BEGIN IF printer_lockhandle IS NULL THEN DBMS_LOCK.ALLOCATE_UNIQUE (lockname => printer_lockname ,lockhandle => printer_lockhandle); END IF; RETURN printer_lockhandle; END get_printer_lockhandle; END printer_access; As illustrated in the example, it is a good idea to call ALLOCATE_UNIQUE only once for any given lockname per session. This is why the function stashes the lockhandle in the global variable, printer_lockhandle, and calls ALLOCATE_UNIQUE only if this global has not been initialized. There are two reasons for using this technique: efficiency and avoidance of extra COMMITs. Remember that ALLOCATE_UNIQUE will always return the same handle for a given lockname and that it always performs a COMMIT. Thus, best practice for using DBMS_LOCK includes calling ALLOCATE_UNIQUE only once per named lock. Locks allocated using ALLOCATE_UNIQUE can be viewed in the Oracle data dictionary via the DBMS_LOCK_ALLOCATED view. It is good practice to avoid the possibility of lockname conflicts between applications by adopting standard naming conventions for locknames. Just as Oracle reserves names that begin with "ORA$", you may want to prefix locknames with your own company and application identifier string. 4.1.2.2 The DBMS_LOCK.REQUEST function The REQUEST function is used to acquire a lock in the mode specified by the lockmode parameter. If the lock cannot be acquired in the requested mode within the specified time, the function call completes with a nonzero return value (see the parameter table). The REQUEST function is overloaded on the first parameter, which is used to identify the lock by either an INTEGER identifier or by a VARCHAR2 lockhandle. The release_on_commit parameter indicates whether the lock should persist across RDBMS transactions or be automatically released upon COMMIT or ROLLBACK. The headers for this program, corresponding to each type, are as follows: FUNCTION DBMS_LOCK.REQUEST (id IN INTEGER ,lockmode IN INTEGER DEFAULT X_MODE ,timeout IN INTEGER DEFAULT MAXWAIT ,release_on_commit IN BOOLEAN DEFAULT FALSE) RETURN INTEGER; FUNCTION DBMS_LOCK.REQUEST (lockhandle IN VARCHAR2 ,lockmode IN INTEGER DEFAULT X_MODE ,timeout IN INTEGER DEFAULT MAXWAIT ,release_on_commit IN BOOLEAN DEFAULT FALSE) RETURN INTEGER; [Appendix A] What's on the Companion Disk? 4.1.2 The DBMS_LOCK Interface 206 Parameters for this function are summarized in the following table. Parameter Description id Numeric identifier of the lock lockhandle Handle for lock returned by DBMS_LOCK.ALLOCATE_UNIQUE lockmode Locking mode requested for lock timeout Time in seconds to wait for successful conversion release_on_commit If TRUE, release lock automatically on COMMIT or ROLLBACK The following table summarizes the return values of the function. Return Value Description 0 Success 1 Timed out 2 Deadlock 3 Parameter error 4 Do not own lock; cannot convert 5 Illegal lockhandle The program does not raise any package exceptions. 4.1.2.2.1 Restrictions User−defined lock identifiers must be in the range 0 to 1073741823. Lock identifiers in the range 2000000000 to 2147483647 are reserved for use by Oracle Corporation. 4.1.2.2.2 Example The following procedure calls the REQUEST function to get exclusive access to a lock designated to serialize access to a printer by Oracle sessions. It uses the get_printer_lockhandle function (see the example for the ALLOCATE_UNIQUE procedure) to identify the correct value for the lockhandle parameter. PROCEDURE lock_printer (return_code_OUT OUT INTEGER) IS /* initialize variable with desired lockhandle */ temp_lockhandle printer_lockhandle%TYPE := get_printer_lockhandle; call_status INTEGER; BEGIN /* || lock in exclusive mode, wait for up to 5 seconds */ call_status := DBMS_LOCK.REQUEST (lockhandle => temp_lockhandle ,lockmode => DBMS_LOCK.x_mode ,timeout => 5 ,release_on_commit => TRUE); return_code_OUT := call_status; END lock_printer; It is safest to use the form of REQUEST that identifies the lock by a lockhandle (returned by ALLOCATE_UNIQUE). This minimizes the potential for inadvertent use of the same lock by different [Appendix A] What's on the Companion Disk? 4.1.2 The DBMS_LOCK Interface 207 applications for different purposes, which is possible when locks are identified by integer values chosen by the application. Sessions connected to Oracle using the multithreaded server configuration will not be released from their shared server until all held locks are released. Thus, be careful of specifying FALSE for the release_on_commit parameter in MTS (multithreaded server) environments, as holding locks for long periods could have a negative impact on MTS efficiency. Be sure that distributed transactions specify TRUE for the release_on_commit parameter. If a distributed transaction does not release locks after COMMIT, it is possible for a distributed deadlock to occur, which will be undetectable by either of the databases involved. When two sessions request locks with modes resulting in a deadlock, this is detected by Oracle, and one of the sessions is notified of the deadlock status. 4.1.2.3 The DBMS_LOCK.CONVERT function The CONVERT function is used to convert a previously acquired lock to the mode specified by the lockmode parameter. If the mode conversion cannot be granted within the specified time, the function call completes with a nonzero return value (see the following parameter table). CONVERT is overloaded on the first parameter, which is used to identify the lock by either an INTEGER identifier or a VARCHAR2 lockhandle. The headers for this program, corresponding to each type, follow: FUNCTION DBMS_LOCK.CONVERT (id IN INTEGER ,lockmode IN INTEGER ,timeout IN NUMBER DEFAULT MAXWAIT) RETURN INTEGER; FUNCTION DBMS_LOCK.CONVERT (lockhandle IN VARCHAR2 ,lockmode IN INTEGER ,timeout IN NUMBER DEFAULT MAXWAIT) RETURN INTEGER; Parameters for this program are summarized in the following table. Parameter Description id Numeric identifier of the lock lockhandle Handle for lock returned by ALLOCATE_UNIQUE lockmode Locking mode to which to convert the lock timeout Time in seconds to wait for successful conversion The return values for this function are summarized in the following table. Return Value Description 0 Success 1 Timed out 2 Deadlock 3 Parameter error 4 Do not own lock, cannot convert 5 Illegal lockhandle [Appendix A] What's on the Companion Disk? 4.1.2 The DBMS_LOCK Interface 208 The program does not raise any package exceptions. 4.1.2.3.1 Restrictions User−defined lock identifiers must be in the range 0 to 1073741823. Lock identifiers in the range 2000000000 to 2147483647 are reserved for use by Oracle Corporation. 4.1.2.3.2 Example The following anonymous PL/SQL block converts a previously acquired lock to null mode, reporting success or failure to the screen: DECLARE call_status INTEGER; BEGIN /* convert lock 9999 down to null mode with no wait */ call_status := DBMS_LOCK.CONVERT(9999,DBMS_LOCK.nl_mode,0); IF call_status = 0 THEN DBMS_OUTPUT.PUT_LINE('SUCCESS'); ELSE DBMS_OUTPUT.PUT_LINE('FAIL, RC = '||TO_CHAR(call_status)); END IF; END; See the discussion in the "Section 4.1.2.2.2, "Example"" section for the Section 4.1.2.2, "The DBMS_LOCK.REQUEST function"; all of that discussion also applies to CONVERT. 4.1.2.4 The DBMS_LOCK.RELEASE function The RELEASE function releases a previously acquired lock. RELEASE is overloaded on the first parameter, which is used to identify the lock by either an INTEGER identifier or a VARCHAR2 lockhandle. The program headers for each corresponding type follow: FUNCTION DBMS_LOCK.RELEASE (id IN INTEGER) RETURN INTEGER; FUNCTION DBMS_LOCK.RELEASE (lockhandle IN VARCHAR2) RETURN INTEGER; Parameters are summarized in the following table. Parameter Description id Numeric identifier of the lock lockhandle Handle for lock returned by ALLOCATE_UNIQUE The return values for this function are summarized in the following table. Return Value Description 0 Success 3 Parameter error 4 Do not own lock; cannot release 5 Illegal lockhandle [Appendix A] What's on the Companion Disk? 4.1.2 The DBMS_LOCK Interface 209 The program does not raise any package exceptions. 4.1.2.4.1 Restrictions User−defined lock identifiers must be in the range 0 to 1073741823. Lock identifiers in the range 2000000000 to 2147483647 are reserved for use by Oracle Corporation. 4.1.2.4.2 Example The following procedure calls the RELEASE function to relinquish control of the printer lock (see also the example for the REQUEST function): PROCEDURE release_printer (return_code_OUT OUT INTEGER) IS /* initialize variable with desired lockhandle */ temp_lockhandle printer_lockhandle%TYPE := get_printer_lockhandle; call_status INTEGER; BEGIN /* || release the printer lock */ call_status := DBMS_LOCK.RELEASE (lockhandle => temp_lockhandle); return_code_OUT := call_status; END release_printer; It is good practice to release locks as soon as possible. Doing so minimizes the potential for unnecessary wait times or deadlocks in applications where concurrent access to resources is serialized using DBMS_LOCK. 4.1.2.5 The DBMS_LOCK.SLEEP procedure The SLEEP procedure suspends the session for the number of seconds specified in the seconds parameter. Sleep periods can be specified with accuracy down to the hundredth of a second (e.g., 1.35 and 1.29 are recognized as distinct sleep times). Here's the header for this program: PROCEDURE DBMS_LOCK.SLEEP (seconds IN NUMBER); 4.1.2.5.1 Exceptions This program does not raise any package exceptions. WARNING: The following nasty Oracle exception was raised on Windows NT when the SLEEP procedure was called with a NULL value for seconds: ORA−00600: internal error code, arguments: [15454], [0], [ ], [ ], [ ], [ ], [ ], [ ]. 4.1.2.5.2 Restrictions Do not specify a null value for the seconds parameter; this may result in an ORA−00600 error, as noted previously. [Appendix A] What's on the Companion Disk? 4.1.2 The DBMS_LOCK Interface 210 . for use by Oracle Corporation. 4.1.2.2.2 Example The following procedure calls the REQUEST function to get exclusive access to a lock designated to serialize access to a printer by Oracle sessions lockname conflicts between applications by adopting standard naming conventions for locknames. Just as Oracle reserves names that begin with "ORA$", you may want to prefix locknames with your. ALLOCATE_UNIQUE only once per named lock. Locks allocated using ALLOCATE_UNIQUE can be viewed in the Oracle data dictionary via the DBMS_LOCK_ALLOCATED view. It is good practice to avoid the possibility