TheSwamp

Code Red => .NET => Topic started by: TheMaster on August 02, 2012, 06:59:51 PM

Title: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 02, 2012, 06:59:51 PM
I think it's clear that the advice given on the adndevblog regarding this topic is not only misguided, but the one that offered it clearly didn't give much thought to its implications.

Code - C#: [Select]
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using Autodesk.AutoCAD.ApplicationServices;
  7. using Autodesk.AutoCAD.Runtime;
  8. using Autodesk.AutoCAD.EditorInput;
  9. using Autodesk.AutoCAD.DatabaseServices;
  10. using Autodesk.AutoCAD.Geometry;
  11.  
  12. namespace AutoCADProject1
  13. {
  14.    public static class Commands
  15.    {
  16.       // Straightforward add line to model space:
  17.       [CommandMethod( "AddLineToModel" )]
  18.       public static void AddLineToModel()
  19.       {
  20.          Document doc = Application.DocumentManager.MdiActiveDocument;
  21.          using( Transaction tr = doc.TransactionManager.StartTransaction() )
  22.          {
  23.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId( doc.Database );
  24.             BlockTableRecord model = (BlockTableRecord) msId.GetObject( OpenMode.ForWrite );
  25.             using( Line line = new Line( Point3d.Origin, new Point3d( 2.0, 2.0, 0.0 ) ) )
  26.             {
  27.                line.SetDatabaseDefaults( doc.Database );
  28.                model.AppendEntity( line );
  29.                tr.AddNewlyCreatedDBObject( line, true );
  30.                tr.Commit();
  31.             }
  32.          }
  33.       }
  34.  
  35.       /// Functionally the same as the above, but strictly following
  36.       /// the advice given by Fenton Webb (e.g., dispose *everything*) at:
  37.       /// http://adndevblog.typepad.com/autocad/2012/07/forcing-the-gc-to-run-on-the-main-thread.html:
  38.       ///
  39.       ///   "Therefore, I recommend you Dispose() all AutoCAD .NET objects using
  40.       ///   the .NET 'using' statement – that way you are not relying on the GC
  41.       ///   to clean up after yourself."
  42.  
  43.       [CommandMethod( "FentonsAddLineToModel")]
  44.       public static void FentonsAddLineToModel()
  45.       {
  46.          // Disposing the Document is not only unecessary, doing it will cause
  47.          // a failure on the next attempt to access that same Document:
  48.          using( Document doc = Application.DocumentManager.MdiActiveDocument )  
  49.          {
  50.             // Disposing the TransactionManager is completely unnecessary
  51.             using( Autodesk.AutoCAD.ApplicationServices.TransactionManager transman = doc.TransactionManager )
  52.             {
  53.                using( Transaction tr = transman.StartTransaction() )
  54.                {
  55.                   // Disposing the Database is also completely unnecessary:
  56.                   using( Database db = doc.Database )
  57.                   {
  58.                      ObjectId idModel = SymbolUtilityServices.GetBlockModelSpaceId( db );
  59.  
  60.                      // Disposing the BlockTableRecord is also completely unnecessary:
  61.                      using( BlockTableRecord model = (BlockTableRecord) idModel.GetObject( OpenMode.ForWrite ) )
  62.                      {
  63.                         using( Line line = new Line( Point3d.Origin, new Point3d( 2.0, 2.0, 0.0 ) ) )
  64.                         {
  65.                            line.SetDatabaseDefaults( db );
  66.                            model.AppendEntity( line );
  67.                            tr.AddNewlyCreatedDBObject( line, true );
  68.                            tr.Commit();
  69.                         }
  70.                      }
  71.                   }
  72.                }
  73.             }
  74.          }
  75.       }
  76.    }
  77. }
  78.  
Title: Re: Bad Advice regarding "To dispose or not to dispose"
Post by: Keith™ on August 02, 2012, 07:06:53 PM
Seems kind of convoluted if you ask me
Title: Re: Bad Advice regarding "To dispose or not to dispose"
Post by: TheMaster on August 02, 2012, 07:36:04 PM
Seems kind of convoluted if you ask me

What seems kind of convoluted?
Title: Re: Bad Advice regarding "To dispose or not to dispose"
Post by: Kerry on August 02, 2012, 08:27:40 PM
Tony,
I've been waiting till I have a couple of days spare so I could seek clarification of that blog post.

The question I asked in the post that lead to the one you refer to wasn't answered to my satisfaction.

My argument is STILL that these concepts and requirements should be covered in the documentation ... but my requests have seemingly fallen on deaf ears.

The code composition demonstrated by your second example is exactly as I interpreted the blog post.

for reference, this was my original question
Quote
I have a question

If StartOpenCloseTransaction() if the preferred operation why have we NOT seen it in any AutoDesk samples in the last 7 years ??

and another ..

>>
Quote
Remember to always call Dispose on your AutoCAD.NET objects.
<<
would someone care to make a public definitive statement regarding the disposing of objects ?
... and is the process different when using
StartOpenCloseTransaction() ??

Regards
Kerry
Title: Re: Bad Advice regarding "To dispose or not to dispose"
Post by: Jeff H on August 02, 2012, 09:08:53 PM
Just to make sure I have an understanding is the call to AddNewlyCreatedDBObject() necessary if you already have the line wrapped in the using statement?
 
Does adding the the line to the transaction just call dispose for you or is there more going on?
 
 
 
Code - C#: [Select]
  1.  
  2.          [CommandMethod("AddLineToModel")]
  3.         public static void AddLineToModel()
  4.         {
  5.             Document doc = Application.DocumentManager.MdiActiveDocument;
  6.             using (Transaction tr = doc.TransactionManager.StartTransaction())
  7.             {
  8.                 ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(doc.Database);
  9.                 BlockTableRecord model = (BlockTableRecord)msId.GetObject(OpenMode.ForWrite);
  10.                 using (Line line = new Line(Point3d.Origin, new Point3d(2.0, 2.0, 0.0)))
  11.                 {
  12.                     line.SetDatabaseDefaults(doc.Database);
  13.                     model.AppendEntity(line);
  14.                     //tr.AddNewlyCreatedDBObject(line, true);
  15.                     tr.Commit();
  16.                 }
  17.             }
  18.         }
  19.  
Title: Re: Bad Advice regarding "To dispose or not to dispose"
Post by: TheMaster on August 03, 2012, 12:26:51 AM
Just to make sure I have an understanding is the call to AddNewlyCreatedDBObject() necessary if you already have the line wrapped in the using statement?


Yes, it's still required.  Disposing the Line ensures that if the code were to fail before the line was added to the database, the line is deterministically disposed, preventing its finalizer from running on the GC's thread, which could crash AutoCAD. You could probably get away without disposing the Line since there's little chance of a failure in that particular example, but in more complex situations where more code runs before the line is added to the database, it becomes more dangerous.

Quote

Does adding the the line to the transaction just call dispose for you or is there more going on?
 

Adding the Line to the transaction causes it to be treated the same as database-resident objects that were acquired from the transaction. When a transaction is committed, changes made to all objects in the transaction are committed (as if AcDbObject::close() was called on every open AcDbObject in the transaction). If a transaction is aborted, the changes made to every AcDbObject in the transaction are rolled back (as if AcDbObject::cancel() were called on each), so adding newly-created objects to a transaction (after adding them to a database) allows changes made to those objects to be handled in the same way.

Title: Re: Bad Advice regarding "To dispose or not to dispose"
Post by: TheMaster on August 03, 2012, 12:35:36 AM

My argument is STILL that these concepts and requirements should be covered in the documentation ... but my requests have seemingly fallen on deaf ears.


Reading the documentation reveals numerous telltale signs suggesting that the writers were not all that experienced with the AutoCAD managed API, and of course, that would explain why there is so much missing from it.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Keith™ on August 03, 2012, 01:34:15 AM
Seems kind of convoluted if you ask me

What seems kind of convoluted?

Well, maybe I chose the wrong word .. I tend to do that.

Maybe I just don't see the bigger picture. Disposing of objects when you are done with them is kind of the point isn't it? I thought that the 'using' statement did just that, so why would another disposal be necessary .. unless the concept is that the garbage collector deals with items based upon how they are disposed.

My understanding was that as soon as objects went out of scope, .Net automatically manages the disposal of items not explicitly disposed ... and while it does rely on the garbage collector to do the dirty work, isn't that kind of the point with .Net?

I personally don't recommend leaving unfinished tasks to the GC, instead I like to dispose of items as soon as I am done with them (or as soon as practical). Doing it this way seems straightforward if you ask me ... but apparently others have different ideas ... or maybe that is just the C/C++ programmer speaking.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: VVeli on August 03, 2012, 03:07:14 AM
Hi,
I have also worked with these Disposable objects but there is not a straight line what objects are MUST to dispose.
Have you read Kean´s article of that? That is the clearest article what I have seen.
http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html
http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html (http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html)

Cheers
Veli V.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 03, 2012, 04:04:26 AM

There is also this recent thread here.

http://www.theswamp.org/index.php?topic=40286.0;all

Hi Guys,

Stephen is correct. You dont have to use the "using" statement with existing DB resident objects that are opened with the transaction (provided the transaction object is created with the "using" statement).

I originally thought that we need to explicitly dispose off existing DB resident objects as well that are opened with a transaction but because the transaction will be disposed automatically in the UI thread in case of an exception (provided you use the "using" statement with the transaction object), the DB objects openeds with the transaction will also be disposed off properly.

To simplify:

1) Always use the "using" statement with the transaction object
2) Always use the "using" statement with newly created DB Objects being added to the transaction
3) Always using the "using" statement with DB Objects that are not added to the database
4) You dont have to use the "using" statement with existing DB Objects opened with a transaction object

Cheers
Gopinath
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 03, 2012, 12:52:09 PM
Hi,
I have also worked with these Disposable objects but there is not a straight line what objects are MUST to dispose.
Have you read Kean´s article of that? That is the clearest article what I have seen.
http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html
http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html (http://through-the-interface.typepad.com/through_the_interface/2008/06/cleaning-up-aft.html)

Cheers
Veli V.

I've written about this many times, and agree that the rules aren't simple.

For DBObjects, it depends on two things:

If the DBObject is database-resident, and you got it from a Transaction, you don't have to dispose it.

If the DBObject is not database-resident, or is database-resident and you got it from ObjectId.Open(), then you must dispose it.

Objects whose Dispose() methods have side-effects must always be disposed. Those include the Transaction and DocumentLock. Both of those do things when when they are disposed. You can often tell if an object will perform some specific side-effect in their finalizer, by examining its AutoDelete property. When the value of this property is false, it usually means the object does nothing when its finalizer is called (either by the GC or by a call to Dispose()).

For example, when you get a Database from the Document's Database property, the AutoDelete property of the Database managed wrapper is false, because the drawing is open in the editor and for that reason its underlying AcDbDatabase must not be deleted. If on the other hand, you get a Database by calling the constructor (e.g., 'new Database(...)' ), and then you call ReadDwgFile(), the value of the AutoDelete property is true (and serves as the way of telling the Database managed wrapper that it must delete the wrapped AcDbDatabase).

For all DisposableWrappers, you must dispose them if code that runs from their Finalizer is not thread-safe, and could crash AutoCAD if it runs on the GC's thread. Unfortunately, that is not easy to determine, and perhaps is why Fenton gives the advice he does, but I still do not agree with it, or with the overall design which requires either guesswork, intimate knowledge of API internals, or taking no chances and grossly over-complicating client code by disposing *everything* as per my example above.

And here's something those Autodesk people didn't tell you:

Avoiding calls to Dispose() when it is unnecessary can actually improve the performance of AutoCAD and your plug-ins. The reason for that is because by default, the GC runs on a high-priority background thread, which means that on a multiple-processor system (do you know anyone that still runs AutoCAD on a single-processor system?), the GC can execute on a different (and presumably under-utilized) processor, concurrently, while AutoCAD and your plug-in run on another processor (e.g., a form of parallel execution). And, since AutoCAD doesn't leverage multiple processors, work done by the GC can be offloaded to another CPU core with virtually no performance impact on AutoCAD and your plug-in.

So, if you do follow the advice of Fenton and Stephen, the work that must be done by Dispose() will be done synchronously, requiring AutoCAD and your plug-in to wait for it to complete, which is not the case if you just allow the GC to take care of it on another under-utilized CPU core.

Oh and BTW, does that also give a hint as to why disabling concurrent garbage collection in AutoCAD is a really bad idea? If you do choose to do that, you should be the one sitting at that PC waiting the extra time and experiencing the lag in the UI during interactive operations like dragging. If you are going to do that for someone else only because it eliminates the crashes from the bugs in your code, do it only as a temporary stopgap measure, fix the bugs as soon as possible and re-enable concurrent garbage collection, as it should be.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 03, 2012, 01:55:21 PM
Seems kind of convoluted if you ask me

What seems kind of convoluted?

Well, maybe I chose the wrong word .. I tend to do that.

Maybe I just don't see the bigger picture. Disposing of objects when you are done with them is kind of the point isn't it? I thought that the 'using' statement did just that, so why would another disposal be necessary .. unless the concept is that the garbage collector deals with items based upon how they are disposed.

My understanding was that as soon as objects went out of scope, .Net automatically manages the disposal of items not explicitly disposed ... and while it does rely on the garbage collector to do the dirty work, isn't that kind of the point with .Net?

I personally don't recommend leaving unfinished tasks to the GC, instead I like to dispose of items as soon as I am done with them (or as soon as practical). Doing it this way seems straightforward if you ask me ... but apparently others have different ideas ... or maybe that is just the C/C++ programmer speaking.

The whole point to writing code that runs in a 'managed' execution environment, is so that the programmer can rely on the GC to clean up after themself, and offload tasks that C/C++ programmers must routinely do manually (e.g, free memory and call d'tors) or automate with the help of smart pointers.

When we write managed code, we are always relying on the GC to clean up the managed objects we create and use (whether they implement IDisposable or not), and we do not need to be concerned with deallocating those objects or the memory they consume, calling d'tors, etc.

The purpose of IDisposable is to provide a means for the user of class to deterministically tell an instance of the class that they are done using it, and what an object does in response to that signal is entirely up to the designer of the class. For many objects, IDisposable is a clean way to perform operations that must always be performed after code has finished using an object, but should not be delayed until the GC collects the object. For example, if an object represents a file on disk, or a registry key, it can implement IDisposable and in its implementation of Dispose() it can close the open file or registry key. 

The reason for all of the confusion in the case of AutoCAD's managed API, is that many AutoCAD managed types implement IDisposable even when it is unnecessary, and largely out of convenience. Many managed types derive from DisposableWrapper (which is the class that implements IDisposable), and in some cases, do so because they must also derive from the RXObject base type (which is derived from DisposableWrapper), because the managed class represents a native object that is an instance of AcRxObject.

So, what we have in the case of AutoCAD's managed API are many types that implement IDisposable largely out of coincidence and/or convenience, rather than because they must do something meaningful in their implementation of Dispose().
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Keith™ on August 03, 2012, 03:20:16 PM
From my perspective that kinda proves my point .. that being that .Net manages those objects, many times better than the person writing the code would.

Is it possible that Autodesk's managed API is merely a bandaid version? After all, alot of time folks do things just because that is the way they have always done them. Why should Autodesk be any different?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 03, 2012, 05:41:28 PM
From my perspective that kinda proves my point .. that being that .Net manages those objects, many times better than the person writing the code would.

Is it possible that Autodesk's managed API is merely a bandaid version? After all, alot of time folks do things just because that is the way they have always done them. Why should Autodesk be any different?

I don't think it can be called a band-aid, and the architect (Albert Szilvasy) deserves a lot of credit for what is for the most part, a well-designed managed API layer, but I'd also dare to speculate that given what everyone, including Albert has learned about the API and the functional requirements and usage patterns to date, it might have been done somewhat differently if it was completely redone from scratch.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: dgorsman on August 03, 2012, 05:45:27 PM
Do you think that total re-write could be done without significantly breaking existing code?  And in a timeframe consistent with the rate at which tools, .NET frameworks, etc. are released and retired?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 04, 2012, 12:51:05 AM
Do you think that total re-write could be done without significantly breaking existing code?  And in a timeframe consistent with the rate at which tools, .NET frameworks, etc. are released and retired?

There can't be a total re-write without it significantly breaking existing code, so I don't understand the nature of the question.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: MexicanCustard on August 06, 2012, 08:46:11 AM

If the DBObject is not database-resident, or is database-resident and you got it from ObjectId.Open(), then you must dispose it.


What about ObjectId.GetObject()?  I assumed that since I cannot use this outside of an active transaction that it's getting the top transaction and using that.  So, when I dispose of the top transaction and haven't implicitly disposed of the database resident object all is right with the world.  Am I right to make this assumption?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 06, 2012, 01:59:41 PM
Hey TT

Actually, I think you misunderstood what I said...

I do mention at the end of that sentence "to cleanup after yourself" - meaning, that you should clean up *YOUR* objects not AutoCAD's.

I noted your comments on the link you posted and I really think it's fairly obvious that you should not dispose of master property objects such as MdiActiveDocument? As common in any computer language, you don't dispose/delete/free objects that are not under your control.

Yes .NET is super cool, but you should still think about what you are doing a bit. Also, the bottom like is that the AutoCAD .NET API wraps ObjectARX, that means you absolutely cannot rely on the GC to Dispose AutoCAD Database objects, and, you shouldn't rely on the GC to Dispose all other AutoCAD .NET classes.
 
By the way, being careful about cleaning up after your self (not relying on the GC) can be said for alot of Microsofts own .NET API! There are so many things that can't be allowed to be disposed by the GC, how about a file for instance? if you open a file for write, do you rely on the GC to close it? The point is that our AutoCAD .NET API use age requirements are not special by any means.

About The Transaction object in AutoCAD... The Transaction model was invented way back when for a specific reason - transacting multiple writes on the same objects(s) and allowing layered rollbacks of these multi-write transactions. Back when AutoCAD V1 of .NET was invented, VB.NET did *not* have a using statement so we thought that if we don't allow VBA developers on VB.NET an easy way to control AutoCAD objects we would cost lots of crashes and support calls. This is when AutoCAD .NET Transactions were born, and why we use them.
       Now, unfortunately, there is a big overhead creating a Transaction and calling StartTransaction() - this is because it's initializing a whole subsystem which allows this layered writing and undoing mechanism... That's why we have another method called StartOpenCloseTransaction() which is much leaner and faster, you should use that.
       When using a Transaction, all objects obtained in the transaction using GetObject() or any objects added to the transaction using AddNewlyCreatedDBObject() do not need to be explicitly Dispose()'s - this is because the Transaction.Commit() does it for you.
       If you do not add a newly created object to the transaction using AddNewlyCreatedDBObject, yes, you will have to call Dispose(). However, by not adding it you break the rules of the transaction, so I don't advise that.

So "TT" - how about you come to Autodesk University this year? We can do a special fun coding class - "Fenton Webb vs TT in AutoCAD .NET Coding Head to Head" - the audience chooses what we code, let's see who does the best job in the shortest time? Come on, it will be fun! I'm sure you will win
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 06, 2012, 03:45:28 PM
<snip>

So "TT" - how about you come to Autodesk University this year? We can do a special fun coding class - "Fenton Webb vs TT in AutoCAD .NET Coding Head to Head" - the audience chooses what we code, let's see who does the best job in the shortest time? Come on, it will be fun! I'm sure you will win

(http://www.theswamp.org/lilly_pond/alanjt/popcorn.gif)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 06, 2012, 04:54:46 PM

Fenton,
Just to demonstrate your point ;

How about you you recode the samples in the SDK and Labs to conform with what you are advocating !

... that will give everyone some concrete examples.

Regards
Kerry.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 06, 2012, 05:50:10 PM

Fenton, I've been back to re-read the couple of blog posts in question.

http://adndevblog.typepad.com/autocad/2012/07/the-right-tools-for-the-job-autocad-part-3.html
http://adndevblog.typepad.com/autocad/2012/07/forcing-the-gc-to-run-on-the-main-thread.html

Quote
One question that came up was when to call Dispose(). The thing is that all of our .NET code wraps ObjectARX, in turn, all of our ObjectARX code is single threaded and the .NET Garbage Collector runs on a background thread... What this means is, if you don't call Dispose() on our AutoCAD .NET objects, you run the high risk of the GC garbage collecting our objects on a background thread, which in turn may cause AutoCAD to crash.


Quote
Therefore, I recommend you Dispose() all AutoCAD .NET objects using the .NET 'using' statement – that way you are not relying on the GC to clean up after yourself.



The logical result of these requirements is code similar to Tony's second example.

The average .NET developer/customiser does not have the benefit of having the AutoCAD  chief architect in the next room and therefore has to rely on samples and documentation.

I believe it's time this issue was cleared up.

Regards
Kerry
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 06, 2012, 07:32:07 PM
A explanation of what is going on behind the scences would be nice.
Also what is the second parameter to Transaction.AddNewlyCreatedDBObject () exactly for?
When would it be ever be false?
Here (http://adndevblog.typepad.com/autocad/2012/06/converting-polyline-to-polyline2d.html) is the only place I have ever seen it false, and is a OpenCloseTransaction
 
 
 
 
For learning LINQ Edulinq (http://msmvps.com/blogs/jon_skeet/archive/2010/09/03/reimplementing-linq-to-objects-part-1-introduction.aspx) is a great learning experince as it shows what it takes to create your own.
 
In that spirit I just slapped this together and took fifteen minutes, but someone actually knowing what is going on would be nice?
 
 
Code - C#: [Select]
  1.  
  2.       [CommandMethod("AddLineToModel")]
  3.         public void AddLineToModel() // This method can have any name
  4.         {
  5.             Document doc = Application.DocumentManager.MdiActiveDocument;
  6.             Database db = doc.Database;
  7.             using (ITransaction trx = db.StartRollYourOwnTransaction())
  8.             {
  9.                 ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(doc.Database);
  10.                 BlockTableRecord model = (BlockTableRecord)trx.GetObject(msId, OpenMode.ForWrite);
  11.                 Line line = trx.CreateNew<Line>();
  12.                 line.StartPoint = Point3d.Origin;
  13.                 line.EndPoint = new Point3d(2.0, 2.0, 0.0);
  14.                 line.SetDatabaseDefaults();
  15.                 model.AppendEntity(line);    
  16.  
  17.                 Line line2 = trx.CreateNew<Line>();
  18.                 line2.StartPoint = Point3d.Origin;
  19.                 line2.EndPoint = new Point3d(2.0, 2.0, 0.0);
  20.                 line2.SetDatabaseDefaults();
  21.                 //model.AppendEntity(line2);    
  22.                          
  23.                 trx.Commit();
  24.             }
  25.         }
  26.  

Code - C#: [Select]
  1.     public interface ITransaction : IDisposable
  2.     {
  3.         void AddObjectToDispose(IDisposable disposableObject);
  4.         void Abort();        
  5.         void Commit();      
  6.         DBObject GetObject(ObjectId id, OpenMode mode);
  7.         DBObject GetObject(ObjectId id, OpenMode mode, bool openErased);
  8.         DBObject GetObject(ObjectId id, OpenMode mode, bool openErased, bool forceOpenOnLockedLayer);
  9.         T CreateNew<T>() where T : DBObject, new();
  10.     }
  11.     public class RollYourOwnTransaction : ITransaction
  12.     {
  13.         Queue<IDisposable> disposables = new Queue<IDisposable>();
  14.         Queue<DBObject> dbobjectsDisposables = new Queue<DBObject>();
  15.  
  16.         private bool isDisposed = false;
  17.  
  18.         public RollYourOwnTransaction()
  19.         {
  20.         }
  21.         ~RollYourOwnTransaction()
  22.         {
  23.             this.Dispose(false);
  24.         }
  25.         public void AddObjectToDispose(IDisposable item)
  26.         {
  27.             disposables.Enqueue(item);
  28.         }
  29.         public void Abort()
  30.         {
  31.             this.Dispose(false);
  32.             GC.SuppressFinalize(this);
  33.         }
  34.         public void Commit()
  35.         {
  36.             this.Dispose();
  37.         }
  38.  
  39.         public DBObject GetObject(ObjectId id, OpenMode mode)
  40.         {
  41.             DBObject obj = id.Open(mode, false, false);
  42.             dbobjectsDisposables.Enqueue(obj);
  43.             return obj;
  44.         }
  45.         public DBObject GetObject(ObjectId id)
  46.         {
  47.             DBObject obj = id.Open(OpenMode.ForRead, false, false);
  48.             dbobjectsDisposables.Enqueue(obj);
  49.             return obj;
  50.         }
  51.         public DBObject GetObject(ObjectId id, OpenMode mode, bool openErased)
  52.         {
  53.             DBObject obj = id.Open(mode, openErased, false);
  54.             dbobjectsDisposables.Enqueue(obj);
  55.             return obj;
  56.         }
  57.         public DBObject GetObject(ObjectId id, OpenMode mode, bool openErased, bool forceOpenOnLockedLayer)
  58.         {
  59.             DBObject obj = id.Open(mode, openErased, forceOpenOnLockedLayer);
  60.             dbobjectsDisposables.Enqueue(obj);
  61.             return obj;
  62.         }
  63.         public void Dispose()
  64.         {
  65.             this.Dispose(true);
  66.             GC.SuppressFinalize(this);
  67.         }
  68.         void Dispose(bool disposing)
  69.         {
  70.             if (!isDisposed)
  71.             {
  72.                 if (disposing)
  73.                 {
  74.                     closeDbObjectQueue();
  75.                 }
  76.                 else
  77.                 {
  78.                     abortDBObjectQueue();
  79.                 }
  80.                 disposeQueue();
  81.             }
  82.             isDisposed = true;
  83.         }
  84.  
  85.         void disposeQueue()
  86.         {
  87.             while (disposables.Count > 0)
  88.             {
  89.                 disposables.Dequeue().Dispose();
  90.             }
  91.             disposables = null;
  92.         }
  93.  
  94.         void closeDbObjectQueue()
  95.         {
  96.             while (dbobjectsDisposables.Count > 0)
  97.             {
  98.                 if (dbobjectsDisposables.Peek().ObjectId.IsNull)
  99.                 {
  100.                     dbobjectsDisposables.Dequeue().Dispose();
  101.                 }
  102.                 else
  103.                 {
  104.                     dbobjectsDisposables.Dequeue().Close();
  105.                 }
  106.             }
  107.             dbobjectsDisposables = null;
  108.         }
  109.  
  110.         void abortDBObjectQueue()
  111.         {
  112.             while (dbobjectsDisposables.Count > 0)
  113.             {
  114.                 if (dbobjectsDisposables.Peek().ObjectId.IsNull)
  115.                 {
  116.                     dbobjectsDisposables.Dequeue().Dispose();
  117.                 }
  118.                 else
  119.                 {
  120.                     dbobjectsDisposables.Dequeue().Cancel();
  121.                 }
  122.             }
  123.             dbobjectsDisposables = null;
  124.         }
  125.  
  126.  
  127.         public T CreateNew<T>() where T : DBObject, new()
  128.         {
  129.             T obj = new T();
  130.             dbobjectsDisposables.Enqueue(obj);
  131.             return obj;
  132.         }
  133.  
  134.     }
  135.     public static class Extensions
  136.     {
  137.    
  138.         public static ITransaction StartRollYourOwnTransaction(this Database db)
  139.         {
  140.             return new RollYourOwnTransaction();
  141.         }
  142.    
  143.     }
  144.  
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 06, 2012, 08:04:44 PM
Hey Kerry

I believe in the context I was writing my post it was clear that I was not talking about Dispose()ing objects defined by AutoCAD itself, but if you believe it's misleading then I'll make the post more clear for you, no problem.

Hey Jeff,

that extra flag on the end of AddNewlyCreatedDBObject(): If add == True, the object passed is added to the top transaction. If add == False, then the object is removed from whatever transaction it's within. The object passed must point to an object that is newly created (that is, it has never been closed) and is already database resident (that is, it's been added to a Database so it has an objectId). If the object passed is not newly created, then an exception eNotNewlyCreated is thrown.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 06, 2012, 09:27:18 PM
Hey Kerry

I believe in the context I was writing my post it was clear that I was not talking about Dispose()ing objects defined by AutoCAD itself, but if you believe it's misleading then I'll make the post more clear for you, no problem.

< .. >


Fenton,
I understand how difficult it is to write well because the writer usually cant help but assume that the reader has some knowledge that the writer takes for granted.

However, words have meanings and the quotes I made in  Reply #20  (http://www.theswamp.org/index.php?topic=42399.msg476080#msg476080) are explicit, which is what brought me into this discussion.

I do look forward to a re-stating of the requirements.
.... and I'd be appreciative if you could cover this concept explicitly somewhere in public documentation so that old lisp hackers like me are able to make the transition to .NET without too much confusion.

For the sake of completeness, how would you code the sample that Tony used at the start of this discussion. ??


Regards
Kerry.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 07, 2012, 01:32:31 AM
Hey TT

Actually, I think you misunderstood what I said...

I do mention at the end of that sentence "to cleanup after yourself" - meaning, that you should clean up *YOUR* objects not AutoCAD's.

I noted your comments on the link you posted and I really think it's fairly obvious that you should not dispose of master property objects such as MdiActiveDocument? As common in any computer language, you don't dispose/delete/free objects that are not under your control.

Yes .NET is super cool, but you should still think about what you are doing a bit. Also, the bottom like is that the AutoCAD .NET API wraps ObjectARX, that means you absolutely cannot rely on the GC to Dispose AutoCAD Database objects, and, you shouldn't rely on the GC to Dispose all other AutoCAD .NET classes.
 
By the way, being careful about cleaning up after your self (not relying on the GC) can be said for alot of Microsofts own .NET API! There are so many things that can't be allowed to be disposed by the GC, how about a file for instance? if you open a file for write, do you rely on the GC to close it? The point is that our AutoCAD .NET API use age requirements are not special by any means.

About The Transaction object in AutoCAD... The Transaction model was invented way back when for a specific reason - transacting multiple writes on the same objects(s) and allowing layered rollbacks of these multi-write transactions. Back when AutoCAD V1 of .NET was invented, VB.NET did *not* have a using statement so we thought that if we don't allow VBA developers on VB.NET an easy way to control AutoCAD objects we would cost lots of crashes and support calls. This is when AutoCAD .NET Transactions were born, and why we use them.
       Now, unfortunately, there is a big overhead creating a Transaction and calling StartTransaction() - this is because it's initializing a whole subsystem which allows this layered writing and undoing mechanism... That's why we have another method called StartOpenCloseTransaction() which is much leaner and faster, you should use that.
       When using a Transaction, all objects obtained in the transaction using GetObject() or any objects added to the transaction using AddNewlyCreatedDBObject() do not need to be explicitly Dispose()'s - this is because the Transaction.Commit() does it for you.
       If you do not add a newly created object to the transaction using AddNewlyCreatedDBObject, yes, you will have to call Dispose(). However, by not adding it you break the rules of the transaction, so I don't advise that.

So "TT" - how about you come to Autodesk University this year? We can do a special fun coding class - "Fenton Webb vs TT in AutoCAD .NET Coding Head to Head" - the audience chooses what we code, let's see who does the best job in the shortest time? Come on, it will be fun! I'm sure you will win

Hi Fenton, and thanks for taking the time to respond here.

I think my point is that there are many people reading that blog who are just starting out with .NET, and have little or no prior experience with native ObjectARX/C++, something that those of us having that background sometimes mistakenly take for granted.

The fact that I misunderstood your post, should serve to make my point, which is that even if it wasn't intended, your advice will be taken literally and without regards for who owns an object. If the docs were as thorough as the native docs are, and clearly indicated what objects are owned and do not require disposal, then perhaps there would be a lot less confusion, and a lot less confusing code. A case in point, if you look at code from Jerry Winter's books and AU material, you will routinely see the TransactionManager being disposed, even though it wraps an object whose scope is the scope of the owning database.

So, it is not about whether your advice was technically correct or not, but rather how it is interpreted (or misinterpreted) by those who may not understand as much about who owns what. While it may be reasonable to assume that if you create an instance of something by calling its class constructor, then you almost certainly own the object and are responsible for disposing it, but in the case of objects that are returned by other API calls, the question of who owns the returned object is not all that clear, especially to the beginner.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 07, 2012, 02:45:30 AM
have little or no prior experience with native ObjectARX/C  , something that those of us having that background sometimes mistakenly take for granted.

Tony and Fenton,
 
Sorry to sidetrack but I am not a professional programmer and most of my background is in electrical, but I found out I really enjoyed programming. I am about a year and half shy of getting my E.E but had to take break due to fact of being a single father and about to get back in and want to change to CS.
I have have been on fence about quitting and not spending anymore time on learning the AutoCAD API besides writing some quick commands to speeding up some task and spending the time on other programming technologies. If anything and has come close to materializing(something fairly large and 1000+ users) I would honestly just see if I could hire Tony to do it. As a last stitch effort should I just spend the next 6 months or so dong all coding using ObjectARX. It seems like I got a little familiar with concepts, then got a little better just how to search and find methods needed, but never got a deeper understanding of the core concepts required. I just do not feel comfortable trying anything to large due to fact I know enough to know I really do not know what the code is really doing.
 
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: gile on August 07, 2012, 05:07:20 AM
Hi,

I consider me as an intermediate .NET coder (not completely newby, but my knowledge is quite far from Fenton's, Tony's, Kerry's, ...). More, English is not my native language and I often have some doubts about my understanding of the docs (and forum discussions).

So, here's an 'intermediate' concrete question about "To dispose or not to dispose" with DBOBjectCollection.
The following code uses two DBOBjectCollection to create 3d solids from polylines. The first one (plineCollection) is fill with selected polylines (database-resident) the second one (regionCollection) contains the Region objects created with Region.CreateFromCurves() method with the first one. The regions in the second DBObjectCollection are only used to create the 3d solids and won't be added to the database.

Code - C#: [Select]
  1.         [CommandMethod("ExtrudePlines")]
  2.         public void ExtrudePlines()
  3.         {
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Database db = doc.Database;
  6.             Editor ed = doc.Editor;
  7.             TypedValue[] filter = { new TypedValue(0, "LWPOLYLINE"), new TypedValue(-4, "&"), new TypedValue(70, 1) };
  8.             PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
  9.             if (psr.Status != PromptStatus.OK) return;
  10.             PromptDoubleResult pdr = ed.GetDistance("\nExtrusion height: ");
  11.             if (pdr.Status != PromptStatus.OK) return;
  12.             double height = pdr.Value;
  13.  
  14.             using (Transaction tr = db.TransactionManager.StartTransaction())
  15.             {
  16.                 BlockTableRecord currentSpace =
  17.                     (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  18.                 using (DBObjectCollection plineCollection = new DBObjectCollection())
  19.                 {
  20.                     foreach (ObjectId id in psr.Value.GetObjectIds())
  21.                     {
  22.                         Polyline pline = (Polyline)tr.GetObject(id, OpenMode.ForRead);
  23.                         plineCollection.Add(pline);
  24.                     }
  25.                     using (DBObjectCollection regionCollection = Region.CreateFromCurves(plineCollection))
  26.                     {
  27.                         foreach (Region region in regionCollection)
  28.                         {
  29.                             try
  30.                             {
  31.                                 using (Solid3d solid = new Solid3d())
  32.                                 {
  33.                                     solid.Extrude(region, height, 0.0);
  34.                                     currentSpace.AppendEntity(solid);
  35.                                     tr.AddNewlyCreatedDBObject(solid, true);
  36.                                 }
  37.                             }
  38.                             finally
  39.                             {
  40.                                 region.Dispose();
  41.                             }
  42.                         }
  43.                     }
  44.                     tr.Commit();
  45.                 }
  46.             }
  47.         }

1. Do I have to dispose the plineCollection (line 18) ?

2. Do I have to dispose the regionCollection (line 25) ? And if so, do I have to dispose each region too (line 40)

3. If I dispose each region (line 40), do I need to dispose the regionCollection too (line 25) ?

4. (subsidiary) May I have used StartOpenCloseTransaction() instead of StartTransaction() (line 14) ?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 07, 2012, 06:10:45 AM

Fenton, I've been back to re-read the couple of blog posts in question.

http://adndevblog.typepad.com/autocad/2012/07/the-right-tools-for-the-job-autocad-part-3.html


Here, Fenton Webb wrote about the poor speed LISP.
In fact, it's not so bad - it all depends on the skill of the programmer and his language skills. Three years ago, I tested the dictionary in autocad - added dictionaries for 100 mb. My program to emulate the internal dictionary as the SQL database. Speed, compared to an external database, located on the same computer that was ten times faster!

I do not think that the author is equally good command of all the above languages​...
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: MexicanCustard on August 07, 2012, 08:58:27 AM
About The Transaction object in AutoCAD... The Transaction model was invented way back when for a specific reason - transacting multiple writes on the same objects(s) and allowing layered rollbacks of these multi-write transactions. Back when AutoCAD V1 of .NET was invented, VB.NET did *not* have a using statement so we thought that if we don't allow VBA developers on VB.NET an easy way to control AutoCAD objects we would cost lots of crashes and support calls. This is when AutoCAD .NET Transactions were born, and why we use them.
       Now, unfortunately, there is a big overhead creating a Transaction and calling StartTransaction() - this is because it's initializing a whole subsystem which allows this layered writing and undoing mechanism... That's why we have another method called StartOpenCloseTransaction() which is much leaner and faster, you should use that.

So should we always be using StartOpenCloseTransaction() or are there times when we should use both?  I'm confused, almost every example on the the ADN blog, Keans Blog, and on here use StartTransaction().  If StartOpenCloseTransaction() is better why don't we see more examples using it?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 07, 2012, 09:01:31 AM
Sorry
 
Also by multiple writes do you mean the transaction model is best when writing to an object that is then passed to another function or nested transaction  that writes to it and is helpful that it only it opens once instead of multiples times.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 07, 2012, 09:35:58 AM
As a Visual LISP guy, who's trying to teach myself .NET, I'd just like to thank everyone for posting (great?) questions, code examples, and the overall discussion... Especially being new to .NET concepts in general, not just AutoCAD's .NET API, but also Civil 3D's .NET API, I don't often know what I don't know.

Working for a company that doesn't exactly encourage development (I do most of it entirely in my personal time), I'm often relegated to using the developer documentation which is not easy to read. With the ActiveX documentation being removed from even the LISP developer documentation (at least as integrated in the main *dev.chm), Autodesk isn't exactly encouraging me to invest much at all in the products I use regularly.

FWIW - Reading this thread is the first time I've even seen StartOpenCloseTransaction() mentioned; granted I should read DevBlog more, but I just don't have time to read everything, as this is still very much just a hobby (or something I work on for my night job).

Having members that I've historically learned much from, discussing this openly with ADN member(s) like Fenton, is of great value to me, and I just wanted to say thanks to the lot!

*bows out to 'listen' and learn*
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 07, 2012, 07:00:41 PM
I am screwing something up for anyone who wants to try to test with code and drawings and results below.
I just do not have the attention span, will or honestly do not care to investigate anything more than a quick slapping together, but tried to make them equal but looking quickly in Reflector looks like you need to use the OpenCloseTransaction object unlike the Transaction object where you can use TransactionManager for GetObject
 
Basically a test using
Open Close method
OpenCloseTransaction
Transaction
 
Here is the code
*******************************************EDIT*********************
Just noticed I open ModelSpace for write in half of them and read for the other
Code - C#: [Select]
  1.         Random ran = new Random();
  2.         Stopwatch sw = new Stopwatch();
  3.  
  4.         [CommandMethod("TurnAllEntitesRandomColor")]
  5.         public void TurnAllEntitesRandomColor()
  6.         {
  7.             int colorIndex = ran.Next(1, 256);
  8.            
  9.             Database db = HostApplicationServices.WorkingDatabase;
  10.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
  11.             Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  12.             sw.Reset();
  13.             sw.Start();
  14.             using (Transaction trx = tm.StartTransaction())
  15.             {
  16.                 BlockTableRecord btr = (BlockTableRecord)tm.GetObject(msId, OpenMode.ForRead, false, false) ;
  17.                
  18.                 foreach (ObjectId id in btr)
  19.                 {
  20.                     Entity ent = (Entity)tm.GetObject(id, OpenMode.ForWrite, false, false);
  21.                     ent.ColorIndex = colorIndex;
  22.                 }
  23.  
  24.                 trx.Commit();
  25.             }
  26.             sw.Stop();
  27.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nStartTransaction\n");
  28.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(sw.ElapsedTicks.ToString());
  29.         }
  30.  
  31.  
  32.         [CommandMethod("TurnAllEntitesRandomColorOpenClosetransaction")]
  33.         public void TurnAllEntitesRandomColorOpenClosetransaction()
  34.         {
  35.             int colorIndex = ran.Next(1, 256);
  36.             Database db = HostApplicationServices.WorkingDatabase;
  37.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
  38.             Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  39.             sw.Reset();
  40.             sw.Start();
  41.             using (OpenCloseTransaction trx = tm.StartOpenCloseTransaction())
  42.             {
  43.                 BlockTableRecord btr = (BlockTableRecord)trx.GetObject(msId, OpenMode.ForWrite, false, false);  
  44.                
  45.                 foreach (ObjectId id in btr)
  46.                 {
  47.                     Entity ent = (Entity)trx.GetObject(id, OpenMode.ForWrite, false, false);
  48.                     ent.ColorIndex = colorIndex;
  49.                 }
  50.  
  51.                 trx.Commit();
  52.             }
  53.             sw.Stop();
  54.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nOpenCloseTransaction\n");
  55.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(sw.ElapsedTicks.ToString());
  56.         }
  57.  
  58.  
  59.         [CommandMethod("TurnAllEntitesRandomColorOpenClose")]
  60.         public void TurnAllEntitesRandomColorOpenClose()
  61.         {
  62.             int colorIndex = ran.Next(1, 256);
  63.             Database db = HostApplicationServices.WorkingDatabase;
  64.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);        
  65.             sw.Reset();
  66.             sw.Start();
  67.             BlockTableRecord btr = (BlockTableRecord)msId.Open(OpenMode.ForRead, false, false);
  68.             foreach (ObjectId id in btr)
  69.             {
  70.                 Entity ent = (Entity)id.Open(OpenMode.ForWrite, false, false);
  71.                 ent.ColorIndex = colorIndex;
  72.                 ent.Close();
  73.             }
  74.             btr.Close();
  75.             sw.Stop();
  76.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nOpenClose\n");
  77.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(sw.ElapsedTicks.ToString());
  78.         }
  79.  
  80.  
  81. /////////Added at end just to mix them
  82.  
  83.  [CommandMethod("TurnAllEntitesRandomColorOpenClosetransaction2")]
  84.         public void TurnAllEntitesRandomColorOpenClosetransaction2()
  85.         {
  86.             int colorIndex = ran.Next(1, 256);
  87.             Database db = HostApplicationServices.WorkingDatabase;
  88.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
  89.             Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  90.             sw.Reset();
  91.             sw.Start();
  92.             using (Transaction trx = tm.StartTransaction())
  93.             {
  94.                 BlockTableRecord btr = (BlockTableRecord)tm.GetObject(msId, OpenMode.ForWrite, false, false);
  95.                 foreach (ObjectId id in btr)
  96.                 {
  97.                     using (OpenCloseTransaction openCloseTrx = tm.StartOpenCloseTransaction())
  98.                     {
  99.                         Entity ent = (Entity)openCloseTrx.GetObject(id, OpenMode.ForWrite, false, false);
  100.                         ent.ColorIndex = colorIndex;
  101.                         openCloseTrx.Commit();
  102.                     }
  103.                 }
  104.  
  105.                 trx.Commit();
  106.             }
  107.             sw.Stop();
  108.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nStartTransAction&OpenCloseTransaction\n");
  109.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(sw.ElapsedTicks.ToString());
  110.         }
  111.  
  112.  
  113.  

Here were the results on drawing with
150,000
1,020,000
150,000
entities
 
For each test
-I started AutoCAD
-Opened drawing
-Netloaded test
-Ran one test
-Closed AutoCAD
 
Quote

150,000
 
OpenClose
5,369,146
 
OpenCloseTransaction
30,092,959
 
StartTransaction
10,525,513
 
StartTransAction&OpenCloseTransaction
11,047,617
 

1,020,000
 
OpenClose
37,983,450
 
OpenCloseTransaction
39,409,458
 
StartTransaction
72,692,233
 

150,000
OpenClose
5,688,127
 
OpenCloseTransaction
5,609,783
 
StartTransaction
10,439,014

Since the drawings are pretty large and not to take up Mark's space these are links to Dropbox public folder
Drawing with 15,000 Entities (https://dl.dropbox.com/u/17374049/TestDrawing_15000%20-%20Copy%20%281%29.dwg)
Drawing with 60,000 Entities (https://dl.dropbox.com/u/17374049/TestDrawing_60000%20-%20Copy%20%281%29.dwg)
Drawing with 150,000 Entities (https://dl.dropbox.com/u/17374049/TestDrawing_150000%20-%20Copy%20%281%29.dwg)
Drawing with 1,020,000 Entities (https://dl.dropbox.com/u/17374049/TestDrawing_10020000%20-%20Copy%20%281%29.dwg)
Zip with all drawings above. (https://dl.dropbox.com/u/17374049/TestDrawings.zip)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 07, 2012, 08:58:08 PM

Fenton, I've been back to re-read the couple of blog posts in question.

http://adndevblog.typepad.com/autocad/2012/07/the-right-tools-for-the-job-autocad-part-3.html

Here, Fenton Webb wrote about the poor speed LISP.
In fact, it's not so bad - it all depends on the skill of the programmer and his language skills.

Sorry, but I'm going to put my $0.02 in here, in defense of Fenton, because you've maligned him, intentionally or not.

The skill of a programmer cannot overcome inherent limitations of a programming language.

You cannot say to me that you can write LISP code that can perform complex coordinate geometry calculations faster, or even remotely close to the speed it can be done in .NET or C++. You cannot say to me that you can use an association list in LISP to lookup values associated with a key, faster than I can do that using a hashcode-based solution like the Dictionary in .NET, or the std:map in C++.  In fact, your LISP code will be 1000x slower at that even with a relatively small amount of data, compared to the equivalent .NET or C++ solution.

Quote
Three years ago, I tested the dictionary in autocad - added dictionaries for 100 mb. My program to emulate the internal dictionary as the SQL database. Speed, compared to an external database, located on the same computer that was ten times faster!

I do not think that the author is equally good command of all the above languages​...

Sorry again, but are you are talking about a kludge to achieve O(1) or hashcode-like lookup performance using AutoCAD dictionaries? Well, that solves one of the thousands of problems that are very easily solved when using the right tools for the job, doesn't it.

If on the other hand, you're comparing the performance of AutoCAD operations, rather than operations performed purely by LISP code, of course, the same AutoCAD APIs, whether they're being used from LISP, ActiveX, .NET or C++ will perform roughly the same, but Fenton was not talking about the performance of AutoCAD or its common APIs that are available to most or all programming interfaces, and he wasn't talking about kludges that use AutoCAD APIs to achieve reasonable performance either, what he was talking about is performance in the context of using APIs that are not even available to LISP, and of operations performed purely in user/client code (e.g., something that must be fully-implemented in the language being used) without kludges.

You can choose any common problem in programming that you wish, implemented in LISP, or any kludge you can find that mooches off of AutoCAD, and it will never come remotely close to the performance of native code.

How about Delaunay triangulation (http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&sqi=2&ved=0CGwQFjAA&url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FDelaunay_triangulation&ei=rLshUMOgLu2p0AGcr4DIDg&usg=AFQjCNEvH2hCZ3uZVJbM8oQRz0q-nEBY2w) ?  Would you care to give that a shot ?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 07, 2012, 09:00:04 PM
Hi all

here are my replies - sorry if it seems too short, but I need to get home to the "wife to be"...

Keith tm - "I personally don't recommend leaving unfinished tasks to the GC, instead I like to dispose of items as soon as I am done with them (or as soon as practical). Doing it this way seems straightforward if you ask me."
Hey Keith, you definitely have the right approach!

TT - "And here's something those Autodesk people didn't tell you:
Avoiding calls to Dispose() when it is unnecessary can actually improve the performance of AutoCAD and your plug-ins. The reason for that is because by default, the GC runs on a high-priority background thread, which means that on a multiple-processor system (do you know anyone that still runs AutoCAD on a single-processor system?), the GC can execute on a different (and presumably under-utilized) processor, concurrently, while AutoCAD and your plug-in run on another processor (e.g., a form of parallel execution). And, since AutoCAD doesn't leverage multiple processors, work done by the GC can be offloaded to another CPU core with virtually no performance impact on AutoCAD and your plug-in.

So, if you do follow the advice of Fenton and Stephen, the work that must be done by Dispose() will be done synchronously, requiring AutoCAD and your plug-in to wait for it to complete, which is not the case if you just allow the GC to take care of it on another under-utilized CPU core."

Hey TT, What you say is totally true, but as I have mentioned before, AutoCAD is *not* thread safe, meaning that if you allow the GC to dispose of AutoCAD objects you run the risk of some AutoCAD code being executed on something otherthan the main thread, which it was never designed for - try it and see what happens - how many of you out there get random crashes happening that you cannot track down...? If you insist on allowing the GC to dispose your AutoCAD class objects, be sure to utilize my blog entry on gcConcurrent - that's my advice.

TT - "The reason for all of the confusion in the case of AutoCAD's managed API, is that many AutoCAD managed types implement IDisposable even when it is unnecessary, and largely out of convenience. Many managed types derive from DisposableWrapper (which is the class that implements IDisposable), and in some cases, do so because they must also derive from the RXObject base type (which is derived from DisposableWrapper), because the managed class represents a native object that is an instance of AcRxObject."

Hey TT, I'll do my best to clear up any confusion where I can, I'll do this on our blog soon. In the meantime, the IDisposable interface as per the Microsoft documentation "The primary use of this interface is to release unmanaged resources". The simple fact that our .NET API wraps an unmanaged ObjectARX API, well, I personally think that generally speaking we have done things correctly. That said, I'd like to definitely hear from you which classes you think implement this interface frivolously?

dgorsman - "Do you think that total re-write could be done without significantly breaking existing code?  And in a timeframe consistent with the rate at which tools, .NET frameworks, etc. are released and retired?"

If you are talking about changing our .NET API so that IDisposable is added/removed then no - It is a version-breaking change to add the IDisposable interface to an existing class, because it changes the semantics of the class.

ElpanovEvgeniy - "Here, Fenton Webb wrote about the poor speed LISP." 
Hey ElpanovEvgeniy, I believe I said I was surprised at how fast LISP was, actually.

gile - "Do I have to dispose the plineCollection (line 18) ?

2. Do I have to dispose the regionCollection (line 25) ? And if so, do I have to dispose each region too (line 40)

3. If I dispose each region (line 40), do I need to dispose the regionCollection too (line 25) ?

4. (subsidiary) May I have used StartOpenCloseTransaction() instead of StartTransaction() (line 14) ?"

Hey gile

1) Yes
2) DBObjectCollection should automatically dispose of all 'disposable' objects held within
3) Calling Dispose on the region is fine, but it should not be needed because of (2)
4) Yes
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 07, 2012, 11:56:28 PM
How about Delaunay triangulation (http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&sqi=2&ved=0CGwQFjAA&url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FDelaunay_triangulation&ei=rLshUMOgLu2p0AGcr4DIDg&usg=AFQjCNEvH2hCZ3uZVJbM8oQRz0q-nEBY2w) ?  Would you care to give that a shot ?

Are you kidding?
Perhaps you are trying to scare me, the complexity of the algorithm?

Here, I'm forced programmers to improve the ARX triangulation that is used in products Autodesk ...
 (http://www.theswamp.org/index.php?topic=15784.100)
For complex systems, is important not the language, and the internal algorithms and simplicity.

And you, ever, made its own algorithm for Delaunay triangulation or Voronoi polygons - Chart Tisson?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 08, 2012, 01:37:15 AM
After Autodesk University I am ready to compete with you, comparing the speed of programs. Choose an interesting problem for the competition.
I am sure, the language just a tool. Speed ​​is gained by algorithms. LISP makes it easier to develop algorithms!
 

it will be hot 'CHALLENGE'  8-)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 02:57:26 AM
Quote
gile - "Do I have to dispose the plineCollection (line 18) ?

2. Do I have to dispose the regionCollection (line 25) ? And if so, do I have to dispose each region too (line 40)

3. If I dispose each region (line 40), do I need to dispose the regionCollection too (line 25) ?

4. (subsidiary) May I have used StartOpenCloseTransaction() instead of StartTransaction() (line 14) ?"

Hey gile
1) Yes
2) DBObjectCollection should automatically dispose of all 'disposable' objects held within
3) Calling Dispose on the region is fine, but it should not be needed because of (2)
4) Yes[/b]

Hi Fenton - Sorry, but WRT 2 & 3, that's not the case.

A DBObjectCollection returned by a call to an API that creates AcDbObjects initially contains only native pointers to those AcDbObjects, but contains no managed wrappers for them. The DBObjectCollection initially assumes ownership of those contained AcDbObjects, until a managed wrapper is created for one and is returned to the client code using the DBObjectCollection.

At the point when a managed wrapper is created for an AcDbObject in the collection, the DBObjectCollection relinquishes ownership of the native AcDbObject, and the client code that obtained the managed wrapper for it assumes ownership of the object, and must Dispose() it, or add it to a database. A DBObjectCollection never disposes managed wrappers.

When a DBObjectCollection is disposed or finalized, it will delete any AcDbObjects it contains, for which a managed wrapper was never created, and conversely, will not delete any AcDbObjects for which managed wrappers were created.

So, the correct answer to 3 is yes, each region managed wrapper obtained from the DBObjectCollection must be disposed or be added to a database, regardless of whether the DBObjectCollection is disposed or not.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 03:14:11 AM
After Autodesk University I am ready to compete with you, comparing the speed of programs. Choose an interesting problem for the competition.
I am sure, the language just a tool. Speed ​​is gained by algorithms. LISP makes it easier to develop algorithms!
 
it will be hot 'CHALLENGE'  8-)

I already suggested an interesting problem.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 08, 2012, 03:26:06 AM
I already suggested an interesting problem.

excellent!
You have chosen Delaunay triangulation?

I accept the challenge.
My conditions are:
1. test computer with 4 real cores or more.
2. AutoCAD 2013 x64 (without vertical product)

We play with a multi-threaded computing...

ps. I want to compare the performance of algorithms. It makes no sense to compare the performance of AutoCAD.
With impatience I will expect to December!
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 03:29:19 AM
How about Delaunay triangulation (http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&sqi=2&ved=0CGwQFjAA&url=http%3A%2F%2Fen.wikipedia.org%2Fwiki%2FDelaunay_triangulation&ei=rLshUMOgLu2p0AGcr4DIDg&usg=AFQjCNEvH2hCZ3uZVJbM8oQRz0q-nEBY2w) ?  Would you care to give that a shot ?

Are you kidding?
Perhaps you are trying to scare me, the complexity of the algorithm?

Here, I'm forced programmers to improve the ARX triangulation that is used in products Autodesk ...
 (http://www.theswamp.org/index.php?topic=15784.100)
For complex systems, is important not the language, and the internal algorithms and simplicity.

And you, ever, made its own algorithm for Delaunay triangulation or Voronoi polygons - Chart Tisson?

Yes, 15 years ago I wrote a Delaunay triangulation method for an ActiveX server that I used from LISP, Delphi, etc. But, that was long before your contest.

Now, I have parallel versions of many algorithims that run concurrently on as many CPU cores as there are in a system, and all of them run about 2-3x faster than the original (single-threaded version), on a quad-core CPU.

You see, that is where you fall down.

No algorithm will allow you to do parallel execution across multiple CPU cores (or on a GPU) using LISP, but having said that, I really don't need to resort to parallel execution to run circles around LISP code.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 03:33:11 AM
I already suggested an interesting problem.

excellent!
You have chosen Delaunay triangulation?

I accept the challenge.
My conditions are:
1. test computer with 4 real cores or more.
2. AutoCAD 2013 x64 (without vertical product)

We play with a multi-threaded computing...

ps. I want to compare the performance of algorithms. It makes no sense to compare the performance of AutoCAD.
With impatience I will expect to December!

I see, and how are you going to do parallel execution with LISP?

Oh, and you must open the source code, or no deal.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 08, 2012, 03:38:13 AM

I see, and how are you going to do parallel execution with LISP?

Oh, and you must open the source code, or no deal.

At Autodesk University in Moscow, I will tell how to use multithreading in AutoLISP ...
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Alexander Rivilis on August 08, 2012, 04:05:43 AM
Tony and Evgeniy!
And who will be the judges?  :-)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 08, 2012, 04:13:23 AM
My friend Alexander Rivilis said your name behind the nickname TT. I hasten to say, I really respect you for your contribution to programming. Indeed, the competition will be hot and fun!

ps. Yes, I will publish here all the sources. I hope you will too...
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 04:20:58 AM

I see, and how are you going to do parallel execution with LISP?

Oh, and you must open the source code, or no deal.

At Autodesk University in Moscow, I will tell how to use multithreading in AutoLISP ...

You can only do that by calling native code written in another language.  It should go without mention, that any solution must be pure LISP, and may not depend on any external API calls or any undocumented APIs.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: pkohut on August 08, 2012, 04:26:25 AM
Money on Evgeniy.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: pkohut on August 08, 2012, 04:28:39 AM
It should go without mention, that any solution must be pure LISP, and may not depend on any external API calls or any undocumented APIs.

Definitely on Evgeniy.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 04:36:27 AM

Hey TT, What you say is totally true, but as I have mentioned before, AutoCAD is *not* thread safe, meaning that if you allow the GC to dispose of AutoCAD objects you run the risk of some AutoCAD code being executed on something otherthan the main thread, which it was never designed for - try it and see what happens - how many of you out there get random crashes happening that you cannot track down...? If you insist on allowing the GC to dispose your AutoCAD class objects, be sure to utilize my blog entry on gcConcurrent - that's my advice.


I think you misunderstood me  :wink:

We already know that managed wrappers whose finalizers execute code that's not thread safe, absolutely must be disposed of determinstically, or kaboom, so I wasn't referring to those cases, but rather the cases where Dispose() is unnecessary and/or pointless, the main one being database-resident DBObjects obtained from a transaction.  If you don't dispose them, their finalizers will run on the GC's thread, on another CPU core, and will have no impact on the performance of the code that uses those managed wrappers. 

I really don't agree with the advice regarding disabling concurrent garbage collection because that requires the application to wait for it to occur, and I seriously doubt you've investigated what kind of effect a GC can have during user-interactive operations like dragging, and so forth. Covering up a problem in way that impacts performance application-wide is not a professionally-responsible way of dealing with bugs (e.g., failure to Dispose() something).  If it is done only as a temporary stopgap measure, then fine, but otherwise, slowing down their application is not a legitimate solution.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 08, 2012, 04:36:45 AM
I do not want to disclose the details to a rival.
But I am sure I will not go to the trick. My solution you are satisfied!
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 05:36:17 AM
I do not want to disclose the details to a rival.
But I am sure I will not go to the trick. My solution you are satisfied!

Details?

As in AutoCAD Core Console?

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 08, 2012, 05:57:08 AM
I do not want to disclose the details to a rival.
But I am sure I will not go to the trick. My solution you are satisfied!

Details?

As in AutoCAD Core Console?

It does not matter!
all the algorithms, calculations, logic, will only AutoLISP / Visual LISP!

Do not think that the present results in other languages ​​are invincible!
If you do measurements only of the calculations, the LISP competitive.

I am thankful that you gave me enough time to prepare.  :-)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: owenwengerd on August 08, 2012, 09:57:10 AM
Evgeniy may win the battle, but native code will always win the war. Nevertheless, I'd buy tickets to see this one!
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: LE3 on August 08, 2012, 10:03:39 AM
... I'd buy tickets to see this one!

+1
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 08, 2012, 10:04:14 AM
USA USA USA USA USA USA
.......
Tony is American?
 
 
I'll thrown down for Tony.
 
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 08, 2012, 10:09:06 AM
Losing team has post all code in VB for a month, with Option explicit and strict off?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 08, 2012, 10:44:21 AM
Losing team has post all code in VB for a month, with Option explicit and strict off?

... Way to rock out with your nerd out, Jeff!  :-P



 :lmao:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 08, 2012, 10:50:07 AM

Hey TT, What you say is totally true, but as I have mentioned before, AutoCAD is *not* thread safe, meaning that if you allow the GC to dispose of AutoCAD objects you run the risk of some AutoCAD code being executed on something otherthan the main thread, which it was never designed for - try it and see what happens - how many of you out there get random crashes happening that you cannot track down...? If you insist on allowing the GC to dispose your AutoCAD class objects, be sure to utilize my blog entry on gcConcurrent - that's my advice.


I think you misunderstood me  :wink:

We already know that managed wrappers whose finalizers execute code that's not thread safe, absolutely must be disposed of determinstically, or kaboom, so I wasn't referring to those cases, but rather the cases where Dispose() is unnecessary and/or pointless, the main one being database-resident DBObjects obtained from a transaction.  If you don't dispose them, their finalizers will run on the GC's thread, on another CPU core, and will have no impact on the performance of the code that uses those managed wrappers. 

I really don't agree with the advice regarding disabling concurrent garbage collection because that requires the application to wait for it to occur, and I seriously doubt you've investigated what kind of effect a GC can have during user-interactive operations like dragging, and so forth. Covering up a problem in way that impacts performance application-wide is not a professionally-responsible way of dealing with bugs (e.g., failure to Dispose() something).  If it is done only as a temporary stopgap measure, then fine, but otherwise, slowing down their application is not a legitimate solution.

Hey TT,

it happens 8-)

I agree with everything you say above.

In addition to your last paragraph, as I said in my blog, "Now, say you are at a customer site where they are experiencing random crashes with your app, or you are running behind schedule and just can’t seem to find the place where the random crash is happening then here’s a quick fix for you – try forcing the GC to run on the Main Thread…"
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: MexicanCustard on August 08, 2012, 11:11:48 AM


... Way to rock out with your nerd out, Jeff!  :-P

 :lmao: :lmao: :lmao:

This was a shameless posts just to put my total posts over 200 and not be a newt anymore.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: gile on August 08, 2012, 12:09:09 PM
Masters games in perspective: Fenton / Tony, Evgeny / Tony.
All this may be far away from my level...

Back to the main thread, I'm still confused: Fenton said white, Tony said black...

So I tried my own tests inspired by the way shown in the "The Stephen and Fenton Show: ADN DevCast Episode 2" (http://through-the-interface.typepad.com/through_the_interface/2010/03/the-stephen-and-fenton-show-adn-devcast-episode-2.html) about Disposing. I run the code (reply #21 (http://www.theswamp.org/index.php?topic=42399.msg476087#msg476087)) after removing disposing statements (plineCollection disposing, regionCollection disposing, each region disposing, and combinations...).

It appears that it's only when I do not dispose each region one by one that the Visual Studio stack windows shows:
"Forgot to call Dispose? (Autodesk.AutoCAD.DatabaseServices.Region): DisposableWrapper"

It looks like Tony was right, disposing the DBObjectCollection doesn't dispose the DBObject it contains for which a managed wrapper have been created.
And it seems that if the DBObjectCollection contains only database-resident objects (plineCollection) or is entirely iterated creating managed wrappers (regionCollection), it do not need to be disposed (none alert from VS in these cases).
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 02:06:24 PM
And it seems that if the DBObjectCollection contains only database-resident objects (plineCollection) or is entirely iterated creating managed wrappers (regionCollection), it do not need to be disposed (none alert from VS in these cases).

Right, but since you're working with a collection (usually using some type of loop where you operate on each item), a best practice is to always dispose the DBObjectCollection, preferably with a using() block, or try/finally, because that ensures it will not delete any unprocessed AcDbObjects in the collection on the GC's thread if your code exits prematurely as a result of an exception.

The DisposableList<T> helper class shown below simplifies the problem of ensuring that  multiple IDisposables are all disposed determinstically:

Code - C#: [Select]
  1.  
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6.  
  7. /// DisposableList<T> class
  8. /// Copyright (c) 2012  Tony Tanzillo
  9.  
  10. namespace System.Collections.Generic
  11. {
  12.  
  13.    /// DisposableList is a specialization of System.Collections.Generic.List<T> that
  14.    /// stores and manages elements that implement the IDisposable interface.
  15.    ///
  16.    /// The main purpose of DisposableList is to gurantee that all items in the list
  17.    /// are disposed when the list is disposed, deterministically, even if a call to
  18.    /// any item's Dispose() method throws an exception.
  19.    ///
  20.    /// Note that DisposableList<T> does not supress exceptions raised by calls to the
  21.    /// Dispose() method of any item, it merely ensures that any remaining 'undisposed'
  22.    /// items are disposed before the exception propagates up the call stack.
  23.    ///
  24.    /// Use DisposableList<T> exactly as you would use its base type, and when you want
  25.    /// all contained elements disposed, call it's Dispose() method.
  26.    ///
  27.    /// Using DisposableList<T> with a DBObjectCollection:
  28.    ///
  29.    /// DisposableList can be handy when working with DBObjectCollections containing
  30.    /// DBObjects that are not database-resident. It helps to ensure that if your code
  31.    /// fails before all items retreived from the collection are processed, those items
  32.    /// will be determinstically disposed, and will not have their finalizer's called
  33.    /// on the GC's thread, which could lead to a fatal error that terminates AutoCAD.
  34.    ///
  35.    /// In addition, because DisposableList is a strongly-typed collection, you can
  36.    /// avoid repetitively casting items to the type you need to work with, and can
  37.    /// more easily use the contents with LINQ.
  38.    ///
  39.    /// For example, you can pull all the items out of a DBObjectCollection from a
  40.    /// call to the DisposableList's constructor overload that takes an untyped
  41.    /// System.Collections.IEnumerable, and subsequently work exclusively with the
  42.    /// the DisposableList rather than the DBObjectCollection:
  43.    ///
  44.    ///    using( DBObjectCollection items = myCurve.GetSplitCurves(...) )
  45.    ///    using( DisposableList<Curve> curves = new DisposableList<Curve>( items ) )
  46.    ///    {
  47.    ///       // Work with each Curve in the curves list.
  48.    ///       // Once control leaves this using() block, all
  49.    ///       // items in the curves list will be disposed.
  50.    ///    }
  51.    ///
  52.  
  53.    public class DisposableList<T> : List<T>, IDisposable
  54.       where T : IDisposable
  55.    {
  56.       bool disposed = false;
  57.  
  58.       /// <summary>
  59.       ///
  60.       /// If true, contained items are disposed of in reverse order
  61.       /// (last to first). Otherwise, items are disposed in the order
  62.       /// they appear in the list.
  63.       ///
  64.       /// </summary>
  65.  
  66.       bool ReverseOrderedDispose
  67.       {
  68.          get;
  69.          set;
  70.       }
  71.  
  72.       public DisposableList()
  73.       {
  74.       }
  75.  
  76.       public DisposableList( int capacity )
  77.          : base( capacity )
  78.       {
  79.       }
  80.  
  81.       public DisposableList( IEnumerable<T> collection )
  82.          : base( collection )
  83.       {
  84.       }
  85.  
  86.       public DisposableList( System.Collections.IEnumerable collection )
  87.          : base( collection.Cast<T>() )
  88.       {
  89.       }
  90.  
  91.       public void Dispose()
  92.       {
  93.          Dispose( true );
  94.       }
  95.  
  96.       protected virtual void Dispose( bool disposing )
  97.       {
  98.          bool flag = this.disposed;
  99.          this.disposed = true;
  100.          if( disposing && !flag )
  101.          {
  102.             if( this.ReverseOrderedDispose )
  103.                base.Reverse();
  104.             using( IEnumerator<T> e = base.GetEnumerator() )
  105.                DisposeItems( e );
  106.          }
  107.       }
  108.  
  109.       private void DisposeItems( IEnumerator<T> e )
  110.       {
  111.          while( e.MoveNext() )
  112.          {
  113.             try
  114.             {
  115.                T item = e.Current;
  116.                if( item != null )
  117.                   item.Dispose();
  118.             }
  119.             catch
  120.             {
  121.                DisposeItems( e );
  122.                throw;
  123.             }
  124.          }
  125.       }
  126.    }
  127. }
  128.  
  129.  
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: gile on August 08, 2012, 02:30:33 PM
Thanks for the clarifiation Tony.
So, the 'good' way is as in the code I posted reply #21. :kewl:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 08, 2012, 02:44:27 PM
Hey Gile

thanks for looking into that.

I personally consider that a bug... However, it looks like we can't address it really because 'fixing' it would risk logically affecting all that are using it (Disposing something that was not originally expected to be Disposed).

So anyway, back to my original point - call Dispose() on all objects that get created by you (what was I thinking advising otherwise) :-)

Hi ElpanovEvgeniy, I love your ideas, but the AutoCAD LISP Engine (an ObjectARX DLL) is laced with global variables and it's not thread safe.

Sorry guys, I have to get back to the ADNBlog - so if you have any further comments for me directly please add them to the relevant post there and I'll be glad to answer.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 02:51:29 PM
My friend Alexander Rivilis said your name behind the nickname TT. I hasten to say, I really respect you for your contribution to programming. Indeed, the competition will be hot and fun!

ps. Yes, I will publish here all the sources. I hope you will too...

Thanks.

But, if your intention is to use AutoCAD Core Console as a form of parallel execution, I would not consider that to be a legitimate solution (that is technically multi-processing rather than multi-threading).

Since the LISP garbage collector is not capable of dealing with multiple threads, even if you did find some way to get an entry point on another thread, it would certainly fail given the appropriate stress-testing.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 08, 2012, 03:03:57 PM

Hey TT, What you say is totally true, but as I have mentioned before, AutoCAD is *not* thread safe, meaning that if you allow the GC to dispose of AutoCAD objects you run the risk of some AutoCAD code being executed on something otherthan the main thread, which it was never designed for - try it and see what happens - how many of you out there get random crashes happening that you cannot track down...? If you insist on allowing the GC to dispose your AutoCAD class objects, be sure to utilize my blog entry on gcConcurrent - that's my advice.


I think you misunderstood me  :wink:

We already know that managed wrappers whose finalizers execute code that's not thread safe, absolutely must be disposed of determinstically, or kaboom, so I wasn't referring to those cases, but rather the cases where Dispose() is unnecessary and/or pointless, the main one being database-resident DBObjects obtained from a transaction.  If you don't dispose them, their finalizers will run on the GC's thread, on another CPU core, and will have no impact on the performance of the code that uses those managed wrappers. 

I really don't agree with the advice regarding disabling concurrent garbage collection because that requires the application to wait for it to occur, and I seriously doubt you've investigated what kind of effect a GC can have during user-interactive operations like dragging, and so forth. Covering up a problem in way that impacts performance application-wide is not a professionally-responsible way of dealing with bugs (e.g., failure to Dispose() something).  If it is done only as a temporary stopgap measure, then fine, but otherwise, slowing down their application is not a legitimate solution.

Hey TT,

it happens 8-)

I agree with everything you say above.

In addition to your last paragraph, as I said in my blog, "Now, say you are at a customer site where they are experiencing random crashes with your app, or you are running behind schedule and just can’t seem to find the place where the random crash is happening then here’s a quick fix for you – try forcing the GC to run on the Main Thread…"

Again, my comments are not so much about whether your advice and comments are technically correct, but rather in how that advice and comments are presented and interpreted or misinterpreted. E.g. "quick fix" may be viewed as 'permanent' to some, especially if they don't understand or realize that it can have performance implications.

So while it is perfectly acceptable to disable concurrent GCs until the bugs are found and fixed, it should also be stressed that it is not acceptable as a permanent solution.

Communicating ideas, concepts, and skills in a way that is clear and well-understood by both experts and novices alike, is as much an art as writing the code that embodies the ideas, concepts and skills.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 08, 2012, 03:24:07 PM
If you were tasked for writing code to just run in the CoreConsole and knew it would never be used with acad.exe(I know does not make much sense) and normally would have a coreConsoles running for each core at the same time would that be good candidate for setting gcConcurrent flag and not worrying about disposing everything?
 
 
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 09, 2012, 01:56:59 AM
My friend Alexander Rivilis said your name behind the nickname TT. I hasten to say, I really respect you for your contribution to programming. Indeed, the competition will be hot and fun!

ps. Yes, I will publish here all the sources. I hope you will too...

Thanks.

But, if your intention is to use AutoCAD Core Console as a form of parallel execution, I would not consider that to be a legitimate solution (that is technically multi-processing rather than multi-threading).

Since the LISP garbage collector is not capable of dealing with multiple threads, even if you did find some way to get an entry point on another thread, it would certainly fail given the appropriate stress-testing.

If you recall the story, the original lisp no one was interested, until the show concurrent execution in a network of computers. After that, two years later, was written specification language and the language was fully certified for any use, including defense.

You know Lisp. You should be clear that Lisp is the language of the lists. The meaning of the program - sequential passage through the list items and their computation. But in the lists may be other lists of data and programs. Each calculation can be performed at its core (thread or processor or computer).

Yes, I'm going to use the console AutoCAD.  :-)

The deal is really unfair?  :?

ps. To outperform existing codes on the arx. It should be very much to sweat...
You really do not want such an easy victory? Do you have a wide range of languages, I only lisp. Do you have a wide selection of tools for parallel computing, I have not.
The deal is really canceled?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 09, 2012, 02:09:35 PM

Yes, I'm going to use the console AutoCAD.  :-)


Well hey then, if that sort of solution is acceptable then why pussy-foot around? 

Why not just do it in the cloud ?  :lmao:

Sorry, no deal.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 09, 2012, 02:29:21 PM

Sorry, no deal.

@TT -

Now that this challenge has been answered, I believe there is another challenge (or offer?) previously posed, that has gone unanswered  :? :

<snip>

So "TT" - how about you come to Autodesk University this year? We can do a special fun coding class - "Fenton Webb vs TT in AutoCAD .NET Coding Head to Head" - the audience chooses what we code, let's see who does the best job in the shortest time? Come on, it will be fun! I'm sure you will win

Pending any personal conflicts of course, would this be something you're willing to entertain?

... I'd very much be interested in the result of this sort of demonstration. Perhaps, if there's enough interest from other members, ADN would even step up, and fly you out?  :angel: Just saying.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: MexicanCustard on August 09, 2012, 03:41:57 PM

Yes, I'm going to use the console AutoCAD.  :-)


Well hey then, if that sort of solution is acceptable then why pussy-foot around? 

Why not just do it in the cloud ?  :lmao:

Sorry, no deal.

I'm lost, so this was going to be in native AutoLisp but just running in the command console instead of full blown AutoCAD?  Is he using multiple command consoles?  Wouldn't native code still be faster multithreading?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 09, 2012, 04:14:59 PM

Yes, I'm going to use the console AutoCAD.  :-)


Well hey then, if that sort of solution is acceptable then why pussy-foot around? 

Why not just do it in the cloud ?  :lmao:

Sorry, no deal.

I'm lost, so this was going to be in native AutoLisp but just running in the command console instead of full blown AutoCAD?  Is he using multiple command consoles?  Wouldn't native code still be faster multithreading?

Yes, given comparable algorithms, native code is definitely faster, and may even be faster without parallelism.

Algorithms can be implemented in any language. In fact, most of the best algorithms were not implemented in LISP, and certainly not in AutoLISP, which lacks even the most-basic facilities like arrays. And, because most proven algorithms are developed using languages that do have things like arrays, hashtables, structs, and so on, they tend to be somewhat dependent on those languages.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 09, 2012, 04:17:45 PM

Sorry, no deal.

@TT -

Now that this challenge has been answered, I believe there is another challenge (or offer?) previously posed, that has gone unanswered  :? :

<snip>

So "TT" - how about you come to Autodesk University this year? We can do a special fun coding class - "Fenton Webb vs TT in AutoCAD .NET Coding Head to Head" - the audience chooses what we code, let's see who does the best job in the shortest time? Come on, it will be fun! I'm sure you will win

Pending any personal conflicts of course, would this be something you're willing to entertain?

... I'd very much be interested in the result of this sort of demonstration. Perhaps, if there's enough interest from other members, ADN would even step up, and fly you out?  :angel: Just saying.

Fenton has access to and is quite familiar with the AutoCAD and ObjectARX source code bases.

Maybe it's true that there's a sucker born every minute, but I ain't one of 'em :grin:

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: JohnK on August 09, 2012, 04:32:23 PM
...
Sorry, no deal.
Se7en throws down his pop-corn and gets up to ask for a refund to the show. Damn!?

...
Maybe it's true that there's a sucker born every minute, but I ain't one of 'em :grin:
*lmao*  ...Fall off the turnip truck at least?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 09, 2012, 04:33:28 PM

Sorry, no deal.

@TT -

Now that this challenge has been answered, I believe there is another challenge (or offer?) previously posed, that has gone unanswered  :? :

<snip>

So "TT" - how about you come to Autodesk University this year? We can do a special fun coding class - "Fenton Webb vs TT in AutoCAD .NET Coding Head to Head" - the audience chooses what we code, let's see who does the best job in the shortest time? Come on, it will be fun! I'm sure you will win

Pending any personal conflicts of course, would this be something you're willing to entertain?

... I'd very much be interested in the result of this sort of demonstration. Perhaps, if there's enough interest from other members, ADN would even step up, and fly you out?  :angel: Just saying.

Fenton has access to and is quite familiar with the AutoCAD and ObjectARX source code bases.

Maybe it's true that there's a sucker born every minute, but I ain't one of 'em :grin:

Well, I certainly was not suggesting that you were... Just thought it would make for an epic dual (with Tron Legacy music in the background).  :-)

Cheers, TT! :beer:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: MexicanCustard on August 09, 2012, 05:04:49 PM

Yes, I'm going to use the console AutoCAD.  :-)


Well hey then, if that sort of solution is acceptable then why pussy-foot around? 

Why not just do it in the cloud ?  :lmao:

Sorry, no deal.

I'm lost, so this was going to be in native AutoLisp but just running in the command console instead of full blown AutoCAD?  Is he using multiple command consoles?  Wouldn't native code still be faster multithreading?

Yes, given comparable algorithms, native code is definitely faster, and may even be faster without parallelism.

Algorithms can be implemented in any language. In fact, most of the best algorithms were not implemented in LISP, and certainly not in AutoLISP, which lacks even the most-basic facilities like arrays. And, because most proven algorithms are developed using languages that do have things like arrays, hashtables, structs, and so on, they tend to be somewhat dependent on those languages.

Thank goodness!  I thought that LISP had won and was gonna have to shoot myself in the head. :cry:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 09, 2012, 06:01:23 PM
...
Sorry, no deal.
Se7en throws down his pop-corn and gets up to ask for a refund to the show. Damn!?

...
Maybe it's true that there's a sucker born every minute, but I ain't one of 'em :grin:
*lmao*  ...Fall off the turnip truck at least?

I refuse to entertain the sort of kludgery that the typical LISP die-hard is willing to resort to in order to justify their existence. :grin:

As far as Fenton goes, give me access to the same things he has access to and I'll clean his clock. :grin:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: mohnston on August 09, 2012, 11:46:17 PM

I refuse to entertain the sort of kludgery that the typical LISP die-hard is willing to resort to in order to justify their existence. :grin:

As far as Fenton goes, give me access to the same things he has access to and I'll clean his clock. :grin:

Best thread in ages. I'm not in their league so I can't form an intelligent opinion but I love the debate/discussion.

What great timing! An Olympic Code Writing event? USA vs Russia vs Great Britain vs India vs ?? That would be fantastic and SO educational. Of course the results would be debatable which makes it all the more interesting.

So where were we? . . . . oh yeah . . . gauntlet thrown.
Please continue.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 10, 2012, 02:44:05 AM
My friend Alexander Rivilis said your name behind the nickname TT. I hasten to say, I really respect you for your contribution to programming. Indeed, the competition will be hot and fun!

ps. Yes, I will publish here all the sources. I hope you will too...

Thanks.

But, if your intention is to use AutoCAD Core Console as a form of parallel execution, I would not consider that to be a legitimate solution (that is technically multi-processing rather than multi-threading).

Since the LISP garbage collector is not capable of dealing with multiple threads, even if you did find some way to get an entry point on another thread, it would certainly fail given the appropriate stress-testing.

If you recall the story, the original lisp no one was interested, until the show concurrent execution in a network of computers. After that, two years later, was written specification language and the language was fully certified for any use, including defense.

You know Lisp. You should be clear that Lisp is the language of the lists. The meaning of the program - sequential passage through the list items and their computation. But in the lists may be other lists of data and programs. Each calculation can be performed at its core (thread or processor or computer).


I've noticed that many AutoLISP die-hard enthusiasts, when confronted with the usual criticism about their favorite programming language, often resort to the tactic of confusing AutoLISP with ANSI Common LISP or various other full-featured LISP development tools. What you say about the latter is true - many of them can compile to native code that can run as fast or faster than code compiled from C/C++.

But, please do not confuse those development tools with AutoLISP, which is lame by comparison.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: It's Alive! on August 10, 2012, 02:53:20 AM
Way to go TT, now your implying lisp hacks may out perform native hacks  :lol:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 10, 2012, 03:12:26 AM
If you were tasked for writing code to just run in the CoreConsole and knew it would never be used with acad.exe(I know does not make much sense) and normally would have a coreConsoles running for each core at the same time would that be good candidate for setting gcConcurrent flag and not worrying about disposing everything?

With relatively-full CPU utilization (all cores that is), and running multiple ACC's, you are generally not waiting for things to happen, so I don't think it would matter that much. The idea behind allowing the GC to run concurrently in AutoCAD is because the typical AutoCAD workflow involves one interactive process that's not making good use of multiple processors, and so there is likely one or more under-utilized processors that can do the garbage collection without impacting AutoCAD use.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 10, 2012, 03:13:50 AM
I've noticed that many AutoLISP die-hard enthusiasts, when confronted with the usual criticism about their favorite programming language, often resort to the tactic of confusing AutoLISP with ANSI Common LISP or various other full-featured LISP development tools. What you say about the latter is true - many of them can compile to native code that can run as fast or faster than code compiled from C/C++.

But, please do not confuse those development tools with AutoLISP, which is lame by comparison.

I do not understand ...
You have refused to match, but stubbornly continue to claim that I want you to cheat and use other languages.
You have all the languages except AutoLISP. I only lisp. Together, we can use all the features of AutoCAD latest version. - These are the conditions.

My opinion is that the large-scale problems, the implementation of complex algorithms in LISP, is much easier and faster. During the allotted time, I can easily develop a full-fledged work of eight of computing power while working at a result. Includes all necessary synchronization and data transfer. The result is supposed to be a fairly simple and straightforward. So, I declare that in other languages​​, such a task is very difficult to implement! I think the difference in the elaboration of the algorithm is completely exclude the quality of the code in other languages ​​and Lisp have a chance to win...

ps. The only thing I dislike about selected to match the problem - my best algorithms for the program has long been known and copied in other languages​​...
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: ElpanovEvgeniy on August 10, 2012, 03:24:37 AM
in my defense I say - Lisp is really slower than other languages ​​used in AutoCAD. But Lisp algorithmic language and it can be quickly and easily develop complex algorithms.
In other words - I am sure that over the same time, Lisp and other languages ​​like C, Lisp is a high quality program.

According to my estimates, quality code in LISP and ARX - the difference:
2000 - 4000% for the input - output
9% - 140% for mathematics and logic

about any hundreds of times one can not speak!  :kewl:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 11, 2012, 01:26:26 AM
Hey guys

a couple of things...

First of all... I posted a new Performance post here http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html

Second of all, if you are in any way confused when to Dispose or not, I have an idea - how about you do *exactly* what TT said not to do (right at the beginning of this post) Dispose absolutely everything!!!! I'm not disrespecting you TT, I'm just running with an idea...

I figure the worst that can happen is your code will cause AutoCAD to simply crash - the good thing though about this approach is, if it crashes it should crash right there and then! I think that is much better than crashing some random place and time when the GC decides kicks in. Also, if it does crash then just tell me! That's because it's a bug, and I'll get it fixed so that it doesn't crash - how does that sound?



Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: gile on August 11, 2012, 01:36:19 PM
Quote
Second of all, if you are in any way confused when to Dispose or not, I have an idea - how about you do *exactly* what TT said not to do (right at the beginning of this post) Dispose absolutely everything!!!!

You're jocking Fenton !... :-o

I think what I and others newbies are waiting for is a definitive and clear explaination of:
- what absolutely needs to be explictely disposed,
- what doesn't need to be disposed,
- what is a safer practice to dispose to prevent unexcepted situations(i.e. an exception occurs).
I think this could be done with simple and clear samples provided in the AutoCAD .NET Developper's Guide, ObjectARX docs or any other Autodesk publications.

Here's a sample example of what I mean.
It covers nearly all I think I finally understood about this since I started AutoCAD .NET self-teaching.

Code - C#: [Select]
  1.         public void ExtrudePline()
  2.         {
  3.             // Never dispose Document nor Document.Editor
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Editor ed = doc.Editor;
  6.  
  7.             // Never dispose Database when the drawing is opened in the Editor
  8.             // On the other hand, always dispose Database created with the constructor, i.e. new Database()
  9.             Database db = doc.Database;
  10.  
  11.             // Always dispose DocumentLock
  12.             using (doc.LockDocument())
  13.             // Always dispose Transaction
  14.             using (Transaction tr = db.TransactionManager.StartTransaction())
  15.             {
  16.                 // No need to dispose a DBObject opened from a transaction
  17.                 BlockTableRecord currentSpace =
  18.                     (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  19.  
  20.                 // Always dispose a new DBObject which may not be added to the database
  21.                 using (Polyline pline = new Polyline())
  22.                 {
  23.                     pline.AddVertexAt(0, Point2d.Origin, 0.0, 0.0, 0.0);
  24.                     pline.AddVertexAt(1, new Point2d(10.0, 0.0), 0.0, 0.0, 0.0);
  25.                     pline.AddVertexAt(2, new Point2d(10.0, 10.0), 1.0, 0.0, 0.0);
  26.                     pline.AddVertexAt(3, new Point2d(0.0, 10.0), 0.0, 0.0, 0.0);
  27.                     pline.Closed = true;
  28.  
  29.                     // Dispose DBObjectCollection in case there're some objects left
  30.                     // for which no managed wrapper have been created
  31.                     using (DBObjectCollection plineCollection = new DBObjectCollection())
  32.                     {
  33.                         plineCollection.Add(pline);
  34.  
  35.                         // Dispose DBObjectCollection in case there're some objects left
  36.                         // for which no managed wrapper have been created
  37.                         using (DBObjectCollection regionCollection =
  38.                             Region.CreateFromCurves(plineCollection))
  39.                         {
  40.                             // Always dispose an object contained in a DBObjectCollection
  41.                             // for which a managed wrapper is created and isn't added to the database
  42.                             using (Region region = (Region)regionCollection[0])
  43.                             {
  44.                                 // Use Dispose to insure the new DBObject will be disposed
  45.                                 // if an exception occurs before it is added to the Database
  46.                                 using (Solid3d solid = new Solid3d())
  47.                                 {
  48.                                     solid.Extrude(region, 30.0, 0.0);
  49.                                     currentSpace.AppendEntity(solid);
  50.                                     tr.AddNewlyCreatedDBObject(solid, true);
  51.                                 }
  52.                             }
  53.                         }
  54.                     }
  55.                     if ((short)Application.GetSystemVariable("DELOBJ") == 0)
  56.                     {
  57.                         currentSpace.AppendEntity(pline);
  58.                         tr.AddNewlyCreatedDBObject(pline, true);
  59.                     }
  60.                 }
  61.                 tr.Commit();
  62.             }
  63.         }

Code - vb.net: [Select]
  1.         Public Sub ExtrudePline()
  2.             ' Never dispose Document nor Document.Editor
  3.             Dim doc As Document = Application.DocumentManager.MdiActiveDocument
  4.             Dim ed As Editor = doc.Editor
  5.  
  6.             ' Never dispose Database when the drawing is opened in the Editor
  7.             ' On the other hand, always dispose Database created with the constructor (i.e. New Database())
  8.             Dim db As Database = doc.Database
  9.  
  10.             ' Always dispose DocumentLock
  11.             Using doc.LockDocument()
  12.  
  13.                 ' Always dispose Transaction
  14.                 Using tr As Transaction = db.TransactionManager.StartTransaction()
  15.  
  16.                     ' No need to dispose a DBObject opened from a transaction
  17.                     Dim currentSpace As BlockTableRecord = _
  18.                         DirectCast(tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord)
  19.  
  20.                     ' Always dispose a new DBObject which may not be added to the database
  21.                     Using pline As New Polyline()
  22.                         pline.AddVertexAt(0, Point2d.Origin, 0.0, 0.0, 0.0)
  23.                         pline.AddVertexAt(1, New Point2d(10.0, 0.0), 0.0, 0.0, 0.0)
  24.                         pline.AddVertexAt(2, New Point2d(10.0, 10.0), 1.0, 0.0, 0.0)
  25.                         pline.AddVertexAt(3, New Point2d(0.0, 10.0), 0.0, 0.0, 0.0)
  26.                         pline.Closed = True
  27.  
  28.                         ' Dispose DBObjectCollection in case there're some objects left
  29.                         ' for which no managed wrapper have been created
  30.                         Using plineCollection As New DBObjectCollection()
  31.                             plineCollection.Add(pline)
  32.  
  33.                             ' Dispose DBObjectCollection in case there're some objects left
  34.                             ' for which no managed wrapper have been created
  35.                             Using regionCollection As DBObjectCollection = _
  36.                                 Region.CreateFromCurves(plineCollection)
  37.  
  38.                                 ' Always dispose an object contained in a DBObjectCollection
  39.                                 ' for which a managed wrapper is created and isn't added to the database
  40.                                 Using region As Region = DirectCast(regionCollection(0), Region)
  41.  
  42.                                     ' Use Dispose to insure the new DBObject will be disposed
  43.                                     ' if an exception occurs before it is added to the Database
  44.                                     Using solid As New Solid3d()
  45.                                         solid.Extrude(region, 30.0, 0.0)
  46.                                         currentSpace.AppendEntity(solid)
  47.                                         tr.AddNewlyCreatedDBObject(solid, True)
  48.                                     End Using
  49.                                 End Using
  50.                             End Using
  51.                         End Using
  52.                         If CShort(Application.GetSystemVariable("DELOBJ")) = 0 Then
  53.                             currentSpace.AppendEntity(pline)
  54.                             tr.AddNewlyCreatedDBObject(pline, True)
  55.                         End If
  56.                     End Using
  57.                     tr.Commit()
  58.                 End Using
  59.             End Using
  60.         End Sub
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 11, 2012, 08:54:07 PM

First of all... I posted a new Performance post here http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html

< .. >

Fenton, The DBObject class in DatabaseServices namespace thinks that
Quote
Autodesk.AutoCAD.DatabaseServices.DBObject.Close()' is obsolete: 'Use Transaction instead'   


Autodesk.AutoCAD.DatabaseServices.ObjectId.Open(Autodesk.AutoCAD.DatabaseServices.OpenMode)' is obsolete: 'For advanced use only. Use GetObject instead'   

It seems that your personal preference to use Open/Close is contrary to the class designer's intent.



Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 11, 2012, 09:48:43 PM
My Results using the drawings from Post Reply#31
Thanks to Jeff H for the Post.

Note that the returned value is Stopwatch.Ticks not DateTime.Ticks.( see note)

TestDrawing_150000 .dwg
Command: OPENCLOSETRANSACTION
2018 143
2012 762
2011 954
2005 563
2032 259
Command: OPENCLOSE
2202 859
2130 969
2166 854
2199 823
Command: STARTTRANSACTION
5209 462
5451 483
5339 095
5300 119
5247 409
Command: IDGETOBJECT
6594 048
6607 023
6550 874
6545 139
6486 834


This test simply Opens each entity and changes it's color to match a random number.
I imagine that most operations would reflect a similar differential.


note
http://geekswithblogs.net/BlackRabbitCoder/archive/2012/01/12/c.net-little-pitfalls-stopwatch-ticks-are-not-timespan-ticks.aspx
Quote
In short, remember that the ticks in Stopwatch are machine/OS dependent, thus you should never count on the ration of Stopwatch ticks to seconds to be the same between two systems, and possibly even on the same system after a reboot.  Thus, you can never count on Stopwatch ticks to be the same interval as DateTime/TimeSpan ticks.

To get system-independent time, make sure to use the Stopwatch’s Elapsed or ElapsedMilliseconds properties, which already take the Stopwatch.Frequency (ticks per second) into account.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 11, 2012, 10:15:22 PM
Quote from: http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html

About The Transaction object in AutoCAD... The Transaction model (StartTransaction()) was invented way back when for a specific reason - transacting multiple writes on the same objects(s) and allowing layered rollbacks of these multi-write transactions.

Here’s what I recommend:
If you guys are using StartTransaction(),
and you don’t need the multiple write feature I just mentioned in the above paragraph,
simply change your StartTransaction() to StartOpenCloseTransaction()…


Is anyone able to clarify the distinction regarding transacting multiple writes on the same objects(s)

Regards
Kerry
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 11, 2012, 10:45:38 PM
!... :-o

I think what I and others newbies are waiting for is a definitive and clear explaination of:
- what absolutely needs to be explictely disposed,
- what doesn't need to be disposed,
- what is a safer practice to dispose to prevent unexcepted situations(i.e. an exception occurs).
I think this could be done with simple and clear samples provided in the AutoCAD .NET Developper's Guide, ObjectARX docs or any other Autodesk publications.

< .. >



votes += mine;

Thanks gile,

Regards
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 11, 2012, 11:32:58 PM
First off thanks for repling Fenton, your input is greatly appreciated
 

 
.......
Note that the returned value is Stopwatch.Ticks not DateTime.Ticks.( see note)
.......

note
http://geekswithblogs.net/BlackRabbitCoder/archive/2012/01/12/c.net-little-pitfalls-stopwatch-ticks-are-not-timespan-ticks.aspx (http://geekswithblogs.net/BlackRabbitCoder/archive/2012/01/12/c.net-little-pitfalls-stopwatch-ticks-are-not-timespan-ticks.aspx)
Quote
In short, remember that the ticks in Stopwatch are machine/OS dependent, thus you should never count on the ration of Stopwatch ticks to seconds to be the same between two systems, and possibly even on the same system after a reboot. Thus, you can never count on Stopwatch ticks to be the same interval as DateTime/TimeSpan ticks.

To get system-independent time, make sure to use the Stopwatch’s Elapsed or ElapsedMilliseconds properties, which already take the Stopwatch.Frequency (ticks per second) into account.
Oops Thanks!
The thing that makes me face palm more than anything else is the crap I slapped together here (http://www.theswamp.org/index.php?topic=42399.msg476087#msg476087) was performing better than OpenCloseTransaction, and of course it has not been throughly tested and was just a simple test. Code not updated for stopwatch as Kerry noted. Thanks again!
Code - C#: [Select]
  1.         [CommandMethod("TurnAllEntitesRandomRollYorOwn")]
  2.         public void TurnAllEntitesRandomRollYorOwn() // This method can have any name
  3.         {
  4.             int colorIndex = ran.Next(1, 256);
  5.             Database db = HostApplicationServices.WorkingDatabase;
  6.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
  7.             Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  8.             sw.Reset();
  9.             sw.Start();
  10.             using (ITransaction trx = db.StartRollYourOwnTransaction())
  11.             {
  12.                 BlockTableRecord btr = (BlockTableRecord)trx.GetObject(msId, OpenMode.ForRead, false, false);
  13.                 foreach (ObjectId id in btr)
  14.                 {
  15.                     Entity ent = (Entity)trx.GetObject(id, OpenMode.ForWrite, false, false);
  16.                     ent.ColorIndex = colorIndex;
  17.                 }
  18.  
  19.                 trx.Commit();
  20.             }
  21.             sw.Stop();
  22.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\nRollYorOwn\n");
  23.             Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage(sw.ElapsedTicks.ToString());
  24.         }
  25.  
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 12, 2012, 12:04:04 AM
I have code that plays a squeaker fart noise if I forget to call dispose on object(squeezing the last bit of life out of it) which goes to show you can do anything inside dispose method, and no reason to reply what an ingenious idea that is as it is already understood.
 
So to look at what happens when a line is disposed I can follow the different paths except for one part in DeleteUnmanagedObject.
Where it checks if its ObjectId is null and if so then if the DBObject is Not null I am not sure what that does?
calli (http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.calli.aspx)
Code - C#: [Select]
  1.  
  2.  
  3.         ////    DBObject
  4.         protected unsafe override void DeleteUnmanagedObject()
  5.         {
  6.             AcDbObject* impObj = this.GetImpObj();
  7.             AcDbObjectId acDbObjectId;
  8.             if (((*<Module>.AcDbObject.objectId(impObj, &acDbObjectId) == 0L) ? 1 : 0) != 0)
  9.             {
  10.                 if (impObj != null)
  11.                 {
  12.                     object arg_24_0 = calli(System.Void* modopt(System.Runtime.CompilerServices.CallConvCdecl)(System.IntPtr,System.UInt32), impObj, 1, *(*(long*)impObj));
  13.                 }
  14.             }
  15.             else
  16.             {
  17.                 int num = (int)<Module>.AcDbObject.close(this.GetImpObj());
  18.                 if (num != 0)
  19.                 {
  20.                     throw new Autodesk.AutoCAD.Runtime.Exception((ErrorStatus)num);
  21.                 }
  22.             }
  23.         }
  24.  
  25.  
  26.  
  27.  

I think I got all the methods used for a line used when Disposed is called
Code - C#: [Select]
  1.  
  2.         ////    DisposableWrapper
  3.         public sealed override void Dispose()
  4.         {
  5.             this.Dispose(true);
  6.             GC.SuppressFinalize(this);
  7.         }
  8.  
  9.  
  10.         ////    DisposableWrapper
  11.         protected virtual void Dispose([MarshalAs(UnmanagedType.U1)] bool flag)
  12.         {
  13.             if (flag)
  14.             {
  15.                 this.~DisposableWrapper();
  16.             }
  17.             else
  18.             {
  19.                 try
  20.                 {
  21.                     this.!DisposableWrapper();
  22.                 }
  23.                 finally
  24.                 {
  25.                     base.Finalize();
  26.                 }
  27.             }
  28.         }
  29.  
  30.         ////    DisposableWrapper
  31.         private void ~DisposableWrapper()
  32.         {
  33.             this.!DisposableWrapper();
  34.         }
  35.  
  36.         ////    DisposableWrapper
  37.         private void !DisposableWrapper()
  38.         {
  39.             if (this.m_bAutoDelete && this.m_imp != IntPtr.Zero)
  40.             {
  41.                 this.DeleteUnmanagedObject();
  42.             }
  43.             this.Detach();
  44.         }
  45.  
  46.  
  47.  
  48.         ////    DisposableWrapper
  49.         internal void Detach()
  50.         {
  51.             this.m_imp = IntPtr.Zero;
  52.             if (this.m_bAutoDelete)
  53.             {
  54.                 GC.SuppressFinalize(this);
  55.             }
  56.             this.m_bAutoDelete = false;
  57.             GC.KeepAlive(this);
  58.         }
  59.  
  60.         ////    DBObject
  61.         protected override void Dispose([MarshalAs(UnmanagedType.U1)] bool flag)
  62.         {
  63.             if (flag)
  64.             {
  65.                 try
  66.                 {
  67.                     return;
  68.                 }
  69.                 finally
  70.                 {
  71.                     base.Dispose(true);
  72.                 }
  73.             }
  74.             try
  75.             {
  76.                 this.!DBObject();
  77.             }
  78.             finally
  79.             {
  80.                 base.Dispose(false);
  81.             }
  82.         }
  83.  
  84.         ////    DBObject
  85.         protected unsafe override void DeleteUnmanagedObject()
  86.         {
  87.             AcDbObject* impObj = this.GetImpObj();
  88.             AcDbObjectId acDbObjectId;
  89.             if (((*<Module>.AcDbObject.objectId(impObj, &acDbObjectId) == 0L) ? 1 : 0) != 0)
  90.             {
  91.                 if (impObj != null)
  92.                 {
  93.                     object arg_24_0 = calli(System.Void* modopt(System.Runtime.CompilerServices.CallConvCdecl)(System.IntPtr,System.UInt32), impObj, 1, *(*(long*)impObj));
  94.                 }
  95.             }
  96.             else
  97.             {
  98.                 int num = (int)<Module>.AcDbObject.close(this.GetImpObj());
  99.                 if (num != 0)
  100.                 {
  101.                     throw new Autodesk.AutoCAD.Runtime.Exception((ErrorStatus)num);
  102.                 }
  103.             }
  104.         }
  105.  
  106.         ////    DBObject
  107.         public void Close()
  108.         {
  109.             int num = (int)<Module>.AcDbObject.close(this.GetImpObj());
  110.             if (num != 0)
  111.             {
  112.                 throw new Autodesk.AutoCAD.Runtime.Exception((ErrorStatus)num);
  113.             }
  114.             base.Detach();
  115.         }
  116.  
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 12, 2012, 12:11:18 AM
I have code that plays a squeaker fart noise if I forget to call dispose on object(squeezing the last bit of life out of it) which goes to show you can do anything inside dispose method, and no reason to reply what an ingenious idea that is as it is already understood.
 
So to look at what happens when a line is disposed I can follow the different paths except for one part in DeleteUnmanagedObject.
Where it checks if its ObjectId is null and if so then if the DBObject is Not null I am not sure what that does?
calli (http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.calli.aspx)


First it checks to see if the ObjectId is null, which means the object is not database-resident.  If the object is not database-resident then it deletes the wrapped AcDbObject.

If the object is database-resident then it calls AcDbObject::close(), which doesn't actually close objects that are transaction-resident.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 12, 2012, 01:02:49 AM
Hey guys

a couple of things...

First of all... I posted a new Performance post here http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html

Second of all, if you are in any way confused when to Dispose or not, I have an idea - how about you do *exactly* what TT said not to do (right at the beginning of this post) Dispose absolutely everything!!!! I'm not disrespecting you TT, I'm just running with an idea...

I figure the worst that can happen is your code will cause AutoCAD to simply crash - the good thing though about this approach is, if it crashes it should crash right there and then! I think that is much better than crashing some random place and time when the GC decides kicks in. Also, if it does crash then just tell me! That's because it's a bug, and I'll get it fixed so that it doesn't crash - how does that sound?

Or to put it another way, turn your code into spaghetti.

The point to not disposing things that don't need to be disposed is precisely that (avoiding needlessly complicated code).

If your code crashes, it will probably be a result of a bug that was fostered by your code having become needlessly over-complicated  (as a result of disposing everything), rather than a bug in AutoCAD.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 12, 2012, 01:20:52 AM
I have code that plays a squeaker fart noise if I forget to call dispose on object(squeezing the last bit of life out of it) which goes to show you can do anything inside dispose method, and no reason to reply what an ingenious idea that is as it is already understood.
 
So to look at what happens when a line is disposed I can follow the different paths except for one part in DeleteUnmanagedObject.
Where it checks if its ObjectId is null and if so then if the DBObject is Not null I am not sure what that does?
calli (http://msdn.microsoft.com/en-us/library/system.reflection.emit.opcodes.calli.aspx)


First it checks to see if the ObjectId is null, which means the object is not database-resident.  If the object is not database-resident then it deletes the wrapped AcDbObject.

If the object is database-resident then it calls AcDbObject::close(), which doesn't actually close objects that are transaction-resident.

Thanks Tony,
I looked a while back at it and was not following it and knew I missing something and your earlier post explaining autodelete  (http://www.theswamp.org/index.php?topic=42399.msg475806#msg475806)made me realize what I was missing and then went and looked how it set in constuctor and changed in AddNewlyCreatedObject.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 12, 2012, 01:32:08 AM
Hey guys

a couple of things...

First of all... I posted a new Performance post here http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html

Second of all, if you are in any way confused when to Dispose or not, I have an idea - how about you do *exactly* what TT said not to do (right at the beginning of this post) Dispose absolutely everything!!!! I'm not disrespecting you TT, I'm just running with an idea...

I figure the worst that can happen is your code will cause AutoCAD to simply crash - the good thing though about this approach is, if it crashes it should crash right there and then! I think that is much better than crashing some random place and time when the GC decides kicks in. Also, if it does crash then just tell me! That's because it's a bug, and I'll get it fixed so that it doesn't crash - how does that sound?

I'll copy the comments I posted regarding that here:

Quote

Hi Fenton and thanks for the in-depth performance data.

There is one major difference between the Open/Close mechanism and Transaction. When a transaction is disposed, if it was not previously comitted, it results in a call to every resident object's AcDbObject::cancel() memeber (or the functional equivalent of doing that)

Conversely, when a DBObject is disposed, it will always result in a call to AcDbObject::close(), committing any changes made.

So, in the case of using the Open/Close mechanism, what happens when an exception causes control to leave the using() block that disposes a DBObject that was acquired via ObjectId.Open(), was opened for write, and has already had some changes made to it prior to the point where the exception was raised?


And...

Quote

Quote from: fentonwebb
"Now my preferred way, Open/Close – see how much nicer it is?"

That's the problem with apples-to-oranges comparisons. :grin:

Your OpenClose() method and the StartTransaction() method shown above are not functionally-equivalent.

One rolls back changes to all objects that were modified in the foreach() loop in the event that an exception terminates the loop prematurely, while the other does not.


Frankly, I think Albert deserves a bit more credit than you give him, especially in light of the fact that your comparison overlooks a major functional difference between Open/Close and Transactions (in the context of that test code), and I also suspect that Albert's preference for the transaction model may very well be because Transactions facilitate precisely what's overlooked there. And yes, there's a performance cost, but it is nonetheless an apples-to-oranges comparison.


Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 12, 2012, 03:21:56 PM
Quote from: http://adndevblog.typepad.com/autocad/2012/08/the-right-tools-for-the-job-autocad-part-5.html

About The Transaction object in AutoCAD... The Transaction model (StartTransaction()) was invented way back when for a specific reason - transacting multiple writes on the same objects(s) and allowing layered rollbacks of these multi-write transactions.

Here’s what I recommend:
If you guys are using StartTransaction(),
and you don’t need the multiple write feature I just mentioned in the above paragraph,
simply change your StartTransaction() to StartOpenCloseTransaction()…


Is anyone able to clarify the distinction regarding transacting multiple writes on the same objects(s)

Regards
Kerry

Transacting multiple writes means that you can repeatedly open a Transaction-resident object for write, regardless of whether it's currently open for write, or not, and the operation succeeds.

However, Regarding OpenCloseTransaction, Fenton hasn't given you the complete picture insofar as making a decision to switch from Transaction to OpenCloseTransaction.

First, there is no native ObjectARX OpenCloseTransaction, and the native transaction management api knows nothing about them.

The implications of that are:

1.  When a DBObject is opened using an OpenCloseTransaction, it's IsTransactionResident property returns false.

2.  When you start an OpenCloseTransaction, the TransactionManager knows nothing about it. For example, its TopTransaction property returns null, its NumberOfActiveTransactions property returns 0, and calls to TransactionManager.GetObject() and ObjectId.GetObject() will both fail.

The major downside of all of this is that you are forced into passing around a transaction, because Autodesk did not bother to integrate OpenCloseTransaction into the core, and the transaction system, so that an active OpenCloseTransaction is transparently-compatible with a regular AcDbTransaction, and similarly accessible through the TransactionManager.

The OpenCloseTransaction was originally a kludge that was implemented entirely in managed code (it simply cached the managed wrappers it previously created in a Dictionary, keyed to their ObjectIds, and returned them when you called GetObject()). The reason it was created was because the code that scanned the current selection to update the ribbon was taking way too long. Subsequently, the OpenCloseTransaction was reimplemented in native code, but was still not integrated into the transaction management system.

The entertaining part of this is how Fenton makes it all seem so incredibly simple, requiring little more than changing calls to StartTransaction(), to StartOpenCloseTransaction() and you're done.

Not so fast. 

That may be true for relatively-simple command implementations that are 'self-contained' and have no dependence on other shared/reusable code libraries. However, if you do make use of shared/reusable code libraries as do I, then you'll almost certainly have a much bigger mess on your hands. For example, if you use helper classes with methods that currently call ObjectId.GetObject() or TransactionManager.GetObject(), they will have to be completely refactored to take a Transaction as a parameter, because Autodesk didn't seem to think there was any need for a means to access a currently-active OpenCloseTransaction through a Database.

So, with regards to Fenton's advice, caveat emptor.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 12, 2012, 04:01:12 PM
Gotcha and this suceeds.
 
Code - C#: [Select]
  1.  
  2.          [CommandMethod("InneyOutty")]
  3.         public void InneyOutty()
  4.         {
  5.             Database db = HostApplicationServices.WorkingDatabase;
  6.             ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
  7.             Autodesk.AutoCAD.DatabaseServices.TransactionManager tm = db.TransactionManager;
  8.             using (Transaction outerTrx = tm.StartTransaction())
  9.             {
  10.                 BlockTableRecord btr = (BlockTableRecord)outerTrx.GetObject(msId, OpenMode.ForWrite, false, false);
  11.                 BlockTableRecord btr1 = (BlockTableRecord)outerTrx.GetObject(msId, OpenMode.ForWrite, false, false);
  12.                 using (Transaction innerTrx = tm.StartTransaction())
  13.                 {
  14.                     BlockTableRecord btr2 = (BlockTableRecord)innerTrx.GetObject(msId, OpenMode.ForWrite, false, false);
  15.                     innerTrx.Commit();
  16.                 }
  17.                 BlockTableRecord btr3 = (BlockTableRecord)outerTrx.GetObject(msId, OpenMode.ForWrite, false, false);
  18.                 outerTrx.Commit();
  19.             }
  20.         }
  21.  
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 12, 2012, 05:18:57 PM
< .. >

Not so fast. 

< .. >


Thanks for the insight Tony.

I had a niggling 'feeling' that there was a reason I hadn't seen widespread use of the OpenCloseTransaction.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 12, 2012, 10:33:56 PM
I am screwing something up for anyone who wants to try to test with code and drawings and results below.
I just do not have the attention span, will or honestly do not care to investigate anything more than a quick slapping together, but tried to make them equal but looking quickly in Reflector looks like you need to use the OpenCloseTransaction object unlike the Transaction object where you can use TransactionManager for GetObject
 
Basically a test using
Open Close method
OpenCloseTransaction
Transaction
 
Here is the code
*******************************************EDIT*********************
Just noticed I open ModelSpace for write in half of them and read for the other


The OpenClose() method you show points out the potential dangers of using the Open/Close mechanism..

To be completely safe when using ObjectId.Open(), one should never call Close() explicitly, unless it is done in the finally block of a try/finally. But it's easier and cleaner to just dispose the DBObject with using(), as shown in the revised version below.

In the case of your OpenClose() method, if an exception were raised while the model space block was open, both it and possibly one of the objects in it would be left open, which would very quickly crash AutoCAD.

Code: [Select]
   [CommandMethod( "OpenClose" )]
   public static void OpenClose()
   {
      Random ran = new Random();
      Stopwatch sw = new Stopwatch();
      int colorIndex = ran.Next( 1, 7 );
      Database db = HostApplicationServices.WorkingDatabase;
      ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId( db );
      sw.Reset();
      sw.Start();
      using( BlockTableRecord btr = (BlockTableRecord) msId.Open( OpenMode.ForRead ) )
      {
         foreach( ObjectId id in btr )
         {
            using( Entity ent = (Entity) id.Open( OpenMode.ForWrite ) )
            {
               ent.ColorIndex = colorIndex;
            }
         }
      }
      sw.Stop();
      Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( "\nOpenClose\n" );
      Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( sw.ElapsedTicks.ToString() );
   }

Last, to see what I was getting at in my comment to Fenton, about Open/Close not being equivalent to using a Transaction, try adding a locked layer to the test drawing, and placing one of the entities on the locked layer.

Then, run OpenClose() (the revised version I show above that is, otherwise this will crash AutoCAD), and when the exception is raised by trying to open the object on the locked layer for write, your command method ends, you are back at the Command: prompt, and notice that some of the entities have had their colors changed, while others haven't.  That's because in the case of the OpenClose() method, there is no transaction, and nothing else to roll back changes made by the code in the command method, and the drawing is left in a partially-altered and potentially corrupt state.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 12, 2012, 11:06:50 PM

I found some references to OpenCloseTransactions in
AutoCAD® .NET: Practical Examples of Customizing AutoCAD Entity Behavior
which deals mainly with Overrules.
http://au.autodesk.com/?nd=class&session_id=7560

There is also some reading at Keans Blog, particularly referring to it's usage with Event Handlers

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 13, 2012, 01:45:11 AM
I am screwing something up for anyone who wants to try to test with code and drawings and results below.
I just do not have the attention span, will or honestly do not care to investigate anything more than a quick slapping together, but tried to make them equal but looking quickly in Reflector looks like you need to use the OpenCloseTransaction object unlike the Transaction object where you can use TransactionManager for GetObject
 
Basically a test using
Open Close method
OpenCloseTransaction
Transaction
 
Here is the code
*******************************************EDIT*********************
Just noticed I open ModelSpace for write in half of them and read for the other


The OpenClose() method you show points out the potential dangers of using the Open/Close mechanism..

To be completely safe when using ObjectId.Open(), one should never call Close() explicitly, unless it is done in the finally block of a try/finally. But it's easier and cleaner to just dispose the DBObject with using(), as shown in the revised version below.

In the case of your OpenClose() method, if an exception were raised while the model space block was open, both it and possibly one of the objects in it would be left open, which would very quickly crash AutoCAD.

Code: [Select]
   [CommandMethod( "OpenClose" )]
   public static void OpenClose()
   {
      Random ran = new Random();
      Stopwatch sw = new Stopwatch();
      int colorIndex = ran.Next( 1, 7 );
      Database db = HostApplicationServices.WorkingDatabase;
      ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId( db );
      sw.Reset();
      sw.Start();
      using( BlockTableRecord btr = (BlockTableRecord) msId.Open( OpenMode.ForRead ) )
      {
         foreach( ObjectId id in btr )
         {
            using( Entity ent = (Entity) id.Open( OpenMode.ForWrite ) )
            {
               ent.ColorIndex = colorIndex;
            }
         }
      }
      sw.Stop();
      Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( "\nOpenClose\n" );
      Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage( sw.ElapsedTicks.ToString() );
   }

Last, to see what I was getting at in my comment to Fenton, about Open/Close not being equivalent to using a Transaction, try adding a locked layer to the test drawing, and placing one of the entities on the locked layer.

Then, run OpenClose() (the revised version I show above that is, otherwise this will crash AutoCAD), and when the exception is raised by trying to open the object on the locked layer for write, your command method ends, you are back at the Command: prompt, and notice that some of the entities have had their colors changed, while others haven't.  That's because in the case of the OpenClose() method, there is no transaction, and nothing else to roll back changes made by the code in the command method, and the drawing is left in a partially-altered and potentially corrupt state.

I saw another post you made while "Dining" somewhere else about passing ObjectIds vs Dbobjects and from your LINQ extension methods, it is starting to make sense unless I am completely off which is normal.
For that little example I posted I guess you could UNDO but for starters you would be relying on someone else to UNDO that for you.

This might be a dumb example but if you got a 1000 drawings with Company A standards and you need to map them to Company B standards, and you got some criteria like for blocks with this name on this layer and if on this layer etc...,
And you start opening closing the object then pass the objectid to this function and starts a new transaction and ends that passes the id to another function to do other changes or openclose, etc.....
And you do this in some kinda of batch process and half the transactions fail so you basiclly make half the changes needed then you pretty much just created a 1000 drawings with your own new standards that no longer meet the criteria and can not be mapped.
 
So keeping consistent with your functions and how you go about creating your entry and exit points you can make sure if something goes wrong you can at least can roll back to a state that will not harm or compromise the data?

Is at least a match or a LED light starting to go off?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 13, 2012, 03:59:00 AM

I saw another post you made while "Dining" somewhere else about passing ObjectIds vs Dbobjects and from your LINQ extension methods, it is starting to make sense unless I am completely off which is normal.
For that little example I posted I guess you could UNDO but for starters you would be relying on someone else to UNDO that for you.


Well, I'm sure everyone would agree that using Open() and Close() (or Dispose) that way is going to become a problem if the code fails, so I guess it's about how one approaches the problem, and whether they plan for failure or always think about what'll happen in the event of a failure, or maybe they're the type that blissfully presumes that their code can't fail, and so they don't have to worry about what happens (e.g., what state the drawing is left in) if it does.

The ObjectARX docs clearly say that acdbOpenAcDbObject() (which is the native counterpart of ObjectId.Open()), is mainly for very 'transient' opening and closing of objects, and that in a scenario like that which your test code falls into, transactions are preferred.

So, the only problem I have is that StartOpenCloseTransaction() poses problems for reusable/modular code, because there is no way to obtain the currently-active OpenCloseTransaction, which just plain sucks if you ask me.

Quote

This might be a dumb example but if you got a 1000 drawings with Company A standards and you need to map them to Company B standards, and you got some criteria like for blocks with this name on this layer and if on this layer etc...,
And you start opening closing the object then pass the objectid to this function and starts a new transaction and ends that passes the id to another function to do other changes or openclose, etc.....
And you do this in some kinda of batch process and half the transactions fail so you basiclly make half the changes needed then you pretty much just created a 1000 drawings with your own new standards that no longer meet the criteria and can not be mapped.
 
So keeping consistent with your functions and how you go about creating your entry and exit points you can make sure if something goes wrong you can at least can roll back to a state that will not harm or compromise the data?

Is at least a match or a LED light starting to go off?

Not sure I follow. Do you mean transactions across multiple files?  I usually have a batch process backup files as each one is processed, so there is a logical transaction and the means to roll back everything, but I'm not sure if that's what you're getting at.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 13, 2012, 05:09:01 PM
Hey TT

"Well, I'm sure everyone would agree that using Open() and Close() (or Dispose) that way is going to become a problem if the code fails, so I guess it's about how one approaches the problem, and whether they plan for failure or always think about what'll happen in the event of a failure, or maybe they're the type that blissfully presumes that their code can't fail, and so they don't have to worry about what happens (e.g., what state the drawing is left in) if it does.
"

I don't agree, the using statement's Dispose is automatically called even when an exception is thrown. Also, there is a Cancel() method on an Opened entity which undoes the changes. This is the same function that a a StartOpenCloseTransaction.Abort() calls.

"The OpenCloseTransaction was originally a kludge that was implemented entirely in managed code (it simply cached the managed wrappers it previously created in a Dictionary, keyed to their ObjectIds, and returned them when you called GetObject()). The reason it was created was because the code that scanned the current selection to update the ribbon was taking way too long. Subsequently, the OpenCloseTransaction was reimplemented in native code, but was still not integrated into the transaction management system."

Hmm you must be listening at my office door or something? :-) The reason that OpenCloseTransaction was implemented was not because of what you say at all, where did you hear that!? The reason we implemented it was because StartTransaction() cannot be used in Event callbacks without great care being taken, also, it's slower than Open/Close. Therefore, so as not to break the transaction model that all have adopted, we wrapped Open/Close in the "transaction" model to give StartOpenCloseTransaction.

"That may be true for relatively-simple command implementations that are 'self-contained' and have no dependence on other shared/reusable code libraries. However, if you do make use of shared/reusable code libraries as do I, then you'll almost certainly have a much bigger mess on your hands. For example, if you use helper classes with methods that currently call ObjectId.GetObject() or TransactionManager.GetObject(), they will have to be completely refactored to take a Transaction as a parameter, because Autodesk didn't seem to think there was any need for a means to access a currently-active OpenCloseTransaction through a Database."

If you using a transaction inside of your library/helper functions, personally, I would never do that, I'd absolutely use Open/Close or StartOpenCloseTransaction in a self contained way. First of all, it's faster, second of all it's not relying on external components being setup, and third it will work in all contexts making it more portable and, less susceptible to change (like TT found).

Kerry, the messages you see when using Open/Close when you compile - quick history lesson - originally, VB.NET did not have a using statement so we adopted the Transaction model for all .NET code and marked Open/Close as Deprecated/Obsolete. Later on, .NET developers became more and more advanced eventually finding the various issues that using raw Transactions brings (slower, cannot be used in Events, cannot be used with HandOverTo, etc etc) so we removed the Deprecated text on Open/Close and made it 'for advanced use only' (hmmm, looks like we accidentally missed a few, sorry about that). We didn't want to make people change their code, so we implemented the OpenCloseTransaction in a Transaction way. It's true you can't use ObjectId.GetObject() by why would you do that when it's not actually using raw Transactions.

One last point, if you call OpenCloseTransaction.Abort() it rolls back any changes by calling the Open/Close Object.Cancel() function - it works almost exactly the same as a raw Transaction under normal circumstances - "Golden Delicious Apple" vs "Granny Smith Apple"?!

"Apples-Oranges"
Yes, Apples-Oranges - but they are still both fruit are they not!!?

Guys, I'm not on here to confuse you all, I want you to succeed - that's my job!!! I think I have been very clear what the differences are between open/close, normal transactions and openclosetransactions. I have also been very clear about when to use Dispose() (I recommend that if you are not sure then, Dispose "always!!!") If you have any specific questions, then why not post them to our AutoCAD .NET forum http://forums.autodesk.com/t5/NET/bd-p/152 - my team are monitoring those much more closely now and we'll be glad to help you guys.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 13, 2012, 05:47:23 PM
Thanks Fenton,
 
VB.NET did not have a using statement so we
VB reminds me of "that guy" who would always come around and hang out and that no one really liked, but just because everyone knew him for so long they just dealt with him and let him hang out, and wished he would just go away
 
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 13, 2012, 06:11:00 PM
< ..>

Guys, I'm not on here to confuse you all, I want you to succeed - that's my job!!!
 
< .. >

Thanks for the reply Fenton.

How soon will this information be made public. ?

How soon will AutoDesk samples be brought up to date ?


Regarding my earlier comment about assumptions that are made by commentators, here's a typical example.

Quote
Converting Polyline to Polyline2d
By Augusto Goncalves

There is a method called Polyline.ConvertTo that can convert a lightweight polyline into a polyline 2d object, but this method requires a special treatment.

First, it is required to use StartOpenCloseTransaction method to create a new transaction, instead regular StartTransaction. Second, remove the old polyline and append the newly created polyline 2d.
Code - C#: [Select]
  1. ObjectId plineId = // obtain here...
  2.  
  3. Database db =   Application.DocumentManager.MdiActiveDocument.Database;
  4.  
  5. using (Transaction t =   db.TransactionManager.StartOpenCloseTransaction())
  6.  
  7. {
  8.  
  9.   using (Polyline pline = (Polyline)   t.GetObject(plineId, OpenMode.ForWrite))
  10.  
  11.   {
  12.  
  13.     t.AddNewlyCreatedDBObject(pline, false);
  14.  
  15.     Polyline2d poly2 = pline.ConvertTo(true);
  16.  
  17.     t.AddNewlyCreatedDBObject(poly2, true);
  18.  
  19.     t.Commit();
  20.  
  21.   }
  22.  
  23. }


This statement will make no sense to most people because they have no idea why 'it is required'.
Quote
First, it is required to use StartOpenCloseTransaction method to create a new transaction, instead regular StartTransaction. Second, remove the old polyline and append the newly created polyline 2d.


The reasons for the requirement are not covered in the documentation to my knowledge.

I guess that it has something to do with the AcDbObject::handOverTo() used internally in the wrapped ARX causes problems when used in a conventional Transaction so Open/Close or a StartOpenCloseTransaction must be used.

Can you confirm this please.

Issues like this (and those discussed in the last 100 or so posts) SHOULD be covered prominently in the documentation.

Regards
Kerry






Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: dgorsman on August 13, 2012, 06:50:16 PM
Documentation is one of the first things cut in any budget when project deadlines loom.  I'm not holding my breath on this being any different.   :|

I wonder if they would take volunteer assistance, with appropriate NDA... (no, that wasn't me volunteering!).
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 13, 2012, 07:31:32 PM
Hey Kerry

here's a tip for you, and all that read here... If you want more details about a function in .NET, simply find the corresponding function in the ObjectARX Reference... Specifically, for the question about OpenCloseTransaction and Polyline2d.ConvertTo - read AcDbPolyline::convertTo()... it states that the second parameter is used as an "Input Boolean indicating whether or not to do a handOverTo between the AcDbPolyline and the AcDb2dPolyline."

Another tip, if you read the ObjectARX Developers guide, you will understand much more about the underlying unmanaged code that the AutoCAD .NET API wraps.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: SGP2012 on August 13, 2012, 08:04:15 PM
Hi Kerry,

How soon will this information be made public. ?

How soon will AutoDesk samples be brought up to date ?

I've not been following this thread, so I'm not completely clear which samples need bringing up to date in what way, and what information Fenton referred to that needs to be made public. However, ...

For specific documentation bugs, you can use the 'Comment?' link in the ObjectARX helpfiles to report an issue directly to our TechPubs team (if your default browser settings allow it to work properly  :|). Failing that, you're welcome to report documentation issues via DevHelp Online (if you're an ADN member) or on the appropriate Autodesk discussion group (if you're an ADN member or if you're not). As Fenton says, my DevTech team are much more actively monitoring the 'API' discussion groups these days.

And (because you know my email address, Kerry  :kewl:), you're welcome to send your list of documentation omissions and enhancement requests to me if you like, and I can pass them on to our TechPubs team. They have dedicated API documentation writers, who are quick to fix mistakes or omissions when they are brought to their attention.

The same applies for any bugs you find in the SDK samples (and if you'd care to summarize to me how the samples are outdated).

On a related note, I agree that the reference guide should do a good job of describing the basic contract for the API. But the days are gone when relatively static SDK and developer guiide samples are the only place people expect to look for code usage information. (Surely people only ever looked there because they couldn't find the information anywhere else)? The first places I (and, I suspect, everyone else on this forum) look for API examples and advice are: blogs (like Through the Interface or DevBlog), forums (like TheSwamp or the Autodesk forums), and websites (like the Autodesk Developer Center or StackOverflow) - or (more generally) we use Google, Bing or whatever is our search engine of choice.

I can only speak for my DevTech team within Autodesk - but this is why we have started (and will continue) to devote a lot of time to putting as much content as we can in the public domain - but (bugs aside) I'm not planning that we'll be retrofitting any information we post to the DevBlogs into the SDK documentation because to do so would be an expensive/inefficient use of people who could instead be creating much more content on our blogs, developer centers and forums.

HTH,

Stephen
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 13, 2012, 08:04:35 PM
Hey Kerry

here's a tip for you, and all that read here... If you want more details about a function in .NET, simply find the corresponding function in the ObjectARX Reference... Specifically, for the question about OpenCloseTransaction and Polyline2d.ConvertTo - read AcDbPolyline::convertTo()... it states that the second parameter is used as an "Input Boolean indicating whether or not to do a handOverTo between the AcDbPolyline and the AcDb2dPolyline."

Another tip, if you read the ObjectARX Developers guide, you will understand much more about the underlying unmanaged code that the AutoCAD .NET API wraps.

Through necessity, I DO read the ARX ref.

Can you point to where it states that we shouldn't use a transaction with  Polyline2d.ConvertTo().

... just a side note.
I recently answered a question on the discussion group with a google link ; to be told
Quote
Sorry,It's hard for me to Search code from the English pages. In our country few people use CADnet so the information is few with Chinese language. In a word, My english is not very good. Thank you for your help.

This indicates to me that a major percentage of the worlds developers will never read this thread.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 13, 2012, 08:41:18 PM

Thanks for the post Stephen.

Early in this thread I asked for a sample of the recommended way to code the example in Tony's first post.
That would be a good place to start.

Next would be some more complicated (RealLife) samples.
I haven't perused the SDK samples for a long time, so I'm not sure of the status there.

Regarding the doc's
This attachment would be a good place to start:


I know sometimes it seems that I do a lot of bitching about the documentation ; If you saw some of the emails I get from people wanting help,  or had a good look at the posts here at theSwamp or at the AutoDesk discussion group you'd get a better idea why I think it's important.

... and that's just about the stuff that is fundamental, let alone some of the naunces.

Best Regards
Kerry
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: owenwengerd on August 13, 2012, 08:51:24 PM
I can only speak for my DevTech team within Autodesk - but this is why we have started (and will continue) to devote a lot of time to putting as much content as we can in the public domain - but (bugs aside) I'm not planning that we'll be retrofitting any information we post to the DevBlogs into the SDK documentation because to do so would be an expensive/inefficient use of people who could instead be creating much more content on our blogs, developer centers and forums.

<rant-mode>
That kind of thinking is dead wrong. Regardless of whether people look for information first via search engine, *all* programmers eventually need correct and accurate reference material from Autodesk to resolve any doubts, disputes, or confusion. Even if that requires 200 dedicated full-time Autodesk employees, the total outlay will still be much less to do it right and keep it correct than the eventual cost to your customers and third party developers of the confusion and inevitable massive waste of resources that results from not having such a baseline reference.

The move to almost useless HTML documentation is evidence that Autodesk will gladly throw developers under the bus on a whim or to save a buck, and I fear this is becoming the rule rather than the exception. I think it's great that Fenton and the others are shoveling stuff into the public domain, even if the stuff is raw and unfiltered and sometimes smells funny. But if nobody is manning the documentation stations, they should be working on documentation instead.
</rant-mode>
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 13, 2012, 09:14:54 PM
Hey Kerry

here's a tip for you, and all that read here... If you want more details about a function in .NET, simply find the corresponding function in the ObjectARX Reference... Specifically, for the question about OpenCloseTransaction and Polyline2d.ConvertTo - read AcDbPolyline::convertTo()... it states that the second parameter is used as an "Input Boolean indicating whether or not to do a handOverTo between the AcDbPolyline and the AcDb2dPolyline."

Another tip, if you read the ObjectARX Developers guide, you will understand much more about the underlying unmanaged code that the AutoCAD .NET API wraps.

Hi Fenton, The native AcMgOpenCloseTransaction is to the best of my knowledge, not a public API, and is not documented in the native ObjectARX documentation. The OpenCloseTransaction is derived from Transaction, so that is the managed docs for it.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: owenwengerd on August 13, 2012, 10:00:32 PM
Not 30 minutes after posting my rant, I have a need to implement two new AcDbCurve members in a custom object that is derived from AcDbCurve. The new members are AcDbCurve::getAcGeCurve() and AcDbCurve::setFromAcGeCurve(). The ObjectARX reference includes the following very informative and helpful description: "This is getAcGeCurve, a member of class AcDbCurve." Google returns one hit to the the DevBlog (http://adndevblog.typepad.com/autocad/2012/04/converting-geometry-objects-to-database-entity.html) which is no help at all. That's it, nothing else.

Therefore, I cannot implement these functions at all. I don't even know whether AcDbCurve implements them. Since only Autodesk knows how to implement them, it's not safe to call the functions on non-Autodesk objects because there is no contract, hence no way to know how to use them correctly. They are effectively unuseable, all because nobody could be bothered to write complete and correct documentation for them.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 13, 2012, 10:38:01 PM
Hey TT

Quote from: TT
"Well, I'm sure everyone would agree that using Open() and Close() (or Dispose) that way is going to become a problem if the code fails, so I guess it's about how one approaches the problem, and whether they plan for failure or always think about what'll happen in the event of a failure, or maybe they're the type that blissfully presumes that their code can't fail, and so they don't have to worry about what happens (e.g., what state the drawing is left in) if it does."

I don't agree, the using statement's Dispose is automatically called even when an exception is thrown. Also, there is a Cancel() method on an Opened entity which undoes the changes. This is the same function that a a StartOpenCloseTransaction.Abort() calls.


Hey Fenton.  I think you've misunderstood me. That is the whole point of my comment - that DBObject.Dispose() calls AcDbObject::close(), which is incorrect if an exception was thrown before the call to Dispose() is made by using().

That is precisely why Transaction.Dispose() calls Abort() rather than Commit(), if Commit() was not previously called.

Here is your 'nicer' code from that post:

Quote
Now my preferred way, Open/Close – see how much nicer it is?

Code - C#: [Select]
  1. public void OpenClose()
  2. {
  3.   Database db = HostApplicationServices.WorkingDatabase;
  4.   ObjectId msId = SymbolUtilityServices.GetBlockModelSpaceId(db);
  5.   using (BlockTableRecord btr = (BlockTableRecord)msId.Open(OpenMode.ForRead))
  6.   {
  7.     foreach (ObjectId id in btr)
  8.     {
  9.       using (Entity ent = (Entity)id.Open(OpenMode.ForWrite))
  10.       {
  11.         // do something
  12.       }
  13.     }
  14.   }
  15. }
  16.  
  17.  

Now I have shown the differences between the two styles...


You really haven't shown all the differences between the two styles.

There was one important difference, which is what happens when an exception terminates the code within the inner using() block. When using a transaction, all changes made to any object subsequent to starting the transaction will be rolled back.  In your example above, all changes made to all objects prior to the point where the exception terminates the code, will be left intact, and will not be rolled back, and that is because DBObject.Dispose() calls AcDbObject::close(), which commits changes.  So, all I was trying to point out was that your example above is functionally not equivalent to any of the others, all of which will roll back changes to all objects if the code is stopped by an exception.

So, if you want to compare apples with apples, your 'nicer' version must roll back changes to all entities processed by the foreach() loop when an exception is raised, which it doesn't do, making it functionally-incomplete, relative to what the methods that demonstrate the other 3 styles do.

And, for the much simpler case of using ObjectId.Open() to modify a single entity, here is what is actually required:

Code - C#: [Select]
  1.  
  2. using (Entity ent = (Entity)id.Open(OpenMode.ForWrite))
  3. {
  4.    try
  5.    {
  6.       // do something
  7.    }
  8.    catch
  9.    {
  10.       ent.Cancel();
  11.       ent = null;   // supress call to Dispose() and AcDbObject::close()
  12.       throw;
  13.    }
  14. }
  15.  
  16.  

And I think that snippet serves to show that using ObjectId.Open() to modify objects is not nearly as simple as it might at first seem, especially to someone who is used to thinking in terms of the Acad::ErrorStatus returned by most native API calls  :wink:.  It really does take a lot of mental 'retooling' to think about conveying failure using exceptions as it's done in managed code.

So if your response to this is 'well then you should use an OpenCloseTransaction', that's all fine and dandy except that someone neglected to think about the need to get the current/active OpenCloseTransaction without having to pass it around like a jug of moonshine.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: fentonwebb on August 14, 2012, 01:35:00 PM
Hey TT

I personally believe that 99% of code you write for AutoCAD does not need to rollback anything. If it does, more often than not, it's because the code was not implemented correctly. That said, there are completely valid reasons for using nested Transactions with the rollback feature - do you have an example which shows what you are talking about?

Guys,

I'll repeat what I've said before, I personally like to use Open/Close, amongst other reasons, that's my style. Open/Close was in AutoCAD from ObjectARX V1, way before transactions - it works fine and even handles error conditions! For you guys, those who use Transactions, all I'm saying is, there is a big overhead for using "normal" Transactions, if you don't need the nested Transaction/Rollback functionality (99% don't) and you feel your app is running slower than you want, or you are curious if there will be a performance gain, then simply use StartOpenCloseTransaction - it's very simple advice really. If it doesn't work for you, just change it back - Alt+E->Find and Replace->Replace in Files->StartOpenCloseTransaction change to StartTransaction, Match Case, Whole word - simple............... :-)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: SGP2012 on August 14, 2012, 01:44:58 PM
How long do we have to go until we break the record for the longest thread on TheSwamp? Is that within reach here, do you think?  :evil:

@Kerry - So your gripe in that specific example is about the AutoCAD .NET Developers Guide.  That was originally produced to help VBA developers when switching to .NET - that's why it has all the comparisons with VBA in it. I'll pass on your request for mention of OpenCloseTransactions, but there are a lot of other APIs that are not included in the Developers Guide. I don't believe that it will ever be possible to include a section on every AutoCAD API in that developers guide (or in the ObjectARX developers guide). It seems that our difference of opinion is that you feel the developers guide should endeavor to do that, whereas I think it should give people the basics people need to get started and then those same people can find more advanced info elsewhere (like here).

@Owen - Kind of the same answer. Again, we'll have to agree to disagree. I don't believe its possible for us to include the answers to every question that every developer ever has in our documentation. At least not without publishing the AutoCAD source code - which isn't going to happen. So it doesn't matter how many people we employ to write formal documentation, advanced developers will always want to know something we've not written down. Again, that's why my team are actively increasing their engagement in the public domain. Your complaint about searching doesn't worry me since we've only been doing this for a couple of months. But our ultimate goal is that every time my team answer a new (non-confidential) question, the information we provide will be publicly searchable. And if someone can't find the answer to their question by searching, then they'll get an answer if they ask on the correct Autodesk discussion group - that answer could come from my team, but is just as likely to come from one of the excellent (non-Autodesk) programmers who generously give their time and knowledge to help others on those forums (and on this one, of course   :angel:). If you don't see us making progress towards this goal during the next year, then you're welcome to come and throw a custard pie in my face at my 'Meet the Experts' panel session at AU next year :ugly:. (But you'll have to pay for the AU registration and for the custard pie  :laugh:).

(I'll also comment in passing that a big reason we have some gaps in our documentation is because we expose such a large public API. I'm glad its that way around).

And just to reiterate my offer - If someone is interested in sending me a list of documentation omissions and bugs (ideally in order of priority), then I'm very happy to pass it on for you.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: dgorsman on August 14, 2012, 02:48:04 PM
...whereas I think it should give people the basics people need to get started and then those same people can find more advanced info elsewhere (like here).

I can understand the limited ability to not cover everything in the finest detail and I'm fine with that.  But the documentation still needs to have a good grounding in the basics.  If you need to go through the advanced sections of the help files just to understand the basics, I wouldn't consider that a good design.  Same idea with the discussion forums - I shouldn't need to consult the actual AutoDesk developers for Transaction information.  Right now the documentation doesn't cover the basics as well as it could (and probably should).  Its hard to make any suggestions when the questions themselves aren't known.

For the highly advanced issues, a discussion-group, question/answer format is better because it can cover a lot of the nuances a conventional help system cannot.  But as they say the Internet is Forever; somebody searching could find old threads which are now obsolete first, before newer threads which cover those same issues.  With the (in)correct search criteria its possible to completely filter out the newer, correct, answers.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 14, 2012, 04:29:39 PM
Hey TT

I personally believe that 99% of code you write for AutoCAD does not need to rollback anything. If it does, more often than not, it's because the code was not implemented correctly. That said, there are completely valid reasons for using nested Transactions with the rollback feature - do you have an example which shows what you are talking about?


Well, I personally disagree with that. Even LISP programmers are programmed to think in terms of rolling back all changes their LISP commands make if an error occurs. This goes back to my comment about writing code with the assumption that Murphy's law prevails, and that one should always consider what the user will be left with in the event the application fails.

We can find countless examples of cases where custom commands perform a series of steps that make incremental changes to the drawing, which would be invalid or dependent on the operation having completed successfully, and shouldn't be left intact if that doesn't happen.  I think the folks here can easily relate to that, and their routine use of Transactions for ensuring that the changes are completed successfully or rolled back entirely bears witness to it.

Quote
Guys,

I'll repeat what I've said before, I personally like to use Open/Close, amongst other reasons, that's my style. Open/Close was in AutoCAD from ObjectARX V1, way before transactions - it works fine and even handles error conditions! For you guys, those who use Transactions, all I'm saying is, there is a big overhead for using "normal" Transactions, if you don't need the nested Transaction/Rollback functionality (99% don't) and you feel your app is running slower than you want, or you are curious if there will be a performance gain, then simply use StartOpenCloseTransaction - it's very simple advice really. If it doesn't work for you, just change it back - Alt+E->Find and Replace->Replace in Files->StartOpenCloseTransaction change to StartTransaction, Match Case, Whole word - simple............... :-)


For any existing code that relies on ObjectId.GetObject(), it is not nearly as simple as you make it seem. The reason we use ObjectId.GetObject() in the first place, is to avoid the need to pass transactions around. So, if they are making use of that API, then one cannot simply replace StartTransaction() with StartOpenCloseTransaction() without existing calls to ObjectId.GetObject() failing.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: SGP2012 on August 14, 2012, 04:40:27 PM
I don't disagree on the need for formally documentating basic information, dgorsman. I've already made the author of the .NET developers guide aware of this thread. He's going to look into beefing up the info on transactions, and also fixing the places in the sample code where he's 'new'ing DBObjects without a Using statement. He's currently compiling a task list for updating the developers guide, so now is a good time to respond to my invitation to put forward suggestions. Items already on the list are blocks and plotting.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 14, 2012, 05:57:52 PM
Stephen,

If I might ask, will this same 'push' for clarifying the basic documentation also propagate verticals such as Civil 3D, and AutoCAD MEP, etc. or will this be relegated to AutoCAD?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: SGP2012 on August 14, 2012, 06:44:40 PM
My previous comments were about the AutoCAD API documentation, but I'm happy to pass on your requests/suggestions for other Autodesk products too.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 14, 2012, 07:18:33 PM
Not 30 minutes after posting my rant, I have a need to implement two new AcDbCurve members in a custom object that is derived from AcDbCurve. The new members are AcDbCurve::getAcGeCurve() and AcDbCurve::setFromAcGeCurve(). The ObjectARX reference includes the following very informative and helpful description: "This is getAcGeCurve, a member of class AcDbCurve." Google returns one hit to the the DevBlog (http://adndevblog.typepad.com/autocad/2012/04/converting-geometry-objects-to-database-entity.html) which is no help at all. That's it, nothing else.

Therefore, I cannot implement these functions at all. I don't even know whether AcDbCurve implements them. Since only Autodesk knows how to implement them, it's not safe to call the functions on non-Autodesk objects because there is no contract, hence no way to know how to use them correctly. They are effectively unuseable, all because nobody could be bothered to write complete and correct documentation for them.

I agree completely, and the sad thing is that it doesn't take much to put the needed information into the topic, but the problem is that most of the docs are generated from embedded programmer comments and little more, so the programmer didn't do what was required.  Perhaps the programmer was incorrectly assuming the reader understood that the abstract AcDbCurve class defines no geometry and therefore can't implement those methods, which like many other methods of that class, must be implemented by derived types.

If this helps:

ObjectARX 2012 Reference
   What's New in ObjectARX for 2012
      New in Global Functions:

acdbAssignGelibCurveToAcDbCurve            This function takes an AcGeCurve3d and sets the specified AcDbCurve to be geometrically identical to the AcGeCurve3d.
acdbConvertAcDbCurveToGelibCurve      This function takes an AcDbCurve and returns an AcGeCurve3d that is geometrically identical to the AcDbCurve.
acdbConvertGelibCurveToAcDbCurve       This function takes an AcGeCurve3d and returns an AcDbCurve that is geometrically identical to the AcGeCurve3d.

So, if a custom curve class encapsulates an AcGeCurve3d, the above functions should help.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 14, 2012, 08:53:39 PM
I don't disagree on the need for formally documentating basic information, dgorsman. I've already made the author of the .NET developers guide aware of this thread. He's going to look into beefing up the info on transactions, and also fixing the places in the sample code where he's 'new'ing DBObjects without a Using statement. He's currently compiling a task list for updating the developers guide, so now is a good time to respond to my invitation to put forward suggestions. Items already on the list are blocks and plotting.

(http://www.theswamp.org/screens/kerry/pinkie.gif)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: owenwengerd on August 14, 2012, 09:00:25 PM
So, if a custom curve class encapsulates an AcGeCurve3d, the above functions should help.

Even given what I can deduce from this information and from the header file comments where the functions are declared, I still can't reliably determine whether the caller or callee is responsible for allocating the new AcGeCurve3d object. There is just no safe way to reliably implement the functions, so I just return Acad::eNotImplemented in my implementation. Which almost certainly means the global functions you mentioned will not work with my custom objects, and may or may not work with other third party custom objects derived from AcDbCurve.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 15, 2012, 12:09:11 AM
My previous comments were about the AutoCAD API documentation, but I'm happy to pass on your requests/suggestions for other Autodesk products too.

Yes, that would be greatly appreciated. Verticals are lacking even more so than AutoCAD, IMO.

Cheers! :beer:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: BlackBox on August 15, 2012, 03:09:24 AM
I've just been informed that the Civil 3D 2013 documentation has received significant improvement over prior years, while I am currently relegated to using Civil 3D and AutoCAD MEP 2011 for production... So FWIW, I appreciate your willingness to convey our suggestions/requests onto the proper channels none the less.

Cheers! :beer:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: MexicanCustard on August 21, 2012, 07:52:45 AM
I haven't seen anyone comment on the past few articles here.  http://spiderinnet1.typepad.com/blog/ (http://spiderinnet1.typepad.com/blog/) Seems that using ways other than Transaction/StartTransaction may reduce overhead but may introduce other delays.


Tony, I use your database extension methods heavily, thank you BTW.  What are your thoughts on your BlockTableRecord.GetObjects using ObjectId.GetObject instead of using a transaction.  Based on comments in this thread would it be enough of a performance boost to change it to use the top transaction instead?
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 21, 2012, 09:28:12 AM
Looked like the difference was a call to CheckTopTransaction plus some of Tony's comment
http://www.theswamp.org/index.php?topic=42197.msg473360#msg473360 (http://www.theswamp.org/index.php?topic=42197.msg473360#msg473360)
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 22, 2012, 02:26:47 AM
The same applies for any bugs you find in the SDK samples (and if you'd care to summarize to me how the samples are outdated).

On a related note, I agree that the reference guide should do a good job of describing the basic contract for the API. But the days are gone when relatively static SDK and developer guiide samples are the only place people expect to look for code usage information. (Surely people only ever looked there because they couldn't find the information anywhere else)? The first places I (and, I suspect, everyone else on this forum) look for API examples and advice are: blogs (like Through the Interface or DevBlog), forums (like TheSwamp or the Autodesk forums), and websites (like the Autodesk Developer Center or StackOverflow) - or (more generally) we use Google, Bing or whatever is our search engine of choice.

I can only speak for my DevTech team within Autodesk - but this is why we have started (and will continue) to devote a lot of time to putting as much content as we can in the public domain - but (bugs aside) I'm not planning that we'll be retrofitting any information we post to the DevBlogs into the SDK documentation because to do so would be an expensive/inefficient use of people who could instead be creating much more content on our blogs, developer centers and forums.

HTH,

Stephen

And just to reiterate my offer - If someone is interested in sending me a list of documentation omissions and bugs (ideally in order of priority), then I'm very happy to pass it on for you.

Thanks Stephen and Fenton for your input, and appreciate you guys doing what you can help, other than being able take 6+ months for each ADN support request that comes in to produce lacking documentation I am not sure what else you guys could do.
The DevBlog is helpful and appreciate it.
 
I am not sure but unless you guys decide how much resources will be put or spent for producing documentation then it is what it is but I would think the best thing to do is spend it on most common scenarios.
 
I use the docs, but for a particular class or feature unless it directly maps or exposes everything in ObjectARX then there is alot of guesswork, and for common ones you end up spending time in reflector and testing to figure out what is going on.
 
 
That comment link has never worked for me but I just tried and it worked.

Is that the best way to get more resources if everyone just started loading you guys up with comments?

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: TheMaster on August 23, 2012, 06:09:33 AM
My previous comments were about the AutoCAD API documentation, but I'm happy to pass on your requests/suggestions for other Autodesk products too.

Well, there seems to have been some progress made on API docs.

  "description to come" has been replaced with "This is method <methodname> of class <classname>".   :roll:
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Jeff H on August 28, 2012, 07:56:04 AM
Would any of you ARX guys say this is a fair test vs Open Close?
 
Code - C: [Select]
  1.  
  2.   static void HPADArxProject3MyCommand1(void)
  3.  {
  4.   time_t start,end;
  5.   int totalTime=0;
  6.  
  7.   AcDbBlockTable *pBtbl;
  8.   acdbHostApplicationServices()->workingDatabase()->getBlockTable (pBtbl, AcDb::kForRead) ;
  9.   AcDbBlockTableRecord *pModelSpace ;
  10.   time(&start);
  11.   pBtbl->getAt (ACDB_MODEL_SPACE, pModelSpace, AcDb::kForRead) ;
  12.   pBtbl->close () ;
  13.   AcDbBlockTableRecordIterator *pBlockIterator ;
  14.   pModelSpace->newIterator (pBlockIterator, true, true) ;
  15.   pModelSpace->close();
  16.   int index = rand() % 255 + 1;
  17.   AcDbEntity *pEnt;
  18.   Acad::ErrorStatus es;
  19.   for ( ; !pBlockIterator->done () ; pBlockIterator->step () )  
  20.   {  
  21.    pBlockIterator->getEntity(pEnt, AcDb::kForWrite, false);    
  22.    pEnt->setColorIndex(index);
  23.    pEnt->close();
  24.   }
  25.   delete pBlockIterator;
  26.   time(&end);
  27.   totalTime = end-start;
  28.   acutPrintf(_T("\n %i",totalTime));
  29.  }
  30. } ;
  31.  
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: It's Alive! on August 28, 2012, 08:03:35 AM
^ you can use ARXs smart pointers to do cleanup as well
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: owenwengerd on August 28, 2012, 11:45:08 AM
Would any of you ARX guys say this is a fair test vs Open Close?

This times open/close AND undo filing. Depending on your goal, this may not be what you want, because the undo filing is (I would guess) going to account for most of the CPU cycles in your test. If you want to time only open/close, remove the call to setColorIndex.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: Kerry on August 29, 2012, 02:18:53 AM
< .. >
For specific documentation bugs, you can use the 'Comment?' link in the ObjectARX helpfiles to report an issue directly to our TechPubs team (if your default browser settings allow it to work properly  :|). Failing that, you're welcome to report documentation issues via DevHelp Online (if you're an ADN member) or on the appropriate Autodesk discussion group (if you're an ADN member or if you're not). As Fenton says, my DevTech team are much more actively monitoring the 'API' discussion groups these days.

And (because you know my email address, Kerry  :kewl:), you're welcome to send your list of documentation omissions and enhancement requests to me if you like, and I can pass them on to our TechPubs team. They have dedicated API documentation writers, who are quick to fix mistakes or omissions when they are brought to their attention.

< ... >
Stephen

Stephen, The SendComment link does not acknowledge a successful send. Can this be rectified ?

for instance :today :
Quote
acrxProductKey
is listed in the ObjectARX but is NOT available in the API.

Regards

I don't know if this and others are finding a home.

Regards

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: SGP2012 on August 31, 2012, 04:39:00 PM
Thanks Kerry.

Noted and reported.
Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: kdub_nz on August 02, 2023, 05:15:14 PM
Happy birthday to this thread !!  :smitten: 

I come back and visit it regularly.

Title: Re: Fenton Webb's advice re "To dispose or not to dispose"
Post by: It's Alive! on August 02, 2023, 08:39:49 PM
Quote
You got to know when to close’em
And not dispose’em
When to delete’em
And when to walk away