Question
· Sep 29, 2021

Locking / unlocking

Hi.

What is the best/right way to unlock the object in ObjectScript after the lock was acquired by %OpenId class method? Setting the opened object to null (Set object="") doesn't release the lock.

Regards,

Matjaž

Product version: IRIS 2021.1
Discussion (19)1
Log in or sign up to continue

Hi.

Tnx for your quick reply.

Here is the class method:

ClassMethod Checking(CardNo As %String) As %String [ Language = objectscript ]
{
Set person=##class(MasterData.Person).CardNoOpen(CardNo,4)
Quit:person="" "null"

Set checking=##class(Production.Checking).%New()
Set checking.Person=person
Set checking.Condition.Type=person.Condition.Type
Set checking.Condition.ValidBy=person.Condition.ValidBy
Set checking.ConditionIsValid=(person.Condition.ValidBy>=+$Horolog)
Do checking.%Save()

Set model=##class(API.Models.Operative.PersonGetData).%New()
Set model.Ident=person.Ident
Set model.FirstName=person.FirstName
Set model.LastName=person.LastName
Set model.Department=person.Department.Name
Set model.IsValid=checking.ConditionIsValid

If ('checking.ConditionIsValid)
{
Set person.Condition.Type="T"
Set person.Condition.ValidBy=+$Horolog+6
Do person.%Save()
}

Do model.%JSONExportToString(.json)

Do ##class(MasterData.Person).%UnlockId(person.%Id())

Quit json
}

I'm using now %UnlockId method instead of setting the object to null.

Regards,
Matjaž

Code snipet to expose the problem.

Class ZUser.NewClass1 Extends %Persistent [ Not ProcedureBlock ]
{

ClassMethod Demo()
{

    Do ..TestLock()
    ; This class is Not ProcedureBlock the record still locked

    ; If the class is ProcedureBlock the record is released.
    ; try by yourself :-)
}

ClassMethod TestLock() As %Status
{
    Set id = $Order(^ZUser.NewClass1D(""))
    If id = "" {
        Set obj = ##class(ZUser.NewClass1).%New()
        Do obj.%Save()
        Kill obj
    }

    Set id = $Order(^ZUser.NewClass1D(""))

    Set obj = ##class(ZUser.NewClass1).%OpenId(id, 4)

    ; in case of usage Not ProcedureBlock you should 
    ; kill obj or set obj="" (and all others variables with this object reference) to release the lock


    Return $$$OK


}
}

And if so?

ClassMethod TestLock() As %Status
{
    New id,obj

    Set id $Order(^ZUser.NewClass1D(""))
    If id "" {
        Set obj ##class(ZUser.NewClass1).%New()
        Do obj.%Save()
    }

    Set id $Order(^ZUser.NewClass1D(""))

    Set obj ##class(ZUser.NewClass1).%OpenId(id, 4)

    ; in case of usage Not ProcedureBlock you should 
    ; kill obj or set obj="" (and all others variables with this object reference) to release the lock

    Return $$$OK
}
  1. ProcedureBlock does not apply to your issue at all
  2. you didn't initially indicate that the issue is with C#
  3. use ReleaseIRISObjects

    Example:

    IRISConnection db; IRIS iris; IRISObject person;   // ...   using (person = (IRISObject)iris.ClassMethodObject("Sample.Person""%OpenId""1""4")) {   }   /* or   person = (IRISObject)iris.ClassMethodObject("Sample.Person", "%OpenId", "1", "4") person.Dispose(); person = null; */   db.ReleaseIRISObjects();

I'm not using IRISObjects, just ClassMethodString:

PersonData = JsonSerializer.Deserialize<PersonDataClass>(App.Iris.ClassMethodString("API.Calls.Operative", "Checking", CardNo.Text));
            if (PersonData != null)
            {
                if (!PersonData.IsValid)
                {
                    SystemSounds.Exclamation.Play();
            }

I did a test and that's what found out.
It is assumed that the data has already been generated.

COS

Class dc.test Extends %Persistent
{

ClassMethod Checking(CardNo As %StringAs %String
{
  tmp=..%OpenId(CardNo,4)
  "test"
}

}

If executed from the terminal, the lock is removed automatically immediately after the method is completed:

USER>##class(dc.test).Checking("1")
test

C#

// ...
string json = iris.ClassMethodString("dc.test""Checking""1");
 
// here the lock is still present
 
db.ReleaseIRISObjects(); // or iris.ReleaseAllLocks();
 
// here already more is no lock

There is one thing you should check, than this could trigger effects observed by you.
Objects are tracked by reference counts, as long as an objects reference count is greater then one, locks won't be released and the object isn't deleted.

set obj = ##class(Some.Class).%OpenId(id, 4) // the obj's ref count is one
... // more commands
    // now, the application does something like this
set tmp = obj    // obj's ref count is now two!
... // more commands

set obj = "" // the application intents to close the object
             // but the object still exists due to the fact that the ref count is one
             // (the object is still referenced by <tmp>)

There are methods to detect such a situation:
- $system.OBJ.ShowObjects(), lists all objects with reference counts
- $system.OBJ.ShowReferences(obj), list all variables which contains a reference to <obj>

A quick and dirty approach:

set filename = "...some file name"
open filename:"nw":0
if $t { use filename
        do $system.ShowObjects()
        do $system.ShowReferences(obj) 
        close filename
      }
set obj = "" 

Give it a try, maybe your object has multiple references which cause the problem

Hi.

As I've mentioned in previous post I'm using workaround, but I think it's a bug in API, because the same method works fine in terminal window without explicite set the objects to null (all used objects are released and consequently all locks are released also - expected behaviour in class with procedure block enabled).

Regards,
Matjaž

Yes, most likely this is a feature of the API.
Here's what I found about it in the "old" documentation:

In some situations, caching may not be desirable. For example, if an object is opened with Concurrency Level 4 (Exclusive Lock), the lock will not be released until the next server call. To destroy the object immediately, you can call Close() with the optional useCache parameter set to false

Closing Proxy Objects

PS: by the way, for ActiveX, Factory.ForceSync() method serves the same purpose.

But in new Native API for IRIS there are no proxies, just class methods/functions are called from app and I think, it should be pretty the same as calling this methods/functions from terminal window.

Introduction to the Native API

Using .NET Reverse Proxy Objects

The Native API allows you to create instances of ObjectScript classes on InterSystems IRIS and generate Object Gateway proxy objects for them at runtime. Your .NET application can use proxies to work with ObjectScript objects as easily as if they were native .NET objects.