[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
well… the line cui.FloatDialogBar myRollout is redundant.
the ‘close dialog’ code should be
try(cui.UnRegisterDialogBar myRollout) catch()
try(destroydialog myRollout) catch()
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.
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?
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.
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.
Well, this is turning into a guessing game. Try to recreate the issue in a simple script.
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.
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.