[v2] SetTimer does not respect -0 as an argument Topic is solved

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

[v2] SetTimer does not respect -0 as an argument

01 Jun 2022, 20:23

v1 code demonstrating immediate asynchronous execution

Code: Select all

#persistent
DllCall("QueryPerformanceCounter", "int64*", start:=0)
DllCall("QueryPerformanceCounter", "int64*", end:=0)
Msgbox % end - start

DllCall("QueryPerformanceCounter", "int64*", start:=0)
settimer xd, -0
return

xd:
DllCall("QueryPerformanceCounter", "int64*", end:=0)
Msgbox % end - start
return
v2 code where -0 is identical to Off and therefore does nothing, or -1 which suffers from the time granularity limitations as A_TickCount and therefore becomes -10 or -15.6.

Code: Select all

DllCall("QueryPerformanceCounter", "int64*", &start:=0)
DllCall("QueryPerformanceCounter", "int64*", &end:=0)
Msgbox(end - start)

DllCall("QueryPerformanceCounter", "int64*", &start:=0)
settimer () => (DllCall("QueryPerformanceCounter", "int64*", &end:=0), Msgbox(end - start)), -1
lexikos
Posts: 9666
Joined: 30 Sep 2013, 04:07
Contact:

Re: [v2] SetTimer does not respect -0 as an argument  Topic is solved

01 Jun 2022, 22:29

Is there some documented behaviour that is not working?

-0 is not a value, it is an expression which evaluates to 0.

SetTimer does not, and is not intended to, offer immediate execution. All timers are checked at the same interval, not immediately after SetTimer, except by coincidence.
User avatar
TheDewd
Posts: 1513
Joined: 19 Dec 2013, 11:16
Location: USA

Re: [v2] SetTimer does not respect -0 as an argument

02 Jun 2022, 10:56

If I wanted to execute immediately, I've always just called GoSub, LabelName after SetTimer. I was never aware that anyone was trying or suggesting -0 as a value.
iseahound
Posts: 1459
Joined: 13 Aug 2016, 21:04
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

02 Jun 2022, 11:51

GoSub is removed in v2. Goto cannot jump out of functions.
iPhilip
Posts: 827
Joined: 02 Oct 2013, 12:21

Re: [v2] SetTimer does not respect -0 as an argument

02 Jun 2022, 11:58

I saw Helgef use it a few times in the past and found it confusing. See line 30 in this post.
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
lexikos
Posts: 9666
Joined: 30 Sep 2013, 04:07
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

02 Jun 2022, 22:27

SetTimer can't jump anywhere, let alone out of functions, and it can't call a label in v2.

Helgef used SetTimer to call a function at the next timer check after the hook callback returns. The timer cannot execute before then because of Critical. It is not immediate.
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

04 Jun 2022, 03:03

iPhilip wrote:
02 Jun 2022, 11:58
I saw Helgef use it a few times in the past and found it confusing. See line 30 in this post.
I have -0 recollection of this, it could have been the result of an extra-terrestrial event as far as I am concerned. However, looking at the documentation I see no support for it. It could (have) work(ed) because the command parameters takes strings as input, so -0 isn't an expression which evaluates to 0 (as in v2 as lexikos mentioned). However, that doesn't mean that the -0 will not be converted to 0 internally.

The aliens probably wanted to call the user callback as soon as possible after the hook callback returned.
immediate asynchronous execution
If you just want to call a function in a new thread, it is trivial without setttimer,

Code: Select all

minus_0(fn, p*) => dllcall(callbackcreate( ()=> fn(p*) ))
I leave it as an exerice for the interested reader to fetch the return value and any expection resulting from the call to fn.

Cheers :alien:
iseahound
Posts: 1459
Joined: 13 Aug 2016, 21:04
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

04 Jun 2022, 11:00

Is freeing the callback after dllcall safe?

Code: Select all

minus_0(fn, p*) => (dllcall(address := callbackcreate( ()=> fn(p*) )), callbackfree(address))
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

04 Jun 2022, 13:50

that is recommended if a script calls,
CallbackCreate an indefinite/unlimited number of times
Cheers.
Spoiler
iPhilip
Posts: 827
Joined: 02 Oct 2013, 12:21

Re: [v2] SetTimer does not respect -0 as an argument

04 Jun 2022, 13:55

Helgef wrote:
04 Jun 2022, 03:03
I have -0 recollection of this, it could have been the result of an extra-terrestrial event as far as I am concerned. However, looking at the documentation I see no support for it. It could (have) work(ed) because the command parameters takes strings as input, so -0 isn't an expression which evaluates to 0 (as in v2 as lexikos mentioned). However, that doesn't mean that the -0 will not be converted to 0 internally.

The aliens probably wanted to call the user callback as soon as possible after the hook callback returned.
Cheers :alien:
Hahaha! :lol:
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
iseahound
Posts: 1459
Joined: 13 Aug 2016, 21:04
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

05 Jun 2022, 09:48

@Helgef

No, I can't get your minus_0 function to replicate the asyncronous execution of SetTimer. At first, I thought it was the fat arrow syntax holding an implicit return, but even when I converted the function, I realized that DllCall waits for execution to finish. Here's v1 and v2 scripts side by side:
Out of order (correct, v1 code):
2 ; Immediate
4 ; Immediate
1 ; async
3 ; async

Happens in order (incorrect, v2):
1 ; callbackcreate
2
3 ; callbackcreate
4


v1 code

Code: Select all

#persistent
Set_Timer:
async(1)
log(2)
asyncwait()
async(3)
log(4)
asyncwait()


async(msg) {
   st(Func("log"), msg)
}

asyncwait(ms := 250) {
   st(Func("wait"), ms)
}

st(fn, p*) {
   obm := fn.Bind(p*)
   SetTimer % obm, -0
}

log(msg) {
   FileAppend % msg . "`n", log.txt
}

wait(ms := 250) {
   sleep % ms
}
v2 code

Code: Select all

persistent(true)
Callback_Create:
async(1)
log(2)
asyncwait()
async(3)
log(4)
asyncwait()


async(msg) {
   minus_0(log, msg)
}

asyncwait(ms := 250) {
   minus_0(sleep, ms)
}

st(fn, p*) {
   SetTimer fn.bind(p*), -1
}

minus_0(fn, p*) {
   address := callbackcreate( ()=> fn(p*) )
   dllcall(address)
   callbackfree(address)
}

log(msg) {
   FileAppend msg . "`n", 'log.txt'
} 
However, the ability to execute in another thread without CreateThread is very interesting, and I will remember that for future use
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

05 Jun 2022, 12:08

@iseahound, I only talk about :arrow: Threads here, these threads are the only relevant threads in this topic, both in v1 and v2 and regardless of which period time you pass to settimer.
Spoiler
Cheers
Last edited by Helgef on 19 Jun 2022, 12:15, edited 1 time in total.
iseahound
Posts: 1459
Joined: 13 Aug 2016, 21:04
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

05 Jun 2022, 15:40

👍 Yeah this is all very interesting stuff. The main "side benefit" is to avoid a return statement. AutoHotkey can call about 500-1000 functions before it runs out of space¹, so to bypass that limit some sort of asynchronous execution that avoids return is ideal. I think I am most curious why -0 even worked in the first place - if all timers are checked at the same interval, how did -0 end up queuing² the next routine in v1?

¹ The call stack
² Or perhaps jumping the queue
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

06 Jun 2022, 02:53

The main "side benefit" is to avoid a return statement
I do not follow.
I think I am most curious why -0 even worked in the first place - if all timers are checked at the same interval, how did -0 end up queuing² the next routine in v1?
As mentioned, it is not supported (in either v1 or v2). In , v1, -0 might cause the timer to be more likely to have timed out when the list of timers are checked, if the program interpret it as run once with period 0.

Cheers.

Edit,

Code: Select all

__int64 period = ATOI64(aPeriod);
if (*aPeriod == '-')
Indeed, in v1, -0 currently is interpreted as run once with period 0. Checking the string after it has been converted to a pure number, is weird enough that it is probably intended to be a feature, so perhaps it should be documented.
lexikos
Posts: 9666
Joined: 30 Sep 2013, 04:07
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

06 Jun 2022, 05:59

v1 wrote:If Period is negative,
v2 wrote:If Period is less than 0,

iseahound wrote:how did -0 end up queuing² the next routine in v1?
The premise of your question is mistaken. As I said at the beginning, timer subroutines do not execute immediately after SetTimer, except by coincidence.

Code: Select all

#requires AutoHotkey v1
SetTimer tick, -0
Loop
    x := A_Index
tick:
MsgBox % x
ExitApp
For me, there are anywhere between 20,000 and 150,000 iterations before the timer executes.
² Or perhaps jumping the queue
They do not. If there are two timers, to be fired at n+0 and n+15, and the timers are checked at tick = n, obviously only the n+0 timer will fire. If the timers are checked at tick = n+16, the timers will fire in the order they were added to the list. There is no queue.

All timers are checked and executed by CheckScriptTimers(), which is called upon entry to MsgSleep() or when a WM_TIMER message with a null lParam is received by the main window. Unless you call the Win32 SetTimer(), this only occurs for the timer set by SET_MAIN_TIMER, which sets an interval of 10. The resolution of these timers is probably more like 15.6 milliseconds, as mentioned in the documentation. Even if the timers are checked due to MsgSleep() calls more often than the resolution of Win32 SetTimer(), GetTickCount() is used to determine when the script timers should execute. GetTickCount() can be observed to have an average resolution of 15.6 milliseconds under normal conditions.

A timer period of 0 would allow the timer to execute within the same tick as the call to SetTimer, whereas a non-zero period would require the tick count to change. Even under ideal conditions, the tick count could change anywhere between 0 and 16 milliseconds after SetTimer is called.
iseahound
Posts: 1459
Joined: 13 Aug 2016, 21:04
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

06 Jun 2022, 17:56

Helgef wrote:
06 Jun 2022, 02:53
The main "side benefit" is to avoid a return statement
Here's an example using callbacks where having a "return" statement allocates 500+ functions on the call stack. It's a recursive function.

Code: Select all

#Requires AutoHotkey v2.0-beta

fib(n, continuation) {
   (n <= 1)
   ? continuation(n)              ; Return the c
   : (fib(n - 1, (x) => (         ; Assign x as n-1
      fib(n - 2, (y) => (         ; Assign y as n-2
         continuation(x + y)))))) ; Fib(n) = Fib(n-1) + Fib(n-2)
}

; Fails when n = 13.
n := 12
showAnswer( fib.bind(n) )

; Pass a Message Box function at the end.
showAnswer(f) {
   ; The final callback is: (x) => MsgBox(x)
   ; where (x) contains the answer.
   f( (x) => MsgBox(x) )
}
To bypass that function recursion limit and go further beyond: Use SetTimer, so this would work for any value greater than 12 (although it's very slow)

Code: Select all

#Requires AutoHotkey v2.0-beta

fib(n, continuation) {
   (n <= 1)
   ? async(continuation.bind(n))  ; Return the c
   : (fib(n - 1, (x) => (         ; Assign x as n-1
      fib(n - 2, (y) => (         ; Assign y as n-2
         async(continuation.bind(x + y))))))) ; Fib(n) = Fib(n-1) + Fib(n-2)
}

; Works when n = 13.
n := 13
showAnswer( fib.bind(n) )

; Pass a Message Box function at the end.
showAnswer(f) {
   ; The final callback is: (x) => MsgBox(x)
   ; where (x) contains the answer.
   f( (x) => MsgBox(x) )
}

; Does not return any values!
async(fn) => SetTimer(fn, -1)
Simple video on how this fib function works (just skip to the end) https://www.youtube.com/watch?v=zg-ddPbzcKM

Keep staring at this until you believe this is assignment without :=

Code: Select all

      fib(n - 2, (y) => (         ; Assign y as n-2
iPhilip
Posts: 827
Joined: 02 Oct 2013, 12:21

Re: [v2] SetTimer does not respect -0 as an argument

06 Jun 2022, 19:12

iseahound wrote:
06 Jun 2022, 17:56
To bypass that function recursion limit and go further beyond: Use SetTimer, so this would work for any value greater than 12 (although it's very slow)
It seems there is an easier way to define a function that, though slow, doesn't have the recursion limits you describe:

Code: Select all

fibonacci(n) {  ; n is the term number: 0,1,2
   return n < 2 ? n : fibonacci(n-1) + fibonacci(n-2)
}
Using Loop is much faster:

Code: Select all

fibonacci(n) {  ; n is the term number: 0,1,2
	seq := [0,1], fib := n <= 1 ? seq[n+1] : 1
	Loop n-1
		fib := seq[1] + seq[2], seq[1] := seq[2], seq[2] := fib
	return fib
}
Windows 10 Pro (64 bit) - AutoHotkey v2.0+ (Unicode 64-bit)
Helgef
Posts: 4709
Joined: 17 Jul 2016, 01:02
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

07 Jun 2022, 01:52

Here's an example using callbacks where having a "return" statement allocates 500+ functions on the call stack. It's a recursive function.
I never heard anyone referring to the issues associated with recursive functions, as being an issue of using return statements, probably because the issue is the opposite, i.e., not returning from functions, hence building up the stack.
ntepa
Posts: 436
Joined: 19 Oct 2022, 20:52

Re: [v2] SetTimer does not respect -0 as an argument

10 Mar 2023, 00:56

I was reading the v1.1 changelog and found this:
https://www.autohotkey.com/docs/v1/AHKL_ChangeLog.htm#v1.1.00.00
Fixed SetTimer Label, -0 to be treated as "run-once, very soon".
User avatar
RaptorX
Posts: 390
Joined: 06 Dec 2014, 14:27
Contact:

Re: [v2] SetTimer does not respect -0 as an argument

11 Mar 2023, 09:12

iPhilip wrote:
02 Jun 2022, 11:58
I saw Helgef use it a few times in the past and found it confusing. See line 30 in this post.
I think that is v1 code though
Projects:
AHK-ToolKit

Return to “Bug Reports”

Who is online

Users browsing this forum: No registered users and 4 guests