Skip to main content
Previous sectionNext section

Object Concurrency Options

Object Concurrency Options

It is important to specify concurrency appropriately when you open or delete objects. You can specify concurrency at several different levels:

  1. You can specify the concurrency argument for the method that you are using.

    Many of the methods of the %Persistent class allow you to specify this argument, an integer. This argument determines how locks are used for concurrency control. A later subsection lists the allowed values.

    If you do not specify the concurrency argument, InterSystems IRIS uses the value of the DEFAULTCONCURRENCY class parameter for the class you are working with; see the next item.

  2. You can specify the DEFAULTCONCURRENCY class parameter for the associated class. All persistent classes inherit this parameter from %Persistent, which defines the parameter as an expression that obtains the default concurrency for the process; see the next item.

    You could override this parameter in your class and specify a hardcoded value or an expression that determines the concurrency via your own rules. In either case, the value of the parameter must be one of the allowed concurrency values discussed later in this section.

  3. You can set the default concurrency mode for a process. To do so, use the $system.OBJ.SetConcurrencyMode() method (which is the SetConcurrencyMode() method of the %SYSTEM.OBJ class).

    As in the other cases, you must use one of the allowed concurrency values. If you do not set the concurrency mode for a process explicitly, the default value is 1.

    The $system.OBJ.SetConcurrencyMode() method has no effect on any classes that specify an explicit value for the DEFAULTCONCURRENCY class parameter.

Why Specify Concurrency?

The following scenario demonstrates why it is important to control concurrency appropriately when you read or write objects. Consider the following scenario:

  1. Process A opens an object without specifying the concurrency.

    SAMPLES>set person = ##class(Sample.Person).%OpenId(5)
     
    SAMPLES>write person
    1@Sample.Person
    Copy code to clipboard
  2. Process B opens the same object with the concurrency value of 4.

    SAMPLES>set person = ##class(Sample.Person).%OpenId(5, 4)
     
    SAMPLES>write person
    1@Sample.Person
    Copy code to clipboard
  3. Process A modifies a property of the object and attempts to save it using %Save() and receives an error status.

    SAMPLES>do person.FavoriteColors.Insert("Green")
    
    SAMPLES>set status = person.%Save()
     
    SAMPLES>do $system.Status.DisplayError(status)
     
    ERROR #5803: Failed to acquire exclusive lock on instance of 'Sample.Person'
    Copy code to clipboard

This is an example of concurrent operations without adequate concurrency control. For example, if process A could possibly save the object back to the disk, it must open the object with concurrency 4 to ensure it can save the object without conflict with other processes. (These values are discussed later in this chapter.) In this case, Process B would then be denied access (failed with a concurrency violation) or would have to wait until Process A releases the object.

Concurrency Values

This section describes the possible concurrency values. First, note the following details:

  • Atomic writes are guaranteed when concurrency is greater than 0.

  • InterSystems IRIS acquires and releases locks during operations such as saving and deleting objects; the details depend upon the concurrency value, what constraints are present, lock escalation status, and the storage structure.

  • In all cases, when an object is removed from memory, any locks for it are removed.

The possible concurrency values are as follows; each value has a name, also shown in the list.

Concurrency Value 0 (No locking)

No locks are used.

Concurrency Value 1 (Atomic read)

Locks are acquired and released as needed to guarantee that an object read will be executed as an atomic operation.

InterSystems IRIS does not acquire any lock when creating a new object.

While opening an object, InterSystems IRIS acquires a shared lock for the object, if that is necessary to guarantee an atomic read. InterSystems IRIS releases the lock after completing the read operation.

The following table lists the locks that are present in each scenario:

  When object is created While object is being opened After object has been opened After save operation is complete
New object no lock N/A N/A no lock
Existing object N/A shared lock, if that is necessary to guarantee an atomic read no lock no lock
Concurrency Value 2 (Shared locks)

The same as 1 (atomic read) except that opening an object always acquires a shared lock (even if the lock is not needed to guarantee an atomic read). The following table lists the locks that are present in each scenario:

  When object is created While object is being opened After object has been opened After save operation is complete
New object no lock N/A N/A no lock
Existing object N/A shared lock no lock no lock
Concurrency Value 3 (Shared/retained locks)

InterSystems IRIS does not acquire any lock when creating a new object.

While opening an existing object, InterSystems IRIS acquires a shared lock for the object.

After saving a new object, InterSystems IRIS has a shared lock for the object.

The following table lists the scenarios:

  When object is created While object is being opened After object has been opened After save operation is complete
New object no lock N/A N/A shared lock
Existing object N/A shared lock shared lock shared lock
Concurrency Value 4 (Exclusive/retained locks)

When an existing object is opened or when a new object is first saved, InterSystems IRIS acquires an exclusive lock.

The following table lists the scenarios:

  When object is created While object is being opened After object has been opened After save operation is complete
New object no lock N/A N/A exclusive lock
Existing object N/A exclusive lock exclusive lock exclusive lock

Concurrency and Swizzled Objects

An object referenced by a property is swizzled on access using the default concurrency defined by the swizzled object’s class. If the default is not defined for the class, the object is swizzled using the default concurrency mode of the process. The swizzled object does not use the concurrency value of the object that swizzles it.

If the object being swizzled is already in memory, then swizzling does not actually open the object — it simply references the existing in-memory object; in that case, the current state of the object is maintained and the concurrency is unchanged.

There are two ways to override this default behavior:

  • Upgrade the concurrency on the swizzled object with a call to the %Open() method that specifies the new concurrency. For example:

     Do person.Spouse.%OpenId(person.Spouse.%Id(),4,.status) 
    Copy code to clipboard

    where the first argument to %OpenId() specifies the ID, the second specifies the new concurrency, and the third (passed by reference) receives the status of the method.

  • Set the default concurrency mode for the process before swizzling the object. For example:

     Set olddefault = $system.OBJ.SetConcurrencyMode(4) 
    Copy code to clipboard

    This method takes the new concurrency mode as its argument and returns the previous concurrency mode.

    When you no longer need a different concurrency mode, reset the default concurrency mode as follows:

     Do $system.OBJ.SetConcurrencyMode(olddefault)
    Copy code to clipboard
Feedback