Notifications
Clear all

[Closed] #preSystemShutdown grief

Greetings

I’m having a bit of trouble exiting max cleanly since adding some new features to my script.

Everything works fine till I close max, at which point I get the all too familiar “3ds Max Error report” dialogue.

If I float, and un-register my toolbar (it’s a right click function on one of my toolbar buttons), and close the floater by clicking on the ‘x’, before closing max, I always exits cleanly.

However if I try to run the following from the #preSystemShutdown callback, max crashes on exit. I thought I was exactly recreating the above? Is there something I might be missing?

(
cui.FloatDialogBar myRollout
cui.UnRegisterDialogBar myRollout
destroydialog myRollout
)

Thanks Muchly

Mikie

15 Replies

well… the line cui.FloatDialogBar myRollout is redundant.
the ‘close dialog’ code should be


 try(cui.UnRegisterDialogBar myRollout) catch()
try(destroydialog myRollout) catch()
 
1 Reply
(@mikiek)
Joined: 11 months ago

Posts: 0

That’s similar to what I originally had (and, yes I’m actually using try() catch(), but omitted for simplification here)… But for some reason, it’s not enough to let Max run whatever garbage collection it needs to run…

I’ve also tried
closeRolloutFloater myRollout

one of the members of my tool’s global structure is a dotNet object, which spawns 8 threads.
It seems to be the main culprit, as I didn’t have this problem before this member was in my struct. I’m quite careful about running that object’s .kill() method (which in turn runs .Abort() on each thread) earlier in my #preShutdown callback, as well as in the .close() function of my main rollout.

I should also add if I unregister, and destroy dialog from the listener then shutdown max. it exits cleanly as well. it just doesn’t seem to prevent the crash when I run identical code from the callback.

AHA! (not actually an AHA, as it turns out)

It appears it is not enough to destroy the dialogs, or even set my global structure to undefined. I need to set the instance of my threaded dotnetObject to undefined first!

ie my new #preShutdown callback contains something like this now, and it hasn’t crashed on close in the last dozen attempts…
(
myGlobalStruct.myThreadedDotNetObject = undefined
unregister…
destroyDialog…
)

  • tho I still don’t know why i don’t need to set my objects to undefined when I unregister, and destroy prior to #preShutdown callback…

DAMNIT! 3ds Max application windows close, and no CER window pops up as an error, but 3dsMax.exe process doesn’t shutdown. Still sitting there in the task manager if one happens to look.

 lo1

It’s time to show some code.

I’m trying to think of how to do that effectively, and without giving away anything the company would spank me for… There are a few classes in dotnet in a library .dll related to this(out of many others), and the code which instantiates those objects, can get picked out, but it’s scattered across 50k lines of maxscript, and used in timechange as well as transform handlers…

do you create any max UI elements (rollouts, dotnetcontrols, etc.) using other than main threads?

3 Replies
(@mikiek)
Joined: 11 months ago

Posts: 0

no all UI elements are created in the main thread…

(@denist)
Joined: 11 months ago

Posts: 0

your trick with setting a struct to undefined unlikely can help. i guess that some dotnet objects stay alive and can’t be finalized (destroyed) by the system because were created in other than main thread.

(@mikiek)
Joined: 11 months ago

Posts: 0

I guess the bit I find strange is that the object and it’s threads apparently CAN be finalized, but only if I run my shutdown function from the listener rather than in the #preShutdown callback.

I’ll work out a simple test version over the next few days anyways.

 lo1

Any usage of DialogMonitorOps?

1 Reply
(@mikiek)
Joined: 11 months ago

Posts: 0

Nope. (at least I don’t think so… I need to google what that is)

 lo1

Well, this is turning into a guessing game. Try to recreate the issue in a simple script.

1 Reply
(@mikiek)
Joined: 11 months ago

Posts: 0

Exactly what I’m trying to sort out

Not a simple function tho unfortunately. Multi threaded Collision detection, compatible with an external system. :-\

I learned quite a bit today. It seems our method to kill threads in dotnet was rubbish. To be honest, I read it wasn’t best practice to use thread.Abort() but I also read if you use it when terminating programs it’s OK. Unfortunately it didn’t click to me that in this scenario, it’s not really on termination as the mainUI thread still needs to run.

So my tool was taking a random number of seconds (from 3 to 15) for all my threads to be finalized (depending on what state the threads are in when .Abort() is thrown).

As it turns out, the #preShutdown callback only gives you about 5 seconds to execute code before it forces a transition into shutdown. (I found this out by running a loop with half second sleeps and print statements) So this would leave some threads still hanging sometimes.

In the C# Code, we changed the while loops in the threads from while(true) to while(!kill). Calling myDotNetObject.kill() from 3ds used to call .Abort() on each thread, it now sets a variable called kill to true. bringing everything to a cleaner stop.

 lo1

This is called cooperative cancellation, and is always the preferred method of cancelling threads.

Nevertheless, I find it hard to believe that merely calling Abort() on your threads is what was causing the issue, rather, most likely it was leaving whatever the threads were doing in an unfavorable state.