• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

[vJASS] What will happen if I destroy "an instance" that has already been destroyed?

Status
Not open for further replies.
Level 11
Joined
Oct 11, 2012
Messages
711
Hi guys, please see the following, its an example in my other posts, but the question is different:
JASS:
struct test
    //Suppose this struct has everything needed
endstruct

function CallBack takes nothing returns nothing
    local timer t = GetExpiredTimer()
    local test a = LoadInteger(hash, GetHandleId(t), 0) 
    //Suppose everything else if fine, can I do the following?
    call a.destroy() //What will happen? I have already deallocated in "function example"
endfunction


function example takes nothing returns nothing
    local test a = test.create()
    local timer t = CreateTimer()
    call SaveInteger(hash, GetHandleId(t), 0, a)
    call TimerStart(t, 1, false, function CallBack)
    call a.destroy()
    //Suppose everything else is fine
endfunction

Thanks.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
2 or more different things may happen.

The default 2 are:

If you have JassHelper in Debug mode, upon calling a.destroy() second time, it will most likely(dont remember atm, but I could try it) show message saying you are trying to double free instance.

If you have JassHelper in Release mode, when you call a.destroy() second time, nothing will happen.

Different results may be seen, in case your struct is declared as extends array, and you implement module that has custom allocate, things may start vary.


This behaviour is because how member function(method) call works. When you try to call method of instance a, it will actually get compiled to single function call with the first argument being the integer a.

The function looks something like this(when automatically generated):

JASS:
private method deallocate takes nothing returns nothing
    local integer i = q[this]
    if (i == 0)
        return //double free
    endif

    //more things, cant remember atm and too lazy to check how it is done
endmethod

method destroy takes nothing returns nothing
    call deallocate() //alternatively, this.dellaocate()
                      //or .deallocate(), all mean the same thing
endmethod
 
Level 11
Joined
Oct 11, 2012
Messages
711
2 or more different things may happen.

The default 2 are:

If you have JassHelper in Debug mode, upon calling a.destroy() second time, it will most likely(dont remember atm, but I could try it) show message saying you are trying to double free instance.

If you have JassHelper in Release mode, when you call a.destroy() second time, nothing will happen.

Different results may be seen, in case your struct is declared as extends array, and you implement module that has custom allocate, things may start vary.


This behaviour is because how member function(method) call works. When you try to call method of instance a, it will actually get compiled to single function call with the first argument being the integer a.

The function looks something like this(when automatically generated):

JASS:
private method deallocate takes nothing returns nothing
    local integer i = q[this]
    if (i == 0)
        return //double free
    endif

    //more things, cant remember atm and too lazy to check how it is done
endmethod

method destroy takes nothing returns nothing
    call deallocate() //alternatively, this.dellaocate()
                      //or .deallocate(), all mean the same thing
endmethod

Thanks for the explanation. +Rep
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,197
Generally bad things will happen if you double free a structure depending on how recycling is managed. For example, it may corrupt the free list with multiple copies of that instance meaning that on allocation of new instances the same index might be handed out twice which will cause all kinds of logical errors.

As such I would treat a double free as a critical error and add logic to avoid it from occurring.

It is not viable to check during freeing if the instance was already freed since that would greatly increase the overhead of allocation/free.
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,197
one if block with return statement is not much of overhead, and every available allocator, even the inbuild one does that.
The problem is you cannot detect a double free efficiently unless you have a member that determines if the instance is allocated or not which would be quite wasteful.

However I guess they merge the free list as a member as opposed to a separate list. They can then use an invalid jump to represent an instance being allocated (0 as an example with index 0 being not used) with any instance having that number being currently allocated.

That said, it is still stupid to double free stuff in the first place. One really should not even need such checks since chances are you are doing logically incorrect stuff if you ever have a double free.
 
Level 23
Joined
Apr 16, 2012
Messages
4,041
you can detect if you are double freeing instance with the same number of variables, because if given instance is allocated, the "pointer" array at that position will have -1 stored in, otherwise it has the next free instance stored in. So you just check if myArray[this] == -1 then

I agree double freeing is stupid, but it will happen from time to time, intentionally or not
 

Dr Super Good

Spell Reviewer
Level 63
Joined
Jan 18, 2005
Messages
27,197
the "pointer" array at that position will have -1 stored in, otherwise it has the next free instance stored in. So you just check if myArray[this] == -1 then
Which I said...

However I guess they merge the free list as a member as opposed to a separate list. They can then use an invalid jump to represent an instance being allocated (0 as an example with index 0 being not used) with any instance having that number being currently allocated.

Other ways of implementing it which are logically easier to understand by some include a stack or a separate list. In order to detect double free you need each instance to also inherit basic link listed properties which are used for the free list.

So you just check if myArray[this] == -1 then
Except that will not pick up an element that never was allocated. Thus why they probably reserve 0 since all indices in the array are initialized to value 0. That way if you free a nonsense member such as 8191 which never was allocated it still will show up as a double free. Otherwise you will have to initialize the full array size which is very inefficient and will add huge load times.

but it will happen from time to time, intentionally or not
It will only happen by mistake. If your script logic is correct it can never reasonably happen. The only time in such a case it will occur is in the case of a very rare hardware error such as bit flips from cosmic radiation which are not recoverable anyway (has a higher chance of causing a BSoD than influencing the trigger, also will cause an immediate OOS error in multiplayer). Computers are deterministic so if software is correct (which is virtually impossible to prove mind you) then it will always be correct except when there are hardware errors.
 
Status
Not open for further replies.
Top