Author | Topic: Re: DataObject xbase 2.0 726 | |
---|---|---|
Andreas Gehrs-Pahl View the complete thread for this message in: | 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 |