Written by

Question Matjaz Murko · 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

Comments

Matjaz Murko · Sep 29, 2021

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ž

0
Lorenzo Scalese  Sep 29, 2021 to Matjaz Murko

Ok,

Could you test with

Set checking = "" ; due to the reference Set checking.Person
Set person = ""

I guess the lock should be released after leaving the classmethod, but if the class is procedure block or not the behavior could be different.

0
Matjaz Murko  Sep 29, 2021 to Lorenzo Scalese

These are my findings:
- class has ProcedureBlock enabled by default,
- if I run the class method from terminal window, it behaves as a procedure block,
- if I run it from IRIS Native API (C# app) then it behaves as non procedure block and I have to set person & checking instances to null to release the lock.

0
Lorenzo Scalese  Sep 29, 2021 to Matjaz Murko

Interesting.
I don't have environment to test wit Native API, but perhaps you could test explicitly "ProcedureBlock" instead of default behavior. Just by using the class key word "ProcedureBlock" ex:

Class ZUser.NewClass1 Extends %Persistent [ ProcedureBlock ]

edit : also you can try with [ ProcedureBlock = 1 ] on the classmethod. doc link

0
Matjaz Murko  Sep 29, 2021 to Lorenzo Scalese

I do not know if it is important, but I'm using abstract class. And although I explicitly add ProcedureBlock  to the class definition it doesn't help when I'm calling the method with Native API...

0
Lorenzo Scalese  Sep 29, 2021 to Matjaz Murko

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
	
	
}
}
0
Vitaliy Serdtsev  Sep 29, 2021 to Lorenzo Scalese

And if so?

<FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">TestLock() </FONT><FONT COLOR="#000080">As %Status
</FONT><FONT COLOR="#000000">{
    </FONT><FONT COLOR="#0000ff">New </FONT><FONT COLOR="#800000">id</FONT><FONT COLOR="#000000">,</FONT><FONT COLOR="#800000">obj

    </FONT><FONT COLOR="#0000ff">Set </FONT><FONT COLOR="#800000">id </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#0000ff">$Order</FONT><FONT COLOR="#000000">(^ZUser.NewClass1D(</FONT><FONT COLOR="#008000">""</FONT><FONT COLOR="#000000">))     </FONT><FONT COLOR="#0000ff">If </FONT><FONT COLOR="#800000">id </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#008000">"" </FONT><FONT COLOR="#800080">{         </FONT><FONT COLOR="#0000ff">Set </FONT><FONT COLOR="#800000">obj </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">ZUser.NewClass1</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">%New</FONT><FONT COLOR="#000000">()         </FONT><FONT COLOR="#0000ff">Do </FONT><FONT COLOR="#800000">obj</FONT><FONT COLOR="#000000">.</FONT><FONT COLOR="#0000ff">%Save</FONT><FONT COLOR="#000000">()     </FONT><FONT COLOR="#800080">}

    </FONT><FONT COLOR="#0000ff">Set </FONT><FONT COLOR="#800000">id </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#0000ff">$Order</FONT><FONT COLOR="#000000">(^ZUser.NewClass1D(</FONT><FONT COLOR="#008000">""</FONT><FONT COLOR="#000000">))

    </FONT><FONT COLOR="#0000ff">Set </FONT><FONT COLOR="#800000">obj </FONT><FONT COLOR="#000000">= </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">ZUser.NewClass1</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">%OpenId</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">id</FONT><FONT COLOR="#000000">, 4)

    </FONT><FONT COLOR="#008000">; 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

    </FONT><FONT COLOR="#0000ff">Return $$$OK </FONT><FONT COLOR="#000000">}</FONT>

0
Matjaz Murko  Sep 29, 2021 to Vitaliy Serdtsev

Hi.
Tnx for reply.
Regardless that I'm using ProcedureBlock, Native API calls to class methods do not put instances out of scope (and consequently do not release locks).
Regards, Matjaž.

0
Vitaliy Serdtsev  Sep 29, 2021 to Matjaz Murko
  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();
0
Lorenzo Scalese  Sep 29, 2021 to Vitaliy Serdtsev

Sure, with the `New` lock will be released.

0
Matjaz Murko · Sep 29, 2021

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();
            }

0
Vitaliy Serdtsev  Sep 30, 2021 to Matjaz Murko

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

COS

<FONT COLOR="#000080">Class dc.test Extends %Persistent
</FONT><FONT COLOR="#000000">{

</FONT><FONT COLOR="#000080">ClassMethod </FONT><FONT COLOR="#000000">Checking(</FONT><FONT COLOR="#ff00ff">CardNo </FONT><FONT COLOR="#000080">As %String</FONT><FONT COLOR="#000000">) </FONT><FONT COLOR="#000080">As %String </FONT><FONT COLOR="#000000">{   </FONT><FONT COLOR="#0000ff">s </FONT><FONT COLOR="#800000">tmp</FONT><FONT COLOR="#000000">=..</FONT><FONT COLOR="#0000ff">%OpenId</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#800000">CardNo</FONT><FONT COLOR="#000000">,4)   </FONT><FONT COLOR="#0000ff">q </FONT><FONT COLOR="#008000">"test" </FONT><FONT COLOR="#000000">}

}</FONT>

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

USER><FONT COLOR="#0000ff">w </FONT><FONT COLOR="#000080">##class</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008080">dc.test</FONT><FONT COLOR="#000000">).</FONT><FONT COLOR="#0000ff">Checking</FONT><FONT COLOR="#000000">(</FONT><FONT COLOR="#008000">"1"</FONT><FONT COLOR="#000000">)</FONT>
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
0
Matjaz Murko  Oct 2, 2021 to Vitaliy Serdtsev

I'm setting all referenced instances to null in COS to release locks as workaround. But I'm not sure it is a bug or expected behaviour...

0
Julius Kavay · Oct 2, 2021

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

0
Matjaz Murko  Oct 3, 2021 to Julius Kavay

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ž

0
Vitaliy Serdtsev  Oct 4, 2021 to Matjaz Murko

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.

0
Matjaz Murko  Oct 4, 2021 to Vitaliy Serdtsev

That's the way the proxy classes are  functioning (caching). 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.

0
Vitaliy Serdtsev  Oct 4, 2021 to Matjaz Murko
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.

0