Critical Error when using SetTimer to hold lifespan of object Topic is solved

Report problems with documented functionality
iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Critical Error when using SetTimer to hold lifespan of object

Post by iseahound » 28 Jan 2022, 15:59

When using a SetTimer with ObjBindMethod, the idea is to hold a reference to the object asynchronously for a specified amount of time. Therefore (1) When the SetTimer expires, it calls the bound object. The bound object has a reference to this, which should be available for the entire run of the bound function object. (2) __Delete is called immediately after the boundfunc runs.

This error only appears for small timer values. Testing has shown that if the creation and destruction of objects are batched, that is, for large timer values the creation happens all at once and destruction happens all at once, the bug does not appear. When creation and destruction overlap, that is when the critical error Invalid memory read/write appears.
---------------------------
test9.ahk
---------------------------
Critical Error: Invalid memory read/write.

Line#
015: Return,this
016: }
018: {
019: FileAppend,"
" &this " 2. __Delete",log.txt
020: }
022: {
023: FileAppend,"
" &this " 1. Blank",log.txt
---> 024: Return
025: }
027: Exit
028: Exit
028: Exit

The program is now unstable and will exit.
---------------------------
OK
---------------------------

Test code: This is as small as I am able to reduce the code.

Code: Select all

SetBatchLines -1
FileDelete log.txt
Loop 10000 {
   ; Creates an object and immediately discards it.
   new TestObj(100) ; If 10000 the error does not appear.
}
MsgBox done

class TestObj {

   __New(time) {
      ; The reference to the object is held by SetTimer, and its lifespan is time. 
      if (time > 0) {
         blank := ObjBindMethod(this, "blank") ; Calls Blank()
         SetTimer % blank, % -time ; Calls __Delete.
      }
      return this
   }

   __Delete() {
      FileAppend , % "`n" &this " 2. __Delete", log.txt
   }

   Blank() {
      FileAppend , % "`n" &this " 1. Blank", log.txt
      return
   }
}

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Critical Error when using SetTimer to hold lifespan of object

Post by iseahound » 28 Jan 2022, 16:16

Doesn't happen on v2

Code: Select all

FileDelete "log.txt"
Loop 10000 {
   ; Creates an object and immediately discards it.
   TestObj(10) ; If 10000 the error does not appear.
}
MsgBox "done"

class TestObj {

   __New(time) {
      ; The reference to the object is held by SetTimer, and its lifespan is time. 
      if (time > 0) {
         SetTimer () => this.Blank(), -time ; Calls __Delete.
      }
      return this
   }

   __Delete() {
      FileAppend "`n" ObjPtr(this) " 2. __Delete", "log.txt"
   }

   Blank() {
      FileAppend "`n" ObjPtr(this) " 1. Blank", "log.txt"
      return
   }
}

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Critical Error when using SetTimer to hold lifespan of object

Post by Helgef » 04 Apr 2022, 12:17

The issue arise when a timer being (automatically) deleted cause a call to __delete, and this call is interrupted by another timer which is also deleted. In addition this second timer have also been queued to be considered as the next timer after the first timer, the program will then invoke a deleted object, which is undefined behaviour.

You can improve improve your chances by using critical in __delete and avoid manually delete timers via settimer in __delete.

This issue applies to v2 too, why I have submitted a proposal to fix this (for v2).

Cheers.

iseahound
Posts: 1444
Joined: 13 Aug 2016, 21:04
Contact:

Re: Critical Error when using SetTimer to hold lifespan of object

Post by iseahound » 05 Apr 2022, 18:34

Thanks a lot Helgef! This must have taken a lot of time to understand, it's such a strange error.

Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Critical Error when using SetTimer to hold lifespan of object

Post by Helgef » 06 Apr 2022, 03:34

No it was very easy to realise, ofc, not from simply running your script but when debugging this in vs, looking at the source code.

Cheers.


Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: Critical Error when using SetTimer to hold lifespan of object

Post by Helgef » 21 Apr 2022, 03:35

    @lexikos, I suppose you didn't get notified on the :arrow: comment I made on github. It seems this issue applies to the corresponding fix for v1. Example,

    Code: Select all

    #noenv
    SetBatchLines -1
    global s := ""
    loop 6 {
    	x := func("f").bind(a_index)
    	settimer % x, -1
    }
    sleep 300
    msgbox % A_AHKVersion  "`n`n" s
    f(a){
    	s .= a "`t" A_TickCount "`n"  
    }
    
    Compare 1.1.33.10 vs. 1.1.33.11,

    Code: Select all

    1.1.33.10
    
    1	1032130718
    2	1032130718
    3	1032130718
    4	1032130718
    5	1032130718
    6	1032130718
    
    ---------------------------
    OK   
    ---------------------------
    
    1.1.33.11
    
    1	1032412609
    3	1032412609
    5	1032412609
    2	1032412625
    6	1032412625
    4	1032412640
    
    ---------------------------
    OK   
    ---------------------------
    

    lexikos
    Posts: 9583
    Joined: 30 Sep 2013, 04:07
    Contact:

    Re: Critical Error when using SetTimer to hold lifespan of object

    Post by lexikos » 21 Apr 2022, 17:22

    Commit 66b7f07 fixes that.

    I prefer not to let the flow of emails dictate how I spend my time. I only see GitHub notifications if I go looking for them.


    Post Reply

    Return to “Bug Reports”