Author Topic: Transaction timing issues...  (Read 18382 times)

0 Members and 1 Guest are viewing this topic.

Jeff H

  • Needs a day job
  • Posts: 6144
Re: Transaction timing issues...
« Reply #30 on: March 13, 2013, 04:33:43 PM »
docs
Quote
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #31 on: March 13, 2013, 05:39:46 PM »
Thanks for doing that Stevray! My OCD is involved at this point and I'd like to learn the real issue here to avoid it in the future.  :ugly:


So, the error code changes from eNotApplicable to eAtMaxReaders.  Anyone know what that means and how to get around it?

Frankly, AutoDesk's error handling sucks.  They might a well replace every error code with a simple "An error has occurred" and leave it at that.
To Jeff H.

When I put the commit in it still fails randomly.  However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine.  However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?

After looking through your code for a nested item being explicitly opened and not finding one I came across your post where you had success by not setting the properties.

I hope somebody can confirm, but I'm guessing that each time we are accessing the block's property collection we are opening the definition for read.  Since in each iteration of the main loop we are creating a new instance of the XbyX_End_View class which we are not explicitly disposing then perhaps the definition is not being closed until the garbage collector cleans up?
If we explicitly dispose the object at the end of the main loop, or in the property setting loop we could possibly eliminate the problem???

What says you Jeff?
« Last Edit: March 13, 2013, 05:43:09 PM by WILL HATCH »

Jeff H

  • Needs a day job
  • Posts: 6144
Re: Transaction timing issues...
« Reply #32 on: March 13, 2013, 06:16:06 PM »
Thanks for doing that Stevray! My OCD is involved at this point and I'd like to learn the real issue here to avoid it in the future.  :ugly:


So, the error code changes from eNotApplicable to eAtMaxReaders.  Anyone know what that means and how to get around it?

Frankly, AutoDesk's error handling sucks.  They might a well replace every error code with a simple "An error has occurred" and leave it at that.
To Jeff H.

When I put the commit in it still fails randomly.  However, if I also set the Height to 3.5 so both the Height and Width are simply being set to the default value that the block has, it works fine. However, what is the point of having a dynamic block if you can't set the properties to anything but their defaults?

After looking through your code for a nested item being explicitly opened and not finding one I came across your post where you had success by not setting the properties.

I hope somebody can confirm, but I'm guessing that each time we are accessing the block's property collection we are opening the definition for read.  Since in each iteration of the main loop we are creating a new instance of the XbyX_End_View class which we are not explicitly disposing then perhaps the definition is not being closed until the garbage collector cleans up?
If we explicitly dispose the object at the end of the main loop, or in the property setting loop we could possibly eliminate the problem???

What says you Jeff?


I missed that or skimmed over it to quickly,


Did you catch reply
Your width and height properties are set to list.
Are you using values defined in the list?
That was my first thought would your problem could be but it looked liked you were setting them to values in the list.


Tested on Win 7, AutoCAD 2013, 64bit


Look at picture below or open your drawing and select a BlockReference then on the properties palette notice how the Width and Height properties or limited to what's in the combobox.
Does code work when using any of those values or change from list to none


As a side question do you work for a envelope company?


WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #33 on: March 13, 2013, 06:51:15 PM »
As a side question do you work for a envelope company?

 :lmao:

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #34 on: March 14, 2013, 02:37:55 AM »
eAtMaxReaders clearly suggests to me, a problem in the API somewhere, and I wouldn't doubt it, given that (in earlier releases at least), a BlockReference had to be transaction-resident to access its dynamic block properties (don't know if that's still the case, but if not and it was 'fixed', then I think this is a side-effect of the 'fix').

The strange thing is that this should never happen if you use a separate transaction for inserting each block reference, and there is no outer Transaction enclosing everything. Any objects that are being opened should be closed when the outermost transaction is committed.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #35 on: March 14, 2013, 06:10:48 AM »
An envelope company?  An envelope company?  :lmao:  Why would you think that I work for an envelope company?

I am using values for height and width that are defined in the list of allowed values.  If you have done any building at all, you would recognize the allowed values as the true size of a 2x4, 2x6, ... 4x4, ...  In other words, the dynamic block is the end view of a piece of wood.

I think that we are all now agreed that, to misquote Shakespeare, the problem is not in our stars but is in the api! 

The only outstanding question is how to work around it.  If you really can only set the value of a dynamic block property 256 times that really limits their usefulness. Can anybody think of a way around this limit.


Stevray

  • Guest
Re: Transaction timing issues...
« Reply #36 on: March 14, 2013, 07:56:32 AM »
docs
Quote
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.

Does not the END USING statement close all  references to the db that were opened withing the scope of the transaction?

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #37 on: March 14, 2013, 09:04:23 AM »
yes, but only the top transaction.  The routine is crashing before we get there.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction timing issues...
« Reply #38 on: March 14, 2013, 09:12:59 AM »
As a workaround... The vertical products I've worked with were wrapping my instructions in a transaction so it would probably need to start by committing and disposing of the top transaction, then break the workload up into smaller pieces each wrapped in their own transaction. Sounds messy... Thanks AutoDesk!

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #39 on: March 14, 2013, 04:32:23 PM »
docs
Quote
If attempting to open AcDb::kForRead and the object is already opened for read the maximum of 256 times, then the open attempt will fail and this function will return Acad::eAtMaxReaders.

Does not the END USING statement close all  references to the db that were opened withing the scope of the transaction?

Yes, for the outer-most transaction.

I can't run your code, but here are a few things I would try:  First, try changing StartTransaction() to StartOpenCloseTransaction(). You can also try changing your CommandMethod to a LispFunction with a name starting with "C:", and adding a ResultBuffer argument (which isn't used).  ObjectARX commands are implicitly undo groups and may also have some affect on how transactions behave. As far as I can tell your code isn't opening any object > 255 times, so it must be an object being opened internally by the API that is breaching the limit.

Stevray

  • Guest
Re: Transaction timing issues...
« Reply #40 on: March 14, 2013, 05:09:53 PM »
yes, but only the top transaction.  The routine is crashing before we get there.

Even if I delete the outer most transaction and wrap each block reference insertion in it's own transaction, it still crashes.    :cry:

Gasty

  • Newt
  • Posts: 90
Re: Transaction timing issues...
« Reply #41 on: March 14, 2013, 06:46:18 PM »
Hi,

This is weird, I'd tried different approaches to this, using just a transaction, 1 top transaction plus a local insert transaction, and in both cases AutoCAD go south after a random number of inserts (over 150), in some cases with "funny" results like a message about a proxy in the block definition, no erasable inserts, etc, obviously because a memory or db corruption . After some test I have a code that at least does not crash AutoCAD (tested up to 1501 inserts), but have a serious performance problem,  that I routed to the change of dynprops part, the time it takes to change the values is consistently increasing  (up to seconds) after each insert, but I don't see a memory issue in process explorer, just a delay changing the dynprops. The code is crude, assuming a lot of things with a minimum error checking :

Code: [Select]
    Public Sub InsertBlockTest3(ByVal blkName As String, ByVal px As Point3d, ByVal Theta As Double, ByVal sx As Double, ByVal sy As Double, ByVal sz As Double, ByVal Height As Double, ByVal Width As Double, ByVal Layer As String)
        Dim Db As Database

        Db = HostApplicationServices.WorkingDatabase()

        Using acTrans As Transaction = Db.TransactionManager.StartTransaction()
            Dim bt As BlockTable = acTrans.GetObject(Db.BlockTableId, OpenMode.ForRead)
            Dim btr As BlockTableRecord
            Dim id As ObjectId

            btr = acTrans.GetObject(bt.Item(BlockTableRecord.ModelSpace), OpenMode.ForWrite)

            If bt.Has(blkName) Then
                Dim btrSrc As BlockTableRecord
                btrSrc = acTrans.GetObject(bt.Item(blkName), OpenMode.ForRead)
                id = btrSrc.Id
            Else
                MsgBox("Block definition not found for Block:" + blkName)

            End If

            Dim blkRef As New BlockReference(px, id)
            Dim sc As New Scale3d(sx, sy, sz)
            blkRef.Layer = Layer
            blkRef.Rotation = Theta
            blkRef.ScaleFactors = sc
            btr.AppendEntity(blkRef)
            acTrans.AddNewlyCreatedDBObject(blkRef, True)

            Dim dPropsCollection As DynamicBlockReferencePropertyCollection

            dPropsCollection = blkRef.DynamicBlockReferencePropertyCollection

            Dim t1 As Date = Now
            For Each dprop As DynamicBlockReferenceProperty In dPropsCollection
                Select Case dprop.PropertyName
                    Case "Heigth"
                        dprop.Value = Height
                    Case "Width"
                        dprop.Value = Width
                End Select
            Next

            Dim t2 As Date = Now
            Debug.Print("Time in props:" + (t2 - t1).TotalMilliseconds.ToString + "[ms]")

            Try
                btr.UpdateAnonymousBlocks()
                blkRef.RecordGraphicsModified(True)
            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                MsgBox("Error Updating RefBTR:blkName :=" + blkName + ": " + ex.Message)
            End Try
            acTrans.Commit()
        End Using
    End Sub

    <CommandMethod("BTest3")> _
    Public Sub btest3()
        Dim blkName As String = "A"
        Dim px As New Point3d(0, 0, 0)
        Dim offset As New Vector3d(4.5, 0, 0)
        Debug.Print(Now.TimeOfDay.ToString)
        For i = 0 To 199
            InsertBlockTest3(blkName, px, 0, 1, 1, 1, 2, 4, "0")
            px += offset
        Next
        Debug.Print(Now.TimeOfDay.ToString)
    End Sub

My concerns are not only with the poor documentation, but with the absence of a clear workflow and design patterns to achieve a very recurrent task as insert blocks. With AutoCAD 2013 we are about to complete 9 version of this API without a decent documentation, clear design patterns and official guidance.

Regards,

Gaston Nunez


Jeff H

  • Needs a day job
  • Posts: 6144
Re: Transaction timing issues...
« Reply #42 on: March 14, 2013, 08:49:13 PM »
Has anyone with problems crashing checked the AnonymousBlockTableRecord & DynamicBlockTableRecord properties before and after changing its properties?

LE3

  • Guest
Re: Transaction timing issues...
« Reply #43 on: March 14, 2013, 09:19:05 PM »
maybe i have not read all, but i can do an update of a an dynamic block property without an error, just tested with a simple stud block (rectangle) copied on the drawing 2500+ times, that in my case has a "Width" property name. See if helps...

Code - C#: [Select]
  1.         [CommandMethod("UPDYNBLKS")]
  2.         public void cmd_updateDynamicBlocks()
  3.         {
  4.             Editor e = AcadApp.DocumentManager.MdiActiveDocument.Editor;
  5.             TypedValue[] tv = new TypedValue[] { new TypedValue((int)DxfCode.Start, "INSERT") };
  6.             SelectionFilter filter = new SelectionFilter(tv);
  7.             PromptSelectionOptions options = new PromptSelectionOptions();
  8.             options.MessageForAdding = "\nAdd blocks to selection";
  9.             options.MessageForRemoval = "\nRemove blocks from selection";
  10.             options.AllowDuplicates = false;
  11.             options.RejectObjectsFromNonCurrentSpace = true;
  12.             PromptSelectionResult psr = e.GetSelection(options, filter);
  13.             if (psr.Status != PromptStatus.OK) return;
  14.             string blockName = "STUD";
  15.             string propertyName = "Width";
  16.             double wValue = 4.0;
  17.             using (var tr = e.Document.Database.TransactionManager.StartTransaction())
  18.             {
  19.                 var blockTable = tr.GetObject(e.Document.Database.BlockTableId, OpenMode.ForRead) as BlockTable;
  20.                 var blockTableRecord = tr.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
  21.                 foreach (var entityId in blockTableRecord)
  22.                 {
  23.                     var dynamicBlock = tr.GetObject(entityId, OpenMode.ForWrite, false) as BlockReference;
  24.                     if (dynamicBlock != null && !dynamicBlock.IsErased && string.Compare(dynamicBlock.Name, blockName, true) == 0 && dynamicBlock.IsDynamicBlock)
  25.                     {
  26.                         dynamicBlock.DynamicBlockReferencePropertyCollection.Cast<DynamicBlockReferenceProperty>().ToList().ForEach(dynamicProperty =>
  27.                         {
  28.                             try
  29.                             {
  30.                                 if (!dynamicProperty.ReadOnly && propertyName == dynamicProperty.PropertyName)
  31.                                 {
  32.                                     if (dynamicProperty.PropertyTypeCode == 1)
  33.                                     {
  34.                                         dynamicProperty.Value = wValue;
  35.                                     }
  36.                                 }
  37.                             }
  38.                             catch (System.Exception ex)
  39.                             {
  40.                                 e.WriteMessage("\nError: {0} \n", ex.Message);
  41.                             }
  42.                         });
  43.                     }
  44.                 }
  45.                 tr.Commit();
  46.             }
  47.         }
  48.  

TheMaster

  • Guest
Re: Transaction timing issues...
« Reply #44 on: March 15, 2013, 01:36:00 AM »
Has anyone with problems crashing checked the AnonymousBlockTableRecord & DynamicBlockTableRecord properties before and after changing its properties?

The performance issues with scripting dynamic blocks and their properties has existed from the beginning, and it has to do with the failure to expose APIs that allow the programmer to inform the Dynamic block API that they are going to be make many changes to many properties, and/or many dynamic block insertions, and the failure of the API to be able to apply those changes without a 'recalcl' (so to speak) happening on each and every one. In other words, the issue doesn't exist insofar as interactive editing of Dynamic blocks, because the user doesn't perform the operation in mass, except perhaps, when they've selected many insertions and use the Properties Palette to change a property common to all selected inserts.

Every time you change a dynamic block property, it requires AutoCAD to first check to see if there are any existing Anonymous blocks whose properties are all the same as the dynamic block reference that you've changed, and if it finds one that matches, it will then set the block reference to that anonymous block. That's if you're lucky. Otherwise, a new anonymous block must be defined and the insertion must be set to reference that. So, there's quite a bit of things happening under the covers when you do something seemingly simple, like change a dynamic block property.

Implementing features that are unquestionably scripting-unfriendly is something we are seeing more and more of from Autodesk (FLATSHOT, Solid 3d primitives, etc).

I think the only way to get around the problem is to insert with default properties, commit, and then return and open each insertion and modify their properties.