Author | Topic: DataObject xbase 2.0 726 | |
---|---|---|
Raffaele Lafratta | DataObject 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 Lafratta | Re: 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 Lafratta | Re: 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 Lafratta | Re: 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 |