Alaska Software Inc. - DataObject xbase 2.0 726
Username: Password:
AuthorTopic: DataObject xbase 2.0 726
Raffaele LafrattaDataObject xbase 2.0 726
on Thu, 13 Oct 2016 09:18:57 +0200
I use DataObject in a large multithreaded applications ( webserver )

I use a Dataobject for every session and every thread

Dataobject is previously created with some members and then new members 
are added during application execution
It happens that   adding or setting a member, applications hangs

Ex.
function Initsession()

local oInfo:=DataObject():New()
   oInfo:session       :=cIdSession
   oInfo:user          :="XXX"
   oInfo:lingua        :=1
   oInfo:collation     :=SetCollation()
   oInfo:codepage      :=0
   oInfo:userPath      :=""
   oInfo:userFiles     :=""
   oInfo:usage         :=0
   oInfo:Started       :=MilliSeconds()
   oInfo:forms         :={}        aDDING THIS MEMBER HERE application 
hangs .. commented it applications goes well


return oInfo
.......................
...................

function update

local oInfo:=Xsession()  retreive session informations


oInfo:newmember :="...."  ---> application hangs
Andreas Gehrs-Pahl
Re: DataObject xbase 2.0 726
on Thu, 13 Oct 2016 16:02:25 -0400
Raffaele,

>I use DataObject in a large multithreaded applications ( webserver )
>I use a Dataobject for every session and every thread

DataObjects are not "thread-safe" in the way you are apparently trying to 
use them. If a DataObject is accessed from more than one thread at the same 
time, the application will hang, because each thread is waiting for the 
other one(s) to release the object.

If you want to use DataObjects the way your code seems to use them, you need 
to implement some sort of thread-safe wrapper for them yourself, for example 
using a custom class with a Synch(ed) Access/Assign (Class) Method.

Example (pseudo) code:

Class AppInfo
   Exported:
   Class Var oInfo

   Class Method Init

   Sync Access Assign Class Method oInfo
EndClass

Class Method AppInfo:Init()
   ::oInfo := DataObject():New()
return (Self)

Class Method AppInfo:oInfo(oInfo)
   if PCount() == 1
      ::oInfo := oInfo
   endif
return (::oInfo)

And then use it like this:

Function InitSession()
LOCAL oApp := AppInfo():New()
   oApp:oInfo:session   := cIdSession
   oApp:oInfo:user      := "XXX"
   oApp:oInfo:lingua    := 1
   oApp:oInfo:collation := SetCollation()
   oApp:oInfo:codepage  := 0
   oApp:oInfo:userPath  := ""
   oApp:oInfo:userFiles := ""
   oApp:oInfo:usage     := 0
   oApp:oInfo:Started   := MilliSeconds()
   oApp:oInfo:forms     := {}
return (oApp)

Function Update()
LOCAL oApp := Xsession()  retrieve session information
   oApp:oInfo:NewMember := "Some Value"
[...]

That should basically work and the application shouldn't hang anymore.

Hope that helps,

Andreas

Andreas Gehrs-Pahl
Absolute Software, LLC

phone: (989) 723-9927
email: Andreas@AbsoluteSoftwareLLC.com
web:   http://www.AbsoluteSoftwareLLC.com
[F]:   https://www.facebook.com/AbsoluteSoftwareLLC
Raffaele LafrattaRe: DataObject xbase 2.0 726
on Fri, 14 Oct 2016 11:45:28 +0200
Il 13/10/2016 22.02, Andreas Gehrs-Pahl ha scritto:
> Raffaele,
>
>> I use DataObject in a large multithreaded applications ( webserver )
>> I use a Dataobject for every session and every thread
>
> DataObjects are not "thread-safe" in the way you are apparently trying to
> use them. If a DataObject is accessed from more than one thread at the same
> time, the application will hang, because each thread is waiting for the
> other one(s) to release the object.
>
> If you want to use DataObjects the way your code seems to use them, you need
> to implement some sort of thread-safe wrapper for them yourself, for example
> using a custom class with a Synch(ed) Access/Assign (Class) Method.
>
> Example (pseudo) code:
>
> Class AppInfo
>    Exported:
>    Class Var oInfo
>
>    Class Method Init
>
>    Sync Access Assign Class Method oInfo
> EndClass
>
> Class Method AppInfo:Init()
>    ::oInfo := DataObject():New()
> return (Self)
>
> Class Method AppInfo:oInfo(oInfo)
>    if PCount() == 1
>       ::oInfo := oInfo
>    endif
> return (::oInfo)
>
> And then use it like this:
>
> Function InitSession()
> LOCAL oApp := AppInfo():New()
>    oApp:oInfo:session   := cIdSession
>    oApp:oInfo:user      := "XXX"
>    oApp:oInfo:lingua    := 1
>    oApp:oInfo:collation := SetCollation()
>    oApp:oInfo:codepage  := 0
>    oApp:oInfo:userPath  := ""
>    oApp:oInfo:userFiles := ""
>    oApp:oInfo:usage     := 0
>    oApp:oInfo:Started   := MilliSeconds()
>    oApp:oInfo:forms     := {}
> return (oApp)
>
> Function Update()
> LOCAL oApp := Xsession()  retrieve session information
>    oApp:oInfo:NewMember := "Some Value"
> [...]
>
> That should basically work and the application shouldn't hang anymore.
>
> Hope that helps,
>
> Andreas
>
Thanks Andrea

I'll try it soon.

Raffaele
Andreas Gehrs-Pahl
Re: DataObject xbase 2.0 726
on Tue, 18 Oct 2016 09:36:23 -0400
Raffaele,

I wrote:

>That should basically work and the application shouldn't hang anymore.

It actually doesn't work! I have even tried to get this to work by using 
an Abstract Class with Synch(ed) SetNoIVar() / GetNoIVar() methods and an 
internal, protected DataObject, but it still hangs when adding a new iVar 
to the internal DataObject, even when it is done inside a Synch Method, 
resulting in all the threads waiting for each other. This hang-up seems to 
happen whenever you try to add a new Instance Variable to a DataObject which 
already has several Instance Variables, and which has been "used" in (or 
accessed from) multiple threads, no matter in which thread you try to add 
the new Instance Variable. 

I would characterize this as a bug and you might want to contact Alaska and 
have them create a PDR for this issue.

As a workaround, I have written my own substitute DataObject class, called 
AGP_DataObject(), which will fix those issues by simply using two internal 
Arrays for Instance Variables and Methods. The attached source code 
implements this class and some tests.

This implementation uses Sync Methods, so it is completely thread-safe, 
even when executing a dynamic Method. The entire method will be executed 
before a different thread has access to the data of the object, making sure 
that the data integrity isn't compromised. This might be an issue if a 
method takes a long time to complete, but data integrity is more important 
to me than raw speed.

And the most surprising thing is that using AGP_DataObjects is virtually 
as fast as using standard DataObjects, at least when the number of iVars 
is relatively small.

The AGP_DataObject class also includes a method named :DataObject() that 
creates/exports a genuine DataObject with the exact same structure as the 
one the AGP_DataObject is emulating.

The :Merge() method can be used to initialize/create (or update) a new or 
existing AGP_DataObject from another object -- either from a DataObject or 
from another AGP_DataObject. 

Also, the :Copy() method works differently than the :Copy() method of the 
standard DataObject class, and creates a non-dependent non-shallow copy of 
the object. Additionally, the :ClassDescribe() method has an option to 
create a result similar to the DataObject class.

I also included methods to remove dynamic/virtual Instance Variables as well 
as dynamic/virtual Methods, and to sort them, mainly for aesthetic reasons.

The one thing that I couldn't implement is access to Instance Variables via 
the (Array) Index Operator, as in: oDataObject[n], which returns the value 
of the n-th Instance Variable of the oDataObject. Even though I think this 
is a completely useless (and dangerous) coding style -- as you normally 
don't know (for sure) in which order Instance Variables will be (or have 
been) created -- some Xbase++ routines rely on this feature. This includes 
the Var2XML() function, which uses this feature because it is apparently 
faster than accessing the iVars by name. I'm also not sure how this index 
feature affects purely virtual iVars, which are implemented by using only 
Access/Assign Methods.

Anyway, if you want to use this "access via index" feature, you can always 
create a genuine DataObject from the AGP_DataObject with the :DataObject() 
method.

Until Alaska implements a thread-safe DataObject class, my AGP_DataObject 
class is probably the best alternative to use in a multi-thread environment, 
though. I'm using it myself; without any problems (so far.)

Please let us know if this Class fixes your issues!

Thanks,

Andreas

Andreas Gehrs-Pahl
Absolute Software, LLC

phone: (989) 723-9927
email: Andreas@AbsoluteSoftwareLLC.com
web:   http://www.AbsoluteSoftwareLLC.com
[F]:   https://www.facebook.com/AbsoluteSoftwareLLC

AGP_DataObjects.zip
Raffaele LafrattaRe: DataObject xbase 2.0 726
on Wed, 19 Oct 2016 15:14:33 +0200
Il 18/10/2016 15.36, Andreas Gehrs-Pahl ha scritto:
> Raffaele,
>
> I wrote:
>
>> That should basically work and the application shouldn't hang anymore.
>
> It actually doesn't work! I have even tried to get this to work by using
> an Abstract Class with Synch(ed) SetNoIVar() / GetNoIVar() methods and an
> internal, protected DataObject, but it still hangs when adding a new iVar
> to the internal DataObject, even when it is done inside a Synch Method,
> resulting in all the threads waiting for each other. This hang-up seems to
> happen whenever you try to add a new Instance Variable to a DataObject which
> already has several Instance Variables, and which has been "used" in (or
> accessed from) multiple threads, no matter in which thread you try to add
> the new Instance Variable.
>
> I would characterize this as a bug and you might want to contact Alaska and
> have them create a PDR for this issue.
>
> As a workaround, I have written my own substitute DataObject class, called
> AGP_DataObject(), which will fix those issues by simply using two internal
> Arrays for Instance Variables and Methods. The attached source code
> implements this class and some tests.
>
> This implementation uses Sync Methods, so it is completely thread-safe,
> even when executing a dynamic Method. The entire method will be executed
> before a different thread has access to the data of the object, making sure
> that the data integrity isn't compromised. This might be an issue if a
> method takes a long time to complete, but data integrity is more important
> to me than raw speed.
>
> And the most surprising thing is that using AGP_DataObjects is virtually
> as fast as using standard DataObjects, at least when the number of iVars
> is relatively small.
>
> The AGP_DataObject class also includes a method named :DataObject() that
> creates/exports a genuine DataObject with the exact same structure as the
> one the AGP_DataObject is emulating.
>
> The :Merge() method can be used to initialize/create (or update) a new or
> existing AGP_DataObject from another object -- either from a DataObject or
> from another AGP_DataObject.
>
> Also, the :Copy() method works differently than the :Copy() method of the
> standard DataObject class, and creates a non-dependent non-shallow copy of
> the object. Additionally, the :ClassDescribe() method has an option to
> create a result similar to the DataObject class.
>
> I also included methods to remove dynamic/virtual Instance Variables as well
> as dynamic/virtual Methods, and to sort them, mainly for aesthetic reasons.
>
> The one thing that I couldn't implement is access to Instance Variables via
> the (Array) Index Operator, as in: oDataObject[n], which returns the value
> of the n-th Instance Variable of the oDataObject. Even though I think this
> is a completely useless (and dangerous) coding style -- as you normally
> don't know (for sure) in which order Instance Variables will be (or have
> been) created -- some Xbase++ routines rely on this feature. This includes
> the Var2XML() function, which uses this feature because it is apparently
> faster than accessing the iVars by name. I'm also not sure how this index
> feature affects purely virtual iVars, which are implemented by using only
> Access/Assign Methods.
>
> Anyway, if you want to use this "access via index" feature, you can always
> create a genuine DataObject from the AGP_DataObject with the :DataObject()
> method.
>
> Until Alaska implements a thread-safe DataObject class, my AGP_DataObject
> class is probably the best alternative to use in a multi-thread environment,
> though. I'm using it myself; without any problems (so far.)
>
> Please let us know if this Class fixes your issues!
>
> Thanks,
>
> Andreas
>
Thanks Andreas for your great work

I also fixed the problem using Phil Ide's class "Container" . It's 
similar to your solution but yours is more extensive.

Best regards
Raffaele
Raffaele LafrattaRe: DataObject xbase 2.0 726
on Fri, 14 Oct 2016 17:41:55 +0200
Il 13/10/2016 22.02, Andreas Gehrs-Pahl ha scritto:
> Raffaele,
>
>> I use DataObject in a large multithreaded applications ( webserver )
>> I use a Dataobject for every session and every thread
>
> DataObjects are not "thread-safe" in the way you are apparently trying to
> use them. If a DataObject is accessed from more than one thread at the same
> time, the application will hang, because each thread is waiting for the
> other one(s) to release the object.
>
> If you want to use DataObjects the way your code seems to use them, you need
> to implement some sort of thread-safe wrapper for them yourself, for example
> using a custom class with a Synch(ed) Access/Assign (Class) Method.
>
> Example (pseudo) code:
>
> Class AppInfo
>    Exported:
>    Class Var oInfo
>
>    Class Method Init
>
>    Sync Access Assign Class Method oInfo
> EndClass
>
> Class Method AppInfo:Init()
>    ::oInfo := DataObject():New()
> return (Self)
>
> Class Method AppInfo:oInfo(oInfo)
>    if PCount() == 1
>       ::oInfo := oInfo
>    endif
> return (::oInfo)
>
> And then use it like this:
>
> Function InitSession()
> LOCAL oApp := AppInfo():New()
>    oApp:oInfo:session   := cIdSession
>    oApp:oInfo:user      := "XXX"
>    oApp:oInfo:lingua    := 1
>    oApp:oInfo:collation := SetCollation()
>    oApp:oInfo:codepage  := 0
>    oApp:oInfo:userPath  := ""
>    oApp:oInfo:userFiles := ""
>    oApp:oInfo:usage     := 0
>    oApp:oInfo:Started   := MilliSeconds()
>    oApp:oInfo:forms     := {}
> return (oApp)
>
> Function Update()
> LOCAL oApp := Xsession()  retrieve session information
>    oApp:oInfo:NewMember := "Some Value"
> [...]
>
> That should basically work and the application shouldn't hang anymore.
>
> Hope that helps,
>
> Andreas
>
Hi Andreas

IT seems the problem exists even if only one thread ( I'm sure ) 
accesses dataObject
No problem after removing the last member

Regards
Raffaele