Loops get stuck ~5% of the time?

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
Arsonistic
Posts: 20
Joined: 10 Oct 2019, 13:51

Loops get stuck ~5% of the time?

Post by Arsonistic » 04 Mar 2021, 13:06

I'm having problems with loops(both Timer "loops" and actual loops) sometimes getting stuck; About 5% of the time the looping continues after releasing the button(despite being supposed to stop) and I have to press the hotkey again to make it stop.
Doesn't matter which key triggers the hotkey, nor what key is sent.

Example code (Key Up + KeyWait):

Code: Select all

~*LButton::
	SetTimer, LeftMouse, 50
	KeyWait, LButton
return
~*LButton Up::
	SetTimer, LeftMouse, Off
return
LeftMouse(){
	Send, {LButton}
}
I've tried:
GetKeyState + KeyWait
Key Up + KeyWait
Key Up + Bool checking

None of them worked.
Is there a way to avoid this problem?

User avatar
mikeyww
Posts: 26602
Joined: 09 Sep 2014, 18:38

Re: Loops get stuck ~5% of the time?

Post by mikeyww » 04 Mar 2021, 15:27

What if you remove KeyWait and tilde, and add Sleep, 1 before you disable the timer? Just an idea-- I did not try it. Or: keep the KeyWait but remove the Up hotkey.

I had no trouble with the following.

Code: Select all

*LButton::SetTimer, LeftMouse, 100
LButton Up::SetTimer, LeftMouse, Off
LeftMouse:
Click
Return

WatsonEnterprises
Posts: 19
Joined: 25 May 2020, 23:04

Re: Loops get stuck ~5% of the time?

Post by WatsonEnterprises » 04 Mar 2021, 17:46

Does this work?

Code: Select all

~*LButton::
*x::
F1::
	LeftMouse()
return

LeftMouse(){
	keyThatWasPressed := RegexReplace(A_ThisHotkey, "\*|~|\!|\^|\+|\$")
	
	while (GetKeystate(keyThatWasPressed, "P")){
		Click
		sleep 50
	}
}

vmech
Posts: 353
Joined: 25 Aug 2019, 13:03

Re: Loops get stuck ~5% of the time?

Post by vmech » 05 Mar 2021, 09:03

Arsonistic wrote:
04 Mar 2021, 13:06
Your code is overloaded, simplify it.

Code: Select all

*LButton::
	SetTimer, LeftMouse, 50
	KeyWait, LButton
	SetTimer, LeftMouse, Off
Return

LeftMouse(){
	Send, {LButton}
}
And it is not entirely clear what effect you want to achieve:
You need to send autorepeatings of pressing a physically held key, or you want to send hold down a key while hold a physical key ?

Code: Select all

*LButton::
	Send, {LButton down}
	KeyWait, LButton
	Send, {LButton up}
Return
Also Docs recommend sending Click instead of LButton/RButton.
Please post your script code inside [code] ... [/code] block. Thank you.

Arsonistic
Posts: 20
Joined: 10 Oct 2019, 13:51

Re: Loops get stuck ~5% of the time?

Post by Arsonistic » 05 Mar 2021, 17:20

mikeyww wrote:
04 Mar 2021, 15:27
What if you remove KeyWait and tilde, and add Sleep, 1 before you disable the timer? Just an idea-- I did not try it. Or: keep the KeyWait but remove the Up hotkey.
Tilde doesn't matter. KeyWait is required to avoid the native auto-repeat (don't think it matters either tho since using Bool-checking instead of KeyWait has the same issue).
I had no trouble with the following.

Code: Select all

*LButton::SetTimer, LeftMouse, 100
LButton Up::SetTimer, LeftMouse, Off
LeftMouse:
Click
Return
Ah, sorry, I should have specified that I'm using SendMode Input. That exact code with SendMode Input gives me the same issue with loops getting stuck ~5% of the time. I think the issue doesn't occur with SendEvent (the default SendMode) because it has built-in delays and buffering. I don't want that; SendInput is far more consistent in timing behavior than SendEvent.

Arsonistic
Posts: 20
Joined: 10 Oct 2019, 13:51

Re: Loops get stuck ~5% of the time?

Post by Arsonistic » 05 Mar 2021, 18:12

vmech wrote:
05 Mar 2021, 09:03
Your code is overloaded, simplify it.
Hey now, let's not exaggerate :P
It's literally 2 lines of code.
But hey, I'm all for reducing code size, and it could theoretically improve reliability, so I'll gladly try your suggestion out :)
it is not entirely clear what effect you want to achieve
I want to send button presses repeatedly while holding down a key.
Also Docs recommend sending Click instead of LButton/RButton.
I did write this: "Doesn't matter which key triggers the hotkey, nor what key is sent."
The code was just an example, I've had this issue with other buttons as well.

User avatar
mikeyww
Posts: 26602
Joined: 09 Sep 2014, 18:38

Re: Loops get stuck ~5% of the time?

Post by mikeyww » 05 Mar 2021, 18:22

OK, so you have a preference for avoiding the mode that works..... :crazy:

Arsonistic
Posts: 20
Joined: 10 Oct 2019, 13:51

Re: Loops get stuck ~5% of the time?

Post by Arsonistic » 05 Mar 2021, 18:30

mikeyww wrote:
05 Mar 2021, 18:22
OK, so you have a preference for avoiding the mode that works..... :crazy:
Heeeey, I gave a good reason for why I can't use that mode. Not working 5% of the time is worse than working poorly 100% of the time in this instance :)

User avatar
mikeyww
Posts: 26602
Joined: 09 Sep 2014, 18:38

Re: Loops get stuck ~5% of the time?

Post by mikeyww » 05 Mar 2021, 20:00

Strange. The following worked for me.

Code: Select all

SendMode Input
*LButton::SetTimer, LeftMouse, 100
LButton Up::SetTimer, LeftMouse, Off
LeftMouse:
Click
Return

vmech
Posts: 353
Joined: 25 Aug 2019, 13:03

Re: Loops get stuck ~5% of the time?

Post by vmech » 05 Mar 2021, 20:16

Arsonistic wrote:
05 Mar 2021, 18:12
vmech wrote:
05 Mar 2021, 09:03
Your code is overloaded, simplify it.
Hey now, let's not exaggerate :P
It's literally 2 lines of code.
But hey, I'm all for reducing code size, and it could theoretically improve reliability, so I'll gladly try your suggestion out :)
it is not entirely clear what effect you want to achieve
I want to send button presses repeatedly while holding down a key.
Also Docs recommend sending Click instead of LButton/RButton.
I did write this: "Doesn't matter which key triggers the hotkey, nor what key is sent."
The code was just an example, I've had this issue with other buttons as well.
I just meant that LButton Up procedure is superfluous. Its function is perfectly performed by KeyWait in LButton procedure.
Also, there is no need to use SetTimer - a regular loop with Sleep works at least as well.

Code: Select all

*LButton:
	Loop
	{
		Send, {Click left down}
		Sleep, 50
		If !(GetKeyState(A_ThisHotkey, "P"))
			Break
	}
	Send, {Click left up}
Return
Or a slightly more flexible variant:

Code: Select all

Hotkey, LButton, MyButton
...
MyButton() {
	Loop
	{
		Send, {Blind}{%A_ThisHotkey% down}
		Sleep, 50
		If !(GetKeyState(A_ThisHotkey, "P"))
			Break
	}
	Send, % "{Blind}{" A_ThisHotkey " up}"
}
Please post your script code inside [code] ... [/code] block. Thank you.

Arsonistic
Posts: 20
Joined: 10 Oct 2019, 13:51

Re: Loops get stuck ~5% of the time?

Post by Arsonistic » 06 Mar 2021, 08:24

mikeyww wrote:
05 Mar 2021, 20:00
Strange. The following worked for me.

Code: Select all

SendMode Input
*LButton::SetTimer, LeftMouse, 100
LButton Up::SetTimer, LeftMouse, Off
LeftMouse:
Click
Return
It can be a bit of a pain to test for an issue that only occurs ~5% of the time; you'd need upwards of a 100 tests to get reasonable certainty. To speed up the testing process I reduced the delay to 20 after testing my first few times.

Arsonistic
Posts: 20
Joined: 10 Oct 2019, 13:51

Re: Loops get stuck ~5% of the time?

Post by Arsonistic » 06 Mar 2021, 09:33

vmech wrote:
05 Mar 2021, 20:16
there is no need to use SetTimer - a regular loop with Sleep works at least as well.
There is a need for Timers, because Loops cannot handle multithreading. I'm using similar code in scripts with a bunch of looping behaviors that can execute simultaneously, hence requiring Timers.
I just meant that LButton Up procedure is superfluous. Its function is perfectly performed by KeyWait in LButton procedure.
I have now spent some time testing the KeyWait->Send, {Key Up} method you suggested and can confirm that it's not usable for my use-case. It doesn't solve the original issue; loops still get stuck. But most importantly: it changes threading behavior, creating new bugs when combined with other hotkeys of the same kind. I use a variable to toggle the looping on/off, and when off the KeyWait->Send, {Key Up} method keeps creating new threads while holding down multiple keys(~50 threads per sec with 2 keys), which is bad in a number of ways.
While testing I also found a minor bug in how KeyWait multithreads, so I might rewrite my code with Bool-checking instead of KeyWait to fix that, despite it making the code longer. That still won't fix the issue with loops getting stuck tho :(
Or a slightly more flexible variant:

Code: Select all

Hotkey, LButton, MyButton
MyButton() {
	Loop
	{
		Send, {Blind}{%A_ThisHotkey% down}
		Sleep, 50
		If !(GetKeyState(A_ThisHotkey, "P"))
			Break
	}
	Send, % "{Blind}{" A_ThisHotkey " up}"
}
M8, while loops exist :P
Also, I'm already way ahead of you on flexibility:

Code: Select all

global spam := false
spamHotkeys := ["3", "f", "LButton"] ; Hold one of these to spam that key. Just add a key to the array to automatically make it a new spam hotkey

global BoundFuncCache := {} ; A collection of bound functions for use in Timer stopping. Func(f).Bind(k) seems to create an object and return a reference to it, without caching the result, so manual caching is required to reference the same object
for i, key in spamHotkeys { ; Creates hotkeys for each key in the array above
	spamBF := Func("Spam").Bind(key) ; Bind(BoundFunc) the Key to the Spam function to use it as input for the Hotkey Command
	Hotkey, % "$*" . key, % spamBF ; $ to ensure Hotkeys can't trigger themselves
	stopSpamBF := Func("StopSpam").Bind(key)
	Hotkey, % "~*" . key . " Up", % stopSpamBF
	BoundFuncCache[key] := Func("SendBlind").Bind(key)
}

Spam(key){
	Send, % "{Blind}{" . key . " Down}" ; Required because ~ can't be used with KeyWait for blocking Auto-Repeat
	if (spam){
		tmp := BoundFuncCache[key] ; SetTimer doesn't support function references in expression mode, requiring a temporary variable and regular variable dereferencing
		SetTimer, %tmp%, 50 ; Delay between activations in ms. 50ms = 20 times per second. Should be good for most use-cases
		KeyWait, % key
	}
}
StopSpam(key){
	tmp := BoundFuncCache[key]
	SetTimer, %tmp%, Off
}
SendBlind(key){ ; Function-wrapper for the Send Command
	Send, % "{Blind}{" . key . "}"
}

^L:: ; Ctrl+L toggles Spam On/Off
	spam := !spam
return

whereyomomsat_
Posts: 4
Joined: 02 Dec 2022, 03:11

Re: Loops get stuck ~5% of the time?

Post by whereyomomsat_ » 02 Dec 2022, 16:40

Having a similar issue. Wondering if a fix has been found? Thanks!

Post Reply

Return to “Ask for Help (v1)”