HomeUser Control Panel (unavailable in archive)ForumsTutorialsArt GalleryResourcesMaps

Handle stack corruption testing

11-04-2011, 10:05 AM#1
Bribe
Destroying a trigger with only conditions does not corrupt the stack, and the user is not able to destroy the function interfaces that you are calling with .execute either, so your own internal function can use conditions, that's not a problem.

Edit by Anitarf: This thread was created by moving some off-topic posts from here.
11-04-2011, 10:37 AM#2
Anitarf
Quote:
Originally Posted by Bribe
Destroying a trigger with only conditions does not corrupt the stack,
It seems you missed my earlier post, or maybe you are simply in denial.
Quote:
Originally Posted by me, three posts earlier
I found an easy-to-reproduce example of handle stack corruption here and when I edited the code to use a trigger condition, the bug still occurred
Here, try it yourself:
Expand JASS:
11-04-2011, 12:11 PM#3
Bribe
I wonder if the bug will still be there if "TriggerClearConditions" will be used before "DestroyTrigger". I will test it tonight.
11-04-2011, 01:32 PM#4
Anitarf
I've been doing some more testing, however I likely won't have any more time today to figure this out to the end, so here are some of my preliminary findings:

I was trying to figure out which handle id is it that gets recycled twice when the bug occurs. After displaying a debug message with the handle id of pretty much every handle I ever created, the buggy handle id still wasn't there. Then I made a periodic trigger create a group every few seconds and display its handle id. When I pressed escape to run my other trigger, there was a gap between the handle id of the last group that was created by the periodic trigger and the first handle that my trigger created and that was the handle id that bugged if I had the bug code enabled.

My conclusion is that whenever a trigger runs, it allocates a handle id. This handle id gets recycled when the trigger finishes running because the next group created by my periodic trigger would get that missing id, which also suggests that the periodic trigger didn't need to allocate this internal handle, else it would have used that id and the group it created would have gotten a new one. So, my conclusion is actually that whenever some trigger events run, they allocate a handle id. Periodic timer events don't appear to do that, also triggers that are executed don't do that. A thing to test would be if modifying the code I posted above to use a periodic timer event instead of the escape key event would fix the bug.

It's difficult to explain and my test code is a mess due to all variations I tried, when I have more time I'll write some cleaner test codes and post example outputs.

Anyway, the bug is that this handle id can get recycled multiple times if a trigger is destroyed while it's waiting. It takes multiple waits to cause the bug and the handle id seems to get recycled for each wait, if you change the code above to have 4 waits, then you could create four groups instead of two and they'd all get the same handle id.
11-04-2011, 10:56 PM#5
Anitarf
Okay, I have some cleaner test code now to demonstrate the handle id that some events allocate when their triggers run:
Collapse JASS:
// This is just a simple trigger that creates a handle every two seconds and displays its handle id.
// It is intended to provide some background noise against which missing handle ids can be identified.
scope HandleGenerator initializer Init

function GenerateHandle takes nothing returns nothing
    call BJDebugMsg("Periodic group handle is "+I2S(GetHandleId(CreateGroup())))
endfunction

//===========================================================================
private function Init takes nothing returns nothing
    local trigger t = CreateTrigger(  )
    call TriggerRegisterTimerEventPeriodic( t, 2 )
    call TriggerAddAction( t, function GenerateHandle )
endfunction

endscope
Collapse JASS:
scope TriggerHandleIdTesting initializer Init

private function onEscape takes nothing returns nothing
  // First, try testing without the DestroyTrigger line:
    //call DestroyTrigger(GetTriggeringTrigger())

    call BJDebugMsg("escape group 1 "+I2S(GetHandleId(CreateGroup())))
    call TriggerSleepAction(1.0)
    call BJDebugMsg("escape group 2 "+I2S(GetHandleId(CreateGroup())))
    call TriggerSleepAction(1.0)
    call BJDebugMsg("escape group 3 "+I2S(GetHandleId(CreateGroup())))
endfunction

private function Init takes nothing returns nothing
    local trigger t = CreateTrigger()
    call BJDebugMsg("TRIGGER "+I2S(GetHandleId(t)))
    call BJDebugMsg("ACTION "+I2S(GetHandleId(TriggerAddAction(t, function onEscape))))

  // Only test with one of the following events at a time.
  // When testing with the escape key event, try pressing the key at the same timing as the other event.
  // This is intended to demonstrate that only some events allocate a handle id when run.
  // It is that handle id that is involved in the bug when you uncomment the DestroyTrigger line above.
    //call BJDebugMsg("EVENT "+I2S(GetHandleId(TriggerRegisterPlayerEvent(t, Player(0), EVENT_PLAYER_END_CINEMATIC))))
    call BJDebugMsg("EVENT "+I2S(GetHandleId(TriggerRegisterTimerEvent( t, 5, true ))))

    call BJDebugMsg("post init group "+I2S(GetHandleId(CreateGroup())))
    set t=null
endfunction

endscope
Just let the thing run for eight or so seconds (while pressing the escape key at the five second mark if needed), then open your log screen to examine the results in peace. The "escape group 1" debug message will have a different handle id depending on which of the two events you use. If you're using the escape key event, there'll be a handle id missing between the "escape group 1" message and the last periodic message. If you uncomment the DestroyTrigger line, that handle id will get assigned to multiple new groups, causing the handle stack corruption bug.

Edit: the number of times the buggy handle id gets reused does not perfectly match the number of waits, I get different results depending on whether I press escape slightly before or slightly after the 5 seocnd mark, which changes the order in which escape groups and periodic groups get created. I'm having difficulties figuring out any kind of consistent rule for how this handle id gets recycled multiple times.
11-17-2011, 11:23 PM#6
cohadar
I never destroy triggers. I win.